From ca32c57c52b83aa4e20fd3c17a0d8d0c06c9df27 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sat, 24 Mar 2018 07:32:04 +0100 Subject: [PATCH 001/420] Progress on the new cargo group movements --- Moose Development/Moose/Core/Cargo.lua | 57 ++++++++++++++++------- Moose Development/Moose/Wrapper/Group.lua | 2 +- Moose Development/Moose/Wrapper/Unit.lua | 6 ++- 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/Moose Development/Moose/Core/Cargo.lua b/Moose Development/Moose/Core/Cargo.lua index 991669931..495938e77 100644 --- a/Moose Development/Moose/Core/Cargo.lua +++ b/Moose Development/Moose/Core/Cargo.lua @@ -1,5 +1,7 @@ --- **Core** -- Management of CARGO logistics, that can be transported from and to transportation carriers. -- +-- === +-- -- ![Banner Image](..\Presentations\CARGO\Dia1.JPG) -- -- === @@ -602,23 +604,19 @@ end -- CARGO_REPRESENTABLE --- CARGO_REPORTABLE Constructor. -- @param #CARGO_REPORTABLE self - -- @param Wrapper.Controllable#Controllable CargoObject -- @param #string Type -- @param #string Name -- @param #number Weight -- @param #number ReportRadius (optional) -- @param #number NearRadius (optional) -- @return #CARGO_REPORTABLE - function CARGO_REPORTABLE:New( CargoObject, Type, Name, Weight, ReportRadius ) + function CARGO_REPORTABLE:New( Type, Name, Weight, ReportRadius ) local self = BASE:Inherit( self, CARGO:New( Type, Name, Weight ) ) -- #CARGO_REPORTABLE self:F( { Type, Name, Weight, ReportRadius } ) self.CargoSet = SET_CARGO:New() -- Core.Set#SET_CARGO self.ReportRadius = ReportRadius or 1000 - self.CargoObject = CargoObject - - return self end @@ -776,8 +774,9 @@ do -- CARGO_UNIT -- Respawn the group... if self.CargoObject then + self:F( { CargoObject = self.CargoObject } ) self.CargoObject:ReSpawn( CargoDeployPointVec2:GetVec3(), CargoDeployHeading ) - self:F( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } ) + --self:F( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } ) self.CargoCarrier = nil local Points = {} @@ -1150,6 +1149,8 @@ do -- CARGO_GROUP } --- CARGO_GROUP constructor. +-- This make a new CARGO_GROUP from a @{Group} object. +-- It will "ungroup" the group object within the sim, and will create a @{Set} of individual Unit objects. -- @param #CARGO_GROUP self -- @param Wrapper.Group#GROUP CargoGroup -- @param #string Type @@ -1158,23 +1159,47 @@ do -- CARGO_GROUP -- @param #number NearRadius (optional) -- @return #CARGO_GROUP function CARGO_GROUP:New( CargoGroup, Type, Name, ReportRadius ) - local self = BASE:Inherit( self, CARGO_REPORTABLE:New( CargoGroup, Type, Name, 0, ReportRadius ) ) -- #CARGO_GROUP + local self = BASE:Inherit( self, CARGO_REPORTABLE:New( Type, Name, 0, ReportRadius ) ) -- #CARGO_GROUP self:F( { Type, Name, ReportRadius } ) - self.CargoObject = CargoGroup self:SetDeployed( false ) - self.CargoGroup = CargoGroup local WeightGroup = 0 + local GroupName = CargoGroup:GetName() - for UnitID, UnitData in pairs( CargoGroup:GetUnits() ) do - local Unit = UnitData -- Wrapper.Unit#UNIT - local WeightUnit = Unit:GetDesc().massEmpty - WeightGroup = WeightGroup + WeightUnit - local CargoUnit = CARGO_UNIT:New( Unit, Type, Unit:GetName(), WeightUnit ) - self.CargoSet:Add( CargoUnit:GetName(), CargoUnit ) + CargoGroup:Destroy() + + -- We iterate through the group template and for each unit in the template, we create a new group with one unit. + for UnitID, UnitTemplate in pairs( _DATABASE:GetGroupTemplate(GroupName).units ) do + + local GroupTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplate(GroupName) ) + local GroupName = env.getValueDictByKey( GroupTemplate.name ) + self:E( GroupName ) + + -- We create a new group object with one unit... + -- First we prepare the template... + GroupTemplate.name = GroupName .. "#CARGO#" .. UnitID + GroupTemplate.units = {} + GroupTemplate.units[1] = UnitTemplate + local UnitName = UnitTemplate.name .. "#CARGO" + GroupTemplate.units[1].name = UnitTemplate.name .. "#CARGO" + + + -- Then we register the new group in the database + local CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID) + + -- Now we spawn the new group based on the template created. + _DATABASE:Spawn( GroupTemplate ) + + -- And we register the spawned unit as part of the CargoSet. + local Unit = UNIT:FindByName( UnitName ) + --local WeightUnit = Unit:GetDesc().massEmpty + --WeightGroup = WeightGroup + WeightUnit + local CargoUnit = CARGO_UNIT:New( Unit, Type, UnitName, 10 ) + self.CargoSet:Add( UnitName, CargoUnit ) end + self:SetWeight( WeightGroup ) self:T( { "Weight Cargo", WeightGroup } ) @@ -1355,7 +1380,7 @@ function CARGO_GROUP:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius function( Cargo, NearRadius ) Cargo:__UnBoard( Timer, ToPointVec2, NearRadius ) - Timer = Timer + 10 + Timer = Timer + 1 end, { NearRadius } ) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index f46c2701b..0606b14a0 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -336,7 +336,7 @@ end -- @param #number UnitNumber The number of the UNIT wrapper class to be returned. -- @return Wrapper.Unit#UNIT The UNIT wrapper class. function GROUP:GetUnit( UnitNumber ) - self:F2( { self.GroupName, UnitNumber } ) + self:E( { self.GroupName, UnitNumber } ) local DCSGroup = self:GetDCSObject() diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 26d30ed91..695774052 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -193,10 +193,12 @@ end -- @param #number Heading The heading of the unit respawn. function UNIT:ReSpawn( SpawnVec3, Heading ) + self:T( self:Name() ) local SpawnGroupTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplateFromUnitName( self:Name() ) ) self:T( SpawnGroupTemplate ) local SpawnGroup = self:GetGroup() + self:T( { SpawnGroup = SpawnGroup } ) if SpawnGroup then @@ -221,7 +223,7 @@ function UNIT:ReSpawn( SpawnVec3, Heading ) end for UnitTemplateID, UnitTemplateData in pairs( SpawnGroupTemplate.units ) do - self:T( UnitTemplateData.name ) + self:T( { UnitTemplateData.name, self:Name() } ) if UnitTemplateData.name == self:Name() then self:T("Adjusting") SpawnGroupTemplate.units[UnitTemplateID].alt = SpawnVec3.y @@ -261,6 +263,8 @@ function UNIT:ReSpawn( SpawnVec3, Heading ) i = i + 1 end end + + self:T( SpawnGroupTemplate ) _DATABASE:Spawn( SpawnGroupTemplate ) end From 7963f04bdc41576227ea29c6e2cc0bba7ca665a8 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sat, 24 Mar 2018 08:01:49 +0100 Subject: [PATCH 002/420] progress --- Moose Development/Moose/Core/Cargo.lua | 11 +++++++---- Moose Development/Moose/Core/Point.lua | 12 ++++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Core/Cargo.lua b/Moose Development/Moose/Core/Cargo.lua index 495938e77..7a1949fbc 100644 --- a/Moose Development/Moose/Core/Cargo.lua +++ b/Moose Development/Moose/Core/Cargo.lua @@ -261,7 +261,7 @@ function CARGO:New( Type, Name, Weight ) --R2.1 self.Name = Name self.Weight = Weight self.CargoObject = nil - self.CargoCarrier = nil + self.CargoCarrier = nil -- Wrapper.Client#CLIENT self.Representable = false self.Slingloadable = false self.Moveable = false @@ -753,7 +753,7 @@ do -- CARGO_UNIT if From == "Loaded" then - local CargoCarrier = self.CargoCarrier -- Wrapper.Controllable#CONTROLLABLE + local CargoCarrier = self.CargoCarrier -- Wrapper.Client#CLIENT local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. @@ -764,11 +764,12 @@ do -- CARGO_UNIT -- if there is no ToPointVec2 given, then use the CargoRoutePointVec2 - ToPointVec2 = ToPointVec2 or CargoRoutePointVec2 + ToPointVec2 = ToPointVec2 or CargoCarrierPointVec2:GetRandomCoordinateInRadius( NearRadius, 5 ) local DirectionVec3 = CargoCarrierPointVec2:GetDirectionVec3(ToPointVec2) local Angle = CargoCarrierPointVec2:GetAngleDegrees(DirectionVec3) local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( DeployDistance, Angle ) + local CargoDeployPointVec2 = CargoCarrierPointVec2:GetRandomCoordinateInRadius( NearRadius, 5 ) local FromPointVec2 = CargoCarrierPointVec2 @@ -937,7 +938,7 @@ do -- CARGO_UNIT -- @param #string Event -- @param #string From -- @param #string To - -- @param Wrapper.Unit#UNIT CargoCarrier + -- @param Wrapper.Client#CLIENT CargoCarrier -- @param #number NearRadius function CARGO_UNIT:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) self:F( { From, Event, To, CargoCarrier.UnitName, NearRadius } ) @@ -1366,6 +1367,8 @@ function CARGO_GROUP:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius self:F( {From, Event, To, ToPointVec2, NearRadius } ) NearRadius = NearRadius or 25 + + local CargoCarrier = self.CargoCarrier -- Wrapper.Client#CLIENT local Timer = 1 diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index b5556abea..f3eb0b5b7 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -303,6 +303,18 @@ do -- COORDINATE end + --- Return a random Coordinate within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE. + -- @param #COORDINATE self + -- @param Dcs.DCSTypes#Distance OuterRadius + -- @param Dcs.DCSTypes#Distance InnerRadius + -- @return #COORDINATE + function COORDINATE:GetRandomCoordinateInRadius( OuterRadius, InnerRadius ) + self:F2( { OuterRadius, InnerRadius } ) + + return COORDINATE:NewFromVec2( self:GetRandomVec2InRadius( OuterRadius, InnerRadius ) ) + end + + --- Return a random Vec3 within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE. -- @param #COORDINATE self -- @param Dcs.DCSTypes#Distance OuterRadius From 7088c4c426dc597198c1f509f81d9acf26221b57 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sun, 25 Mar 2018 08:15:13 +0200 Subject: [PATCH 003/420] updated branched creation of include repo --- .appveyor/appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.appveyor/appveyor.yml b/.appveyor/appveyor.yml index 8f77f698d..1430d6ceb 100644 --- a/.appveyor/appveyor.yml +++ b/.appveyor/appveyor.yml @@ -48,7 +48,7 @@ install: build_script: - ps: | - if( $env:appveyor_repo_branch -eq 'master' ) + if( $env:appveyor_repo_branch -eq 'master' -or $env:appveyor_repo_branch -eq 'develop' ) { $apiUrl = 'https://ci.appveyor.com/api' $token = 'qts80b5kpq0ooj4x6vvw' @@ -56,7 +56,7 @@ build_script: "Authorization" = "Bearer $token" "Content-type" = "application/json" } - $RequestBody = @{ accountName = 'FlightControl-Master'; projectSlug = 'moose-include'; branch = 'master'; environmentVariables = @{} } | ConvertTo-Json + $RequestBody = @{ accountName = 'FlightControl-Master'; projectSlug = 'moose-include'; branch = "$env:appveyor_repo_branch"; environmentVariables = @{} } | ConvertTo-Json # get project with last build details $project = Invoke-RestMethod -method Post -Uri "$apiUrl/builds" -Headers $headers -Body $RequestBody } From c4f2446b92497662657c7359b718e781464ee03d Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sun, 25 Mar 2018 08:17:33 +0200 Subject: [PATCH 004/420] fix --- .appveyor/appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor/appveyor.yml b/.appveyor/appveyor.yml index 1430d6ceb..ce7a2f7b6 100644 --- a/.appveyor/appveyor.yml +++ b/.appveyor/appveyor.yml @@ -57,7 +57,7 @@ build_script: "Content-type" = "application/json" } $RequestBody = @{ accountName = 'FlightControl-Master'; projectSlug = 'moose-include'; branch = "$env:appveyor_repo_branch"; environmentVariables = @{} } | ConvertTo-Json - # get project with last build details + # Generate the new version ... $project = Invoke-RestMethod -method Post -Uri "$apiUrl/builds" -Headers $headers -Body $RequestBody } - ps: | From cce90b1f46da60f844ef1e851c4825320ac141a7 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 26 Mar 2018 06:52:24 +0200 Subject: [PATCH 005/420] New AI_CARGO_TROOPS class --- .../Moose/AI/AI_Cargo_Troops.lua | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 Moose Development/Moose/AI/AI_Cargo_Troops.lua diff --git a/Moose Development/Moose/AI/AI_Cargo_Troops.lua b/Moose Development/Moose/AI/AI_Cargo_Troops.lua new file mode 100644 index 000000000..373c6682a --- /dev/null +++ b/Moose Development/Moose/AI/AI_Cargo_Troops.lua @@ -0,0 +1,105 @@ +--- **AI** -- (R2.3) - Models the intelligent transportation of infantry (cargo). +-- +-- === +-- +-- ### Author: **FlightControl** +-- +-- === +-- +-- @module AI_Cargo_Troops + +--- @type AI_Cargo_Troops +-- @extends Core.Base#BASE + + +--- # AI\_CARGO\_TROOPS class, extends @{Core.Base@BASE} +-- +-- === +-- +-- @field #AI_CARGO_TROOPS +AI_CARGO_TROOPS = { + ClassName = "AI_CARGO_TROOPS", +} + +--- Creates a new AI_CARGO_TROOPS object +-- @param #AI_CARGO_TROOPS self +-- @return #AI_CARGO_TROOPS +function AI_CARGO_TROOPS:New( CargoCarrier, CargoGroup, CombatRadius ) + + local self = BASE:Inherit( self, FSM_CONTROLLABLE:New( ) ) -- #AI_CARGO_TROOPS + + self.CargoCarrier = CargoCarrier -- Wrapper.Unit#UNIT + self.CargoGroup = CargoGroup -- Core.Cargo#CARGO_GROUP + self.CombatRadius = CombatRadius + + self:SetControllable( self.CargoCarrier ) + + self:SetStartState( "UnLoaded" ) + + self:AddTransition( "*", "Load", "Boarding" ) + self:AddTransition( "Boarding", "Boarding", "Boarding" ) + self:AddTransition( "Boarding", "Loaded", "Loaded" ) + self:AddTransition( "Loaded", "Unload", "Unboarding" ) + self:AddTransition( "UnBoarding", "Unloaded", "Unloaded" ) + + self:AddTransition( "*", "Monitor", "*" ) + + self:__Monitor( 1 ) + self:__Load( 1 ) + + return self +end + + +--- @param #AI_CARGO_TROOPS self +-- @param Wrapper.Unit#UNIT CargoCarrier +function AI_CARGO_TROOPS:onafterMonitor( CargoCarrier, From, Event, To ) + self:F( { CargoCarrier, From, Event, To } ) + + if CargoCarrier and CargoCarrier:IsAlive() then + end + + self:__Monitor( 1 ) + +end + + +--- @param #AI_CARGO_TROOPS self +-- @param Wrapper.Unit#UNIT CargoCarrier +function AI_CARGO_TROOPS:onafterLoad( CargoCarrier, From, Event, To ) + self:F( { CargoCarrier, From, Event, To } ) + + if CargoCarrier and CargoCarrier:IsAlive() then + self.CargoGroup:__Board( 1, CargoCarrier, 100 ) + self:__Boarding( 1 ) + end + +end + +--- @param #AI_CARGO_TROOPS self +-- @param Wrapper.Unit#UNIT CargoCarrier +function AI_CARGO_TROOPS:onafterBoarding( CargoCarrier, From, Event, To ) + self:F( { CargoCarrier, From, Event, To } ) + + if CargoCarrier and CargoCarrier:IsAlive() then + if self.CargoGroup:IsBoarding() then + self:__Boarding( 1 ) + end + + if self.CargoGroup:IsLoaded() then + self:__Loaded( 1 ) + end + end + +end + +--- @param #AI_CARGO_TROOPS self +-- @param Wrapper.Unit#UNIT CargoCarrier +function AI_CARGO_TROOPS:onafterLoaded( CargoCarrier, From, Event, To ) + self:F( { CargoCarrier, From, Event, To } ) + + if CargoCarrier and CargoCarrier:IsAlive() then + end + +end + From e7518d69e66c8c1b24591e11b0621f000b7d87d1 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 26 Mar 2018 07:39:55 +0200 Subject: [PATCH 006/420] Cargo Troops --- .../Moose/AI/AI_Cargo_Troops.lua | 25 +++++++++++++++-- Moose Development/Moose/Core/Point.lua | 28 +++++++++++++++++++ .../Moose/Wrapper/Controllable.lua | 22 +++++++++++++++ 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Troops.lua b/Moose Development/Moose/AI/AI_Cargo_Troops.lua index 373c6682a..58374618f 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Troops.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Troops.lua @@ -8,8 +8,8 @@ -- -- @module AI_Cargo_Troops ---- @type AI_Cargo_Troops --- @extends Core.Base#BASE +--- @type AI_CARGO_TROOPS +-- @extends Core.Fsm#FSM_CONTROLLABLE --- # AI\_CARGO\_TROOPS class, extends @{Core.Base@BASE} @@ -19,6 +19,7 @@ -- @field #AI_CARGO_TROOPS AI_CARGO_TROOPS = { ClassName = "AI_CARGO_TROOPS", + Coordinate = nil -- Core.Point#COORDINATE, } --- Creates a new AI_CARGO_TROOPS object @@ -32,6 +33,9 @@ function AI_CARGO_TROOPS:New( CargoCarrier, CargoGroup, CombatRadius ) self.CargoGroup = CargoGroup -- Core.Cargo#CARGO_GROUP self.CombatRadius = CombatRadius + self.Zone = ZONE_UNIT:New( self.CargoCarrier:GetName() .. "-Zone", self.CargoCarrier, CombatRadius ) + self.Coalition = self.CargoCarrier:GetCoalition() + self:SetControllable( self.CargoCarrier ) self:SetStartState( "UnLoaded" ) @@ -57,6 +61,22 @@ function AI_CARGO_TROOPS:onafterMonitor( CargoCarrier, From, Event, To ) self:F( { CargoCarrier, From, Event, To } ) if CargoCarrier and CargoCarrier:IsAlive() then + if self.Coordinate then + local Coordinate = CargoCarrier:GetCoordinate() + if Coordinate:IsAtCoordinate2D( self.Coordinate, 2 ) then + self.Zone:Scan( { Object.Category.UNIT } ) + if self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then + else + if not self:Is( "Unloaded" ) then + -- There are enemies within combat range. Unload the CargoCarrier. + self:__Unload( 1 ) + self.CargoCarrier:RouteStop() + end + end + end + else + self.Coordinate = CargoCarrier:GetCoordinate() + end end self:__Monitor( 1 ) @@ -99,6 +119,7 @@ function AI_CARGO_TROOPS:onafterLoaded( CargoCarrier, From, Event, To ) self:F( { CargoCarrier, From, Event, To } ) if CargoCarrier and CargoCarrier:IsAlive() then + CargoCarrier:RouteStop() end end diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index b5556abea..df0a3d21a 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -192,6 +192,20 @@ do -- COORDINATE return self end + --- COORDINATE constructor. + -- @param #COORDINATE self + -- @param #COORDINATE Coordinate. + -- @return #COORDINATE + function COORDINATE:NewFromCoordinate( Coordinate ) + + local self = BASE:Inherit( self, BASE:New() ) -- #COORDINATE + self.x = Coordinate.x + self.y = Coordinate.y + self.z = Coordinate.z + + return self + end + --- Create a new COORDINATE object from Vec2 coordinates. -- @param #COORDINATE self -- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 point. @@ -241,6 +255,20 @@ do -- COORDINATE return { x = self.x, y = self.z } end + + --- Returns if the 2 coordinates are at the same 2D position. + -- @param #COORDINATE self + -- @return #boolean true if at the same position. + function COORDINATE:IsAtCoordinate2D( Coordinate, Precision ) + + local x = Coordinate.x + local z = Coordinate.z + + return x - Precision <= self.x and x + Precision >= self.x and z - Precision <= self.z and z + Precision >= self.z + end + + + --TODO: check this to replace --- Calculate the distance from a reference @{DCSTypes#Vec2}. -- @param #COORDINATE self diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index fe3d1378d..01fe228e4 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1908,6 +1908,28 @@ function CONTROLLABLE:Route( Route, DelaySeconds ) end +--- Stops the movement of the vehicle on the route. +-- @param #CONTROLLABLE self +-- @return #CONTROLLABLE +function CONTROLLABLE:RouteStop() + self:F2() + + local CommandStop = self:CommandStopRoute( true ) + self:SetCommand( CommandStop ) + +end + +--- Resumes the movement of the vehicle on the route. +-- @param #CONTROLLABLE self +-- @return #CONTROLLABLE +function CONTROLLABLE:RouteResume() + self:F2() + + local CommandResume = self:CommandStopRoute( false ) + self:SetCommand( CommandResume ) + +end + --- Make the GROUND Controllable to drive towards a specific point. -- @param #CONTROLLABLE self -- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to. From b6fc46fdd01a66d4aba855db96eafd547fcc5af2 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 26 Mar 2018 17:53:23 +0200 Subject: [PATCH 007/420] Cargo Troops --- .../Moose/AI/AI_Cargo_Troops.lua | 89 ++++++++++++++----- Moose Development/Moose/Core/Cargo.lua | 22 +++++ 2 files changed, 90 insertions(+), 21 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Troops.lua b/Moose Development/Moose/AI/AI_Cargo_Troops.lua index 58374618f..7cb69ab0c 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Troops.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Troops.lua @@ -36,15 +36,16 @@ function AI_CARGO_TROOPS:New( CargoCarrier, CargoGroup, CombatRadius ) self.Zone = ZONE_UNIT:New( self.CargoCarrier:GetName() .. "-Zone", self.CargoCarrier, CombatRadius ) self.Coalition = self.CargoCarrier:GetCoalition() - self:SetControllable( self.CargoCarrier ) + self:SetControllable( CargoCarrier ) self:SetStartState( "UnLoaded" ) self:AddTransition( "*", "Load", "Boarding" ) - self:AddTransition( "Boarding", "Boarding", "Boarding" ) + self:AddTransition( "Boarding", "Board", "Boarding" ) self:AddTransition( "Boarding", "Loaded", "Loaded" ) self:AddTransition( "Loaded", "Unload", "Unboarding" ) - self:AddTransition( "UnBoarding", "Unloaded", "Unloaded" ) + self:AddTransition( "Unboarding", "Unboard", "Unboarding" ) + self:AddTransition( "Unboarding", "Unloaded", "Unloaded" ) self:AddTransition( "*", "Monitor", "*" ) @@ -63,17 +64,24 @@ function AI_CARGO_TROOPS:onafterMonitor( CargoCarrier, From, Event, To ) if CargoCarrier and CargoCarrier:IsAlive() then if self.Coordinate then local Coordinate = CargoCarrier:GetCoordinate() - if Coordinate:IsAtCoordinate2D( self.Coordinate, 2 ) then - self.Zone:Scan( { Object.Category.UNIT } ) - if self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then - else - if not self:Is( "Unloaded" ) then - -- There are enemies within combat range. Unload the CargoCarrier. - self:__Unload( 1 ) - self.CargoCarrier:RouteStop() - end + self.Zone:Scan( { Object.Category.UNIT } ) + if self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then +-- if self:Is( "Unloaded" ) then +-- -- There are no enemies within combat range. Load the CargoCarrier. +-- self:__Load( 1 ) +-- end + else + if self:Is( "Loaded" ) then + -- There are enemies within combat range. Unload the CargoCarrier. + self:__Unload( 1 ) end end + if self:Is( "Unloaded" ) then + if not Coordinate:IsAtCoordinate2D( self.Coordinate, 2 ) then + --self.CargoGroup:RouteTo( Coordinate, 30 ) + end + end + else self.Coordinate = CargoCarrier:GetCoordinate() end @@ -90,23 +98,23 @@ function AI_CARGO_TROOPS:onafterLoad( CargoCarrier, From, Event, To ) self:F( { CargoCarrier, From, Event, To } ) if CargoCarrier and CargoCarrier:IsAlive() then - self.CargoGroup:__Board( 1, CargoCarrier, 100 ) - self:__Boarding( 1 ) + CargoCarrier:RouteStop() + self:Board() + self.CargoGroup:Board( CargoCarrier, 100 ) end end --- @param #AI_CARGO_TROOPS self -- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_TROOPS:onafterBoarding( CargoCarrier, From, Event, To ) +function AI_CARGO_TROOPS:onafterBoard( CargoCarrier, From, Event, To ) self:F( { CargoCarrier, From, Event, To } ) if CargoCarrier and CargoCarrier:IsAlive() then - if self.CargoGroup:IsBoarding() then - self:__Boarding( 1 ) - end - - if self.CargoGroup:IsLoaded() then + self:F({ IsLoaded = self.CargoGroup:IsLoaded() } ) + if not self.CargoGroup:IsLoaded() then + self:__Board( 1 ) + else self:__Loaded( 1 ) end end @@ -119,8 +127,47 @@ function AI_CARGO_TROOPS:onafterLoaded( CargoCarrier, From, Event, To ) self:F( { CargoCarrier, From, Event, To } ) if CargoCarrier and CargoCarrier:IsAlive() then - CargoCarrier:RouteStop() + CargoCarrier:RouteResume() end end + +--- @param #AI_CARGO_TROOPS self +-- @param Wrapper.Unit#UNIT CargoCarrier +function AI_CARGO_TROOPS:onafterUnload( CargoCarrier, From, Event, To ) + self:F( { CargoCarrier, From, Event, To } ) + + if CargoCarrier and CargoCarrier:IsAlive() then + CargoCarrier:RouteStop() + self.CargoGroup:UnBoard( ) + self:__Unboard( 1 ) + end + +end + +--- @param #AI_CARGO_TROOPS self +-- @param Wrapper.Unit#UNIT CargoCarrier +function AI_CARGO_TROOPS:onafterUnboard( CargoCarrier, From, Event, To ) + self:F( { CargoCarrier, From, Event, To } ) + + if CargoCarrier and CargoCarrier:IsAlive() then + if not self.CargoGroup:IsUnLoaded() then + self:__Unboard( 1 ) + else + self:Unloaded() + end + end + +end + +--- @param #AI_CARGO_TROOPS self +-- @param Wrapper.Unit#UNIT CargoCarrier +function AI_CARGO_TROOPS:onafterUnloaded( CargoCarrier, From, Event, To ) + self:F( { CargoCarrier, From, Event, To } ) + + if CargoCarrier and CargoCarrier:IsAlive() then + CargoCarrier:RouteResume() + end + +end diff --git a/Moose Development/Moose/Core/Cargo.lua b/Moose Development/Moose/Core/Cargo.lua index 991669931..66f9c04c8 100644 --- a/Moose Development/Moose/Core/Cargo.lua +++ b/Moose Development/Moose/Core/Cargo.lua @@ -1312,6 +1312,7 @@ function CARGO_GROUP:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, if not Boarded then self:__Boarding( 1, CargoCarrier, NearRadius, ... ) else + self:F("Group Cargo is loaded") self:__Load( 1, CargoCarrier, ... ) end else @@ -1458,6 +1459,27 @@ end end end + + --- Route Cargo to Coordinate and randomize locations. + -- @param #CARGO_GROUP self + -- @param Core.Point#COORDINATE Coordinate + function CARGO_GROUP:RouteTo( Coordinate ) + self:F( ) + + --local NearRadius = NearRadius or 25 + + if From == "UnLoaded" then + + -- For each Cargo object within the CARGO_GROUPED, route each object to the CargoLoadPointVec2 + self.CargoSet:ForEach( + function( Cargo, Coordinate ) + Cargo.CargoObject:RouteTo( Coordinate ) + end, Coordinate + ) + + end + + end end -- CARGO_GROUP From 727d64927b79ffd2eb06c10efc7f49abd484219d Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Tue, 27 Mar 2018 12:07:16 +0200 Subject: [PATCH 008/420] First working prototype of AI_CARGO_TROOPS. --- .../Moose/AI/AI_Cargo_Troops.lua | 98 ++- Moose Development/Moose/Core/Cargo.lua | 589 +++++++++--------- Moose Development/Moose/Core/Point.lua | 5 + 3 files changed, 396 insertions(+), 296 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Troops.lua b/Moose Development/Moose/AI/AI_Cargo_Troops.lua index 7cb69ab0c..dd2104ccc 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Troops.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Troops.lua @@ -48,6 +48,8 @@ function AI_CARGO_TROOPS:New( CargoCarrier, CargoGroup, CombatRadius ) self:AddTransition( "Unboarding", "Unloaded", "Unloaded" ) self:AddTransition( "*", "Monitor", "*" ) + self:AddTransition( "*", "Follow", "Following" ) + self:AddTransition( "*", "Guard", "Guarding" ) self:__Monitor( 1 ) self:__Load( 1 ) @@ -56,38 +58,94 @@ function AI_CARGO_TROOPS:New( CargoCarrier, CargoGroup, CombatRadius ) end +--- Follow Infantry to the Carrier. +-- @param #AI_CARGO_TROOPS self +-- @return #AI_CARGO_TROOPS +function AI_CARGO_TROOPS:FollowToCarrier( Me ) + + self = Me + + self:F( { self = self:GetClassNameAndID(), CargoGroup = self.CargoGroup:GetName() } ) + + -- We check if the Cargo is near to the CargoCarrier. + if self.CargoGroup:IsNear( self.CargoCarrier, 5 ) then + + -- The Cargo does not need to follow the Carrier. + self:Guard() + + else + + -- The Cargo needs to continue to follow the Carrier. + if self:Is( "Following" ) then + + -- For each Cargo object within the CARGO_GROUPED, route each object to the CargoLoadPointVec2 + self.CargoGroup.CargoSet:ForEach( + --- @param Core.Cargo#CARGO Cargo + function( Cargo ) + local CargoUnit = Cargo.CargoObject -- Wrapper.Unit#UNIT + self:F( { UnitName = CargoUnit:GetName() } ) + + if CargoUnit:IsAlive() then + + local InfantryGroup = CargoUnit:GetGroup() + self:F( { GroupName = InfantryGroup:GetName() } ) + + local Waypoints = {} + + -- Calculate the new Route. + local FromCoord = InfantryGroup:GetCoordinate() + local FromGround = FromCoord:WaypointGround( 10, "Diamond" ) + table.insert( Waypoints, FromGround ) + + local ToCoord = self.CargoCarrier:GetCoordinate() + local ToGround = ToCoord:WaypointGround( 10, "Diamond" ) + table.insert( Waypoints, ToGround ) + + local TaskRoute = InfantryGroup:TaskFunction( "AI_CARGO_TROOPS.FollowToCarrier", self ) + + self:F({Waypoints = Waypoints}) + local Waypoint = Waypoints[#Waypoints] + InfantryGroup:SetTaskWaypoint( Waypoint, TaskRoute ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone. + + InfantryGroup:Route( Waypoints ) -- Move after a random seconds to the Route. See the Route method for details. + end + end + ) + end + end +end + + --- @param #AI_CARGO_TROOPS self -- @param Wrapper.Unit#UNIT CargoCarrier function AI_CARGO_TROOPS:onafterMonitor( CargoCarrier, From, Event, To ) self:F( { CargoCarrier, From, Event, To } ) if CargoCarrier and CargoCarrier:IsAlive() then - if self.Coordinate then + if self.CarrierCoordinate then local Coordinate = CargoCarrier:GetCoordinate() self.Zone:Scan( { Object.Category.UNIT } ) if self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then --- if self:Is( "Unloaded" ) then --- -- There are no enemies within combat range. Load the CargoCarrier. --- self:__Load( 1 ) --- end + if self:Is( "Unloaded" ) or self:Is( "Guarding" ) then + -- There are no enemies within combat range. Load the CargoCarrier. + self:__Load( 1 ) + end else if self:Is( "Loaded" ) then -- There are enemies within combat range. Unload the CargoCarrier. self:__Unload( 1 ) end end - if self:Is( "Unloaded" ) then - if not Coordinate:IsAtCoordinate2D( self.Coordinate, 2 ) then - --self.CargoGroup:RouteTo( Coordinate, 30 ) + if self:Is( "Guarding" ) then + if not self.CargoGroup:IsNear( CargoCarrier, 5 ) then + self:Follow() end end - - else - self.Coordinate = CargoCarrier:GetCoordinate() end + self.CarrierCoordinate = CargoCarrier:GetCoordinate() end - self:__Monitor( 1 ) + self:__Monitor( -5 ) end @@ -167,7 +225,23 @@ function AI_CARGO_TROOPS:onafterUnloaded( CargoCarrier, From, Event, To ) self:F( { CargoCarrier, From, Event, To } ) if CargoCarrier and CargoCarrier:IsAlive() then + self:Guard() + self.CargoCarrier = CargoCarrier CargoCarrier:RouteResume() end end + + +--- @param #AI_CARGO_TROOPS self +-- @param Wrapper.Unit#UNIT CargoCarrier +function AI_CARGO_TROOPS:onafterFollow( CargoCarrier, From, Event, To ) + self:F( { CargoCarrier, From, Event, To } ) + + self:F( "Follow" ) + if CargoCarrier and CargoCarrier:IsAlive() then + self:FollowToCarrier( self ) + end + +end + diff --git a/Moose Development/Moose/Core/Cargo.lua b/Moose Development/Moose/Core/Cargo.lua index 66f9c04c8..f767c5ea7 100644 --- a/Moose Development/Moose/Core/Cargo.lua +++ b/Moose Development/Moose/Core/Cargo.lua @@ -491,17 +491,22 @@ end -- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). -- @return #boolean function CARGO:IsNear( PointVec2, NearRadius ) - self:F( { PointVec2, NearRadius } ) + self:F( { PointVec2 = PointVec2, NearRadius = NearRadius } ) - --local Distance = PointVec2:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) - local Distance = PointVec2:Get2DDistance( self.CargoObject:GetPointVec2() ) - self:T( Distance ) - - if Distance <= NearRadius then - return true - else - return false + if self.CargoObject:IsAlive() then + --local Distance = PointVec2:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) + self:F( { CargoObjectName = self.CargoObject:GetName() } ) + self:F( { CargoObjectVec2 = self.CargoObject:GetVec2() } ) + self:F( { PointVec2 = PointVec2:GetVec2() } ) + local Distance = PointVec2:Get2DDistance( self.CargoObject:GetPointVec2() ) + self:T( Distance ) + + if Distance <= NearRadius then + return true + end end + + return false end --- Get the current PointVec2 of the cargo. @@ -746,7 +751,7 @@ do -- CARGO_UNIT function CARGO_UNIT:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius ) self:F( { From, Event, To, ToPointVec2, NearRadius } ) - NearRadius = NearRadius or 25 + NearRadius = NearRadius or 100 local Angle = 180 local Speed = 60 @@ -804,7 +809,7 @@ do -- CARGO_UNIT function CARGO_UNIT:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius ) self:F( { From, Event, To, ToPointVec2, NearRadius } ) - NearRadius = NearRadius or 25 + NearRadius = NearRadius or 100 local Angle = 180 local Speed = 10 @@ -831,7 +836,7 @@ do -- CARGO_UNIT function CARGO_UNIT:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius ) self:F( { From, Event, To, ToPointVec2, NearRadius } ) - NearRadius = NearRadius or 25 + NearRadius = NearRadius or 100 self.CargoInAir = self.CargoObject:InAir() @@ -1149,299 +1154,299 @@ do -- CARGO_GROUP ClassName = "CARGO_GROUP", } ---- CARGO_GROUP constructor. --- @param #CARGO_GROUP self --- @param Wrapper.Group#GROUP CargoGroup --- @param #string Type --- @param #string Name --- @param #number ReportRadius (optional) --- @param #number NearRadius (optional) --- @return #CARGO_GROUP -function CARGO_GROUP:New( CargoGroup, Type, Name, ReportRadius ) - local self = BASE:Inherit( self, CARGO_REPORTABLE:New( CargoGroup, Type, Name, 0, ReportRadius ) ) -- #CARGO_GROUP - self:F( { Type, Name, ReportRadius } ) - - self.CargoObject = CargoGroup - self:SetDeployed( false ) - self.CargoGroup = CargoGroup + --- CARGO_GROUP constructor. + -- @param #CARGO_GROUP self + -- @param Wrapper.Group#GROUP CargoGroup + -- @param #string Type + -- @param #string Name + -- @param #number ReportRadius (optional) + -- @param #number NearRadius (optional) + -- @return #CARGO_GROUP + function CARGO_GROUP:New( CargoGroup, Type, Name, ReportRadius ) + local self = BASE:Inherit( self, CARGO_REPORTABLE:New( CargoGroup, Type, Name, 0, ReportRadius ) ) -- #CARGO_GROUP + self:F( { Type, Name, ReportRadius } ) - local WeightGroup = 0 + self.CargoObject = CargoGroup + self:SetDeployed( false ) + self.CargoGroup = CargoGroup + + local WeightGroup = 0 + + for UnitID, UnitData in pairs( CargoGroup:GetUnits() ) do + local Unit = UnitData -- Wrapper.Unit#UNIT + local WeightUnit = Unit:GetDesc().massEmpty + WeightGroup = WeightGroup + WeightUnit + local CargoUnit = CARGO_UNIT:New( Unit, Type, Unit:GetName(), WeightUnit ) + self.CargoSet:Add( CargoUnit:GetName(), CargoUnit ) + end - for UnitID, UnitData in pairs( CargoGroup:GetUnits() ) do - local Unit = UnitData -- Wrapper.Unit#UNIT - local WeightUnit = Unit:GetDesc().massEmpty - WeightGroup = WeightGroup + WeightUnit - local CargoUnit = CARGO_UNIT:New( Unit, Type, Unit:GetName(), WeightUnit ) - self.CargoSet:Add( CargoUnit:GetName(), CargoUnit ) + self:SetWeight( WeightGroup ) + + self:T( { "Weight Cargo", WeightGroup } ) + + -- Cargo objects are added to the _DATABASE and SET_CARGO objects. + _EVENTDISPATCHER:CreateEventNewCargo( self ) + + self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead ) + self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead ) + self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead ) + + self:SetEventPriority( 4 ) + + return self end - self:SetWeight( WeightGroup ) + --- @param #CARGO_GROUP self + -- @param Core.Event#EVENTDATA EventData + function CARGO_GROUP:OnEventCargoDead( EventData ) - self:T( { "Weight Cargo", WeightGroup } ) - - -- Cargo objects are added to the _DATABASE and SET_CARGO objects. - _EVENTDISPATCHER:CreateEventNewCargo( self ) - - self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead ) - self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead ) - self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead ) - - self:SetEventPriority( 4 ) - - return self -end - ---- @param #CARGO_GROUP self --- @param Core.Event#EVENTDATA EventData -function CARGO_GROUP:OnEventCargoDead( EventData ) - - local Destroyed = false - - if self:IsDestroyed() or self:IsUnLoaded() then - Destroyed = true - for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do - local Cargo = CargoData -- #CARGO - if Cargo:IsAlive() then - Destroyed = false - else - Cargo:Destroyed() - end - end - else - local CarrierName = self.CargoCarrier:GetName() - if CarrierName == EventData.IniDCSUnitName then - MESSAGE:New( "Cargo is lost from carrier " .. CarrierName, 15 ):ToAll() + local Destroyed = false + + if self:IsDestroyed() or self:IsUnLoaded() then Destroyed = true - self.CargoCarrier:ClearCargo() + for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do + local Cargo = CargoData -- #CARGO + if Cargo:IsAlive() then + Destroyed = false + else + Cargo:Destroyed() + end + end + else + local CarrierName = self.CargoCarrier:GetName() + if CarrierName == EventData.IniDCSUnitName then + MESSAGE:New( "Cargo is lost from carrier " .. CarrierName, 15 ):ToAll() + Destroyed = true + self.CargoCarrier:ClearCargo() + end end - end - - if Destroyed then - self:Destroyed() - self:E( { "Cargo group destroyed" } ) - end - -end - ---- Enter Boarding State. --- @param #CARGO_GROUP self --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #string Event --- @param #string From --- @param #string To -function CARGO_GROUP:onenterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) - - local NearRadius = NearRadius or 25 - - if From == "UnLoaded" then - - -- For each Cargo object within the CARGO_GROUPED, route each object to the CargoLoadPointVec2 - self.CargoSet:ForEach( - function( Cargo, ... ) - Cargo:__Board( 1, CargoCarrier, NearRadius, ... ) - end, ... - ) - self:__Boarding( 1, CargoCarrier, NearRadius, ... ) + if Destroyed then + self:Destroyed() + self:E( { "Cargo group destroyed" } ) + end + end - -end ---- Enter Loaded State. --- @param #CARGO_GROUP self --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #string Event --- @param #string From --- @param #string To -function CARGO_GROUP:onenterLoaded( From, Event, To, CargoCarrier, ... ) - self:F( { From, Event, To, CargoCarrier, ...} ) + --- Enter Boarding State. + -- @param #CARGO_GROUP self + -- @param Wrapper.Unit#UNIT CargoCarrier + -- @param #string Event + -- @param #string From + -- @param #string To + function CARGO_GROUP:onenterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) + self:F( { CargoCarrier.UnitName, From, Event, To } ) + + local NearRadius = NearRadius or 25 + + if From == "UnLoaded" then - if From == "UnLoaded" then - -- For each Cargo object within the CARGO_GROUP, load each cargo to the CargoCarrier. + -- For each Cargo object within the CARGO_GROUPED, route each object to the CargoLoadPointVec2 + self.CargoSet:ForEach( + function( Cargo, ... ) + Cargo:__Board( 1, CargoCarrier, NearRadius, ... ) + end, ... + ) + + self:__Boarding( 1, CargoCarrier, NearRadius, ... ) + end + + end + + --- Enter Loaded State. + -- @param #CARGO_GROUP self + -- @param Wrapper.Unit#UNIT CargoCarrier + -- @param #string Event + -- @param #string From + -- @param #string To + function CARGO_GROUP:onenterLoaded( From, Event, To, CargoCarrier, ... ) + self:F( { From, Event, To, CargoCarrier, ...} ) + + if From == "UnLoaded" then + -- For each Cargo object within the CARGO_GROUP, load each cargo to the CargoCarrier. + for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do + Cargo:Load( CargoCarrier ) + end + end + + --self.CargoObject:Destroy() + self.CargoCarrier = CargoCarrier + + end + + --- Leave Boarding State. + -- @param #CARGO_GROUP self + -- @param Wrapper.Unit#UNIT CargoCarrier + -- @param #string Event + -- @param #string From + -- @param #string To + function CARGO_GROUP:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) + self:F( { CargoCarrier.UnitName, From, Event, To } ) + + local NearRadius = NearRadius or 100 + + local Boarded = true + local Cancelled = false + local Dead = true + + self.CargoSet:Flush() + + -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do - Cargo:Load( CargoCarrier ) - end - end + self:T( { Cargo:GetName(), Cargo.current } ) + + + if not Cargo:is( "Loaded" ) + and (not Cargo:is( "Destroyed" )) then -- If one or more units of a group defined as CARGO_GROUP died, the CARGO_GROUP:Board() command does not trigger the CARGO_GRUOP:OnEnterLoaded() function. + Boarded = false + end + + if Cargo:is( "UnLoaded" ) then + Cancelled = true + end - --self.CargoObject:Destroy() - self.CargoCarrier = CargoCarrier + if not Cargo:is( "Destroyed" ) then + Dead = false + end + + end -end - ---- Leave Boarding State. --- @param #CARGO_GROUP self --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #string Event --- @param #string From --- @param #string To -function CARGO_GROUP:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) - - local NearRadius = NearRadius or 25 - - local Boarded = true - local Cancelled = false - local Dead = true - - self.CargoSet:Flush() - - -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 - for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do - self:T( { Cargo:GetName(), Cargo.current } ) - - - if not Cargo:is( "Loaded" ) - and (not Cargo:is( "Destroyed" )) then -- If one or more units of a group defined as CARGO_GROUP died, the CARGO_GROUP:Board() command does not trigger the CARGO_GRUOP:OnEnterLoaded() function. - Boarded = false - end - - if Cargo:is( "UnLoaded" ) then - Cancelled = true - end - - if not Cargo:is( "Destroyed" ) then - Dead = false - end - - end - - if not Dead then - - if not Cancelled then - if not Boarded then - self:__Boarding( 1, CargoCarrier, NearRadius, ... ) + if not Dead then + + if not Cancelled then + if not Boarded then + self:__Boarding( 1, CargoCarrier, NearRadius, ... ) + else + self:F("Group Cargo is loaded") + self:__Load( 1, CargoCarrier, ... ) + end else - self:F("Group Cargo is loaded") - self:__Load( 1, CargoCarrier, ... ) + self:__CancelBoarding( 1, CargoCarrier, NearRadius, ... ) end else - self:__CancelBoarding( 1, CargoCarrier, NearRadius, ... ) + self:__Destroyed( 1, CargoCarrier, NearRadius, ... ) end - else - self:__Destroyed( 1, CargoCarrier, NearRadius, ... ) + end -end - ---- Get the amount of cargo units in the group. --- @param #CARGO_GROUP self --- @return #CARGO_GROUP -function CARGO_GROUP:GetCount() - return self.CargoSet:Count() -end - - ---- Enter UnBoarding State. --- @param #CARGO_GROUP self --- @param Core.Point#POINT_VEC2 ToPointVec2 --- @param #string Event --- @param #string From --- @param #string To -function CARGO_GROUP:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) - self:F( {From, Event, To, ToPointVec2, NearRadius } ) - - NearRadius = NearRadius or 25 - - local Timer = 1 - - if From == "Loaded" then - - if self.CargoObject then - self.CargoObject:Destroy() - end - - -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 - self.CargoSet:ForEach( - function( Cargo, NearRadius ) - - Cargo:__UnBoard( Timer, ToPointVec2, NearRadius ) - Timer = Timer + 10 - end, { NearRadius } - ) - - - self:__UnBoarding( 1, ToPointVec2, NearRadius, ... ) + --- Get the amount of cargo units in the group. + -- @param #CARGO_GROUP self + -- @return #CARGO_GROUP + function CARGO_GROUP:GetCount() + return self.CargoSet:Count() end -end ---- Leave UnBoarding State. --- @param #CARGO_GROUP self --- @param Core.Point#POINT_VEC2 ToPointVec2 --- @param #string Event --- @param #string From --- @param #string To -function CARGO_GROUP:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) - self:F( { From, Event, To, ToPointVec2, NearRadius } ) - - --local NearRadius = NearRadius or 25 - - local Angle = 180 - local Speed = 10 - local Distance = 5 - - if From == "UnBoarding" then - local UnBoarded = true - - -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 - for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do - self:T( Cargo.current ) - if not Cargo:is( "UnLoaded" ) then - UnBoarded = false + --- Enter UnBoarding State. + -- @param #CARGO_GROUP self + -- @param Core.Point#POINT_VEC2 ToPointVec2 + -- @param #string Event + -- @param #string From + -- @param #string To + function CARGO_GROUP:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) + self:F( {From, Event, To, ToPointVec2, NearRadius } ) + + NearRadius = NearRadius or 100 + + local Timer = 1 + + if From == "Loaded" then + + if self.CargoObject then + self.CargoObject:Destroy() end - end - if UnBoarded then - return true - else + -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 + self.CargoSet:ForEach( + function( Cargo, NearRadius ) + + Cargo:__UnBoard( Timer, ToPointVec2, NearRadius ) + Timer = Timer + 10 + end, { NearRadius } + ) + + self:__UnBoarding( 1, ToPointVec2, NearRadius, ... ) end - - return false - end -end + end ---- UnBoard Event. --- @param #CARGO_GROUP self --- @param Core.Point#POINT_VEC2 ToPointVec2 --- @param #string Event --- @param #string From --- @param #string To -function CARGO_GROUP:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) - self:F( { From, Event, To, ToPointVec2, NearRadius } ) - - --local NearRadius = NearRadius or 25 - - self:__UnLoad( 1, ToPointVec2, ... ) -end - - - ---- Enter UnLoaded State. --- @param #CARGO_GROUP self --- @param Core.Point#POINT_VEC2 --- @param #string Event --- @param #string From --- @param #string To -function CARGO_GROUP:onenterUnLoaded( From, Event, To, ToPointVec2, ... ) - self:F( { From, Event, To, ToPointVec2 } ) - - if From == "Loaded" then - - -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 - self.CargoSet:ForEach( - function( Cargo ) - --Cargo:UnLoad( ToPointVec2 ) - local RandomVec2=ToPointVec2:GetRandomPointVec2InRadius(10) - Cargo:UnLoad( RandomVec2 ) + --- Leave UnBoarding State. + -- @param #CARGO_GROUP self + -- @param Core.Point#POINT_VEC2 ToPointVec2 + -- @param #string Event + -- @param #string From + -- @param #string To + function CARGO_GROUP:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) + self:F( { From, Event, To, ToPointVec2, NearRadius } ) + + --local NearRadius = NearRadius or 25 + + local Angle = 180 + local Speed = 10 + local Distance = 5 + + if From == "UnBoarding" then + local UnBoarded = true + + -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 + for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do + self:T( Cargo.current ) + if not Cargo:is( "UnLoaded" ) then + UnBoarded = false + end end - ) - + + if UnBoarded then + return true + else + self:__UnBoarding( 1, ToPointVec2, NearRadius, ... ) + end + + return false + end + end + + --- UnBoard Event. + -- @param #CARGO_GROUP self + -- @param Core.Point#POINT_VEC2 ToPointVec2 + -- @param #string Event + -- @param #string From + -- @param #string To + function CARGO_GROUP:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) + self:F( { From, Event, To, ToPointVec2, NearRadius } ) -end + --local NearRadius = NearRadius or 25 + + self:__UnLoad( 1, ToPointVec2, ... ) + end + + + + --- Enter UnLoaded State. + -- @param #CARGO_GROUP self + -- @param Core.Point#POINT_VEC2 + -- @param #string Event + -- @param #string From + -- @param #string To + function CARGO_GROUP:onenterUnLoaded( From, Event, To, ToPointVec2, ... ) + self:F( { From, Event, To, ToPointVec2 } ) + + if From == "Loaded" then + + -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 + self.CargoSet:ForEach( + function( Cargo ) + --Cargo:UnLoad( ToPointVec2 ) + local RandomVec2=ToPointVec2:GetRandomPointVec2InRadius(10) + Cargo:UnLoad( RandomVec2 ) + end + ) + + end + + end --- Respawn the cargo when destroyed @@ -1464,21 +1469,37 @@ end -- @param #CARGO_GROUP self -- @param Core.Point#COORDINATE Coordinate function CARGO_GROUP:RouteTo( Coordinate ) - self:F( ) + self:F( {Coordinate = Coordinate } ) - --local NearRadius = NearRadius or 25 - - if From == "UnLoaded" then - - -- For each Cargo object within the CARGO_GROUPED, route each object to the CargoLoadPointVec2 - self.CargoSet:ForEach( - function( Cargo, Coordinate ) - Cargo.CargoObject:RouteTo( Coordinate ) - end, Coordinate - ) + -- For each Cargo object within the CARGO_GROUPED, route each object to the CargoLoadPointVec2 + self.CargoSet:ForEach( + function( Cargo ) + Cargo.CargoObject:RouteGroundTo( Coordinate, 10, "vee", 0 ) + end + ) + end + + --- Check if Cargo is near to the Carrier. + -- The Cargo is near to the Carrier if the first unit of the Cargo Group is within NearRadius. + -- @param #CARGO_GROUP self + -- @param Wrapper.Group#GROUP CargoCarrier + -- @param #number NearRadius + -- @return #boolean The Cargo is near to the Carrier. + -- @return #nil The Cargo is not near to the Carrier. + function CARGO_GROUP:IsNear( CargoCarrier, NearRadius ) + self:F( {NearRadius = NearRadius } ) + + local FirstCargo = self.CargoSet:GetFirst() -- #CARGO + + if FirstCargo then + if FirstCargo:IsNear( CargoCarrier:GetCoordinate(), NearRadius ) then + self:F( "Near" ) + return true + end end + return nil end end -- CARGO_GROUP diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index df0a3d21a..0a787216a 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -258,9 +258,14 @@ do -- COORDINATE --- Returns if the 2 coordinates are at the same 2D position. -- @param #COORDINATE self + -- @param #COORDINATE Coordinate + -- @param #number Precision -- @return #boolean true if at the same position. function COORDINATE:IsAtCoordinate2D( Coordinate, Precision ) + self:F( { Coordinate = Coordinate:GetVec2() } ) + self:F( { self = self:GetVec2() } ) + local x = Coordinate.x local z = Coordinate.z From 9759640d52264a3042366b4b46209b0753714a2a Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Tue, 27 Mar 2018 13:46:55 +0200 Subject: [PATCH 009/420] comment --- Moose Development/Moose/AI/AI_Cargo_Troops.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Troops.lua b/Moose Development/Moose/AI/AI_Cargo_Troops.lua index dd2104ccc..eecf65e2b 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Troops.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Troops.lua @@ -22,7 +22,7 @@ AI_CARGO_TROOPS = { Coordinate = nil -- Core.Point#COORDINATE, } ---- Creates a new AI_CARGO_TROOPS object +--- Creates a new AI_CARGO_TROOPS object. -- @param #AI_CARGO_TROOPS self -- @return #AI_CARGO_TROOPS function AI_CARGO_TROOPS:New( CargoCarrier, CargoGroup, CombatRadius ) From 21a7bac4e0938071fbd3ec34c6a3cf11086b46fb Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Tue, 27 Mar 2018 13:56:53 +0200 Subject: [PATCH 010/420] added file --- Moose Setup/Moose.files | 1 + 1 file changed, 1 insertion(+) diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index 1e575f0f2..49a8fc08b 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -61,6 +61,7 @@ AI/AI_Cap.lua AI/AI_Cas.lua AI/AI_Bai.lua AI/AI_Formation.lua +AI/AI_Cargo_Troops.lua Actions/Act_Assign.lua Actions/Act_Route.lua From b1a1c6c5523c77b10dc513c6233d84f80146f146 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Tue, 27 Mar 2018 14:16:04 +0200 Subject: [PATCH 011/420] -- The infantry must only be 5 meters near. --- Moose Development/Moose/AI/AI_Cargo_Troops.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Troops.lua b/Moose Development/Moose/AI/AI_Cargo_Troops.lua index eecf65e2b..55494fa21 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Troops.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Troops.lua @@ -158,7 +158,7 @@ function AI_CARGO_TROOPS:onafterLoad( CargoCarrier, From, Event, To ) if CargoCarrier and CargoCarrier:IsAlive() then CargoCarrier:RouteStop() self:Board() - self.CargoGroup:Board( CargoCarrier, 100 ) + self.CargoGroup:Board( CargoCarrier, 5 ) end end From 7a579a0ab5e8c7ad71f480c18bed8cf160f730c3 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Tue, 27 Mar 2018 20:59:37 +0200 Subject: [PATCH 012/420] -- Fixed a lot of issues with cargo when the cargo representative is destroyed. --- .../Moose/AI/AI_Cargo_Troops.lua | 4 +- Moose Development/Moose/Core/Cargo.lua | 75 ++++++++++--------- 2 files changed, 42 insertions(+), 37 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Troops.lua b/Moose Development/Moose/AI/AI_Cargo_Troops.lua index 55494fa21..8c6491753 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Troops.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Troops.lua @@ -126,7 +126,7 @@ function AI_CARGO_TROOPS:onafterMonitor( CargoCarrier, From, Event, To ) local Coordinate = CargoCarrier:GetCoordinate() self.Zone:Scan( { Object.Category.UNIT } ) if self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then - if self:Is( "Unloaded" ) or self:Is( "Guarding" ) then + if self:Is( "Unloaded" ) or self:Is( "Guarding" ) or self:Is( "Following" ) then -- There are no enemies within combat range. Load the CargoCarrier. self:__Load( 1 ) end @@ -158,7 +158,7 @@ function AI_CARGO_TROOPS:onafterLoad( CargoCarrier, From, Event, To ) if CargoCarrier and CargoCarrier:IsAlive() then CargoCarrier:RouteStop() self:Board() - self.CargoGroup:Board( CargoCarrier, 5 ) + self.CargoGroup:Board( CargoCarrier, 25 ) end end diff --git a/Moose Development/Moose/Core/Cargo.lua b/Moose Development/Moose/Core/Cargo.lua index f767c5ea7..4cc5eac2b 100644 --- a/Moose Development/Moose/Core/Cargo.lua +++ b/Moose Development/Moose/Core/Cargo.lua @@ -760,41 +760,44 @@ do -- CARGO_UNIT if From == "Loaded" then - local CargoCarrier = self.CargoCarrier -- Wrapper.Controllable#CONTROLLABLE + if not self:IsDestroyed() then - local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - - - local CargoRoutePointVec2 = CargoCarrierPointVec2:Translate( RouteDistance, CargoDeployHeading ) - - - -- if there is no ToPointVec2 given, then use the CargoRoutePointVec2 - ToPointVec2 = ToPointVec2 or CargoRoutePointVec2 - local DirectionVec3 = CargoCarrierPointVec2:GetDirectionVec3(ToPointVec2) - local Angle = CargoCarrierPointVec2:GetAngleDegrees(DirectionVec3) - - local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( DeployDistance, Angle ) - - local FromPointVec2 = CargoCarrierPointVec2 - - -- Respawn the group... - if self.CargoObject then - self.CargoObject:ReSpawn( CargoDeployPointVec2:GetVec3(), CargoDeployHeading ) - self:F( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } ) - self.CargoCarrier = nil - - local Points = {} - Points[#Points+1] = CargoCarrierPointVec2:WaypointGround( Speed ) - - Points[#Points+1] = ToPointVec2:WaypointGround( Speed ) + local CargoCarrier = self.CargoCarrier -- Wrapper.Controllable#CONTROLLABLE - local TaskRoute = self.CargoObject:TaskRoute( Points ) - self.CargoObject:SetTask( TaskRoute, 1 ) - + local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() + local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + + + local CargoRoutePointVec2 = CargoCarrierPointVec2:Translate( RouteDistance, CargoDeployHeading ) - self:__UnBoarding( 1, ToPointVec2, NearRadius ) + + -- if there is no ToPointVec2 given, then use the CargoRoutePointVec2 + ToPointVec2 = ToPointVec2 or CargoRoutePointVec2 + local DirectionVec3 = CargoCarrierPointVec2:GetDirectionVec3(ToPointVec2) + local Angle = CargoCarrierPointVec2:GetAngleDegrees(DirectionVec3) + + local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( DeployDistance, Angle ) + + local FromPointVec2 = CargoCarrierPointVec2 + + -- Respawn the group... + if self.CargoObject then + self.CargoObject:ReSpawn( CargoDeployPointVec2:GetVec3(), CargoDeployHeading ) + self:F( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } ) + self.CargoCarrier = nil + + local Points = {} + Points[#Points+1] = CargoCarrierPointVec2:WaypointGround( Speed ) + + Points[#Points+1] = ToPointVec2:WaypointGround( Speed ) + + local TaskRoute = self.CargoObject:TaskRoute( Points ) + self.CargoObject:SetTask( TaskRoute, 1 ) + + + self:__UnBoarding( 1, ToPointVec2, NearRadius ) + end end end @@ -949,7 +952,7 @@ do -- CARGO_UNIT self:F( { From, Event, To, CargoCarrier.UnitName, NearRadius } ) - if CargoCarrier and CargoCarrier:IsAlive() then + if CargoCarrier and CargoCarrier:IsAlive() and self.CargoObject and self.CargoObject:IsAlive() then if CargoCarrier:InAir() == false then if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then self:__Load( 1, CargoCarrier, ... ) @@ -1200,9 +1203,11 @@ do -- CARGO_GROUP -- @param Core.Event#EVENTDATA EventData function CARGO_GROUP:OnEventCargoDead( EventData ) + self:_F( { "Dead Event", EventData = EventData } ) + local Destroyed = false - if self:IsDestroyed() or self:IsUnLoaded() then + if self:IsDestroyed() or self:IsUnLoaded() or self:IsBoarding() then Destroyed = true for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do local Cargo = CargoData -- #CARGO @@ -1392,7 +1397,7 @@ do -- CARGO_GROUP -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do self:T( Cargo.current ) - if not Cargo:is( "UnLoaded" ) then + if not Cargo:is( "UnLoaded" ) and not Cargo:IsDestroyed() then UnBoarded = false end end From 7a8881974caa8b340eb235954b36892739e5fb77 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Wed, 28 Mar 2018 16:42:37 +0200 Subject: [PATCH 013/420] Optimizations of cargo deployment. --- .../Moose/AI/AI_Cargo_Troops.lua | 10 +++---- Moose Development/Moose/Core/Cargo.lua | 27 ++++++++++--------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Troops.lua b/Moose Development/Moose/AI/AI_Cargo_Troops.lua index defb6bd81..f0d524be3 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Troops.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Troops.lua @@ -148,7 +148,7 @@ function AI_CARGO_TROOPS:onafterLoad( CargoCarrier, From, Event, To ) if CargoCarrier and CargoCarrier:IsAlive() then CargoCarrier:RouteStop() - self:Board() + self:__Board( 10 ) self.CargoGroup:Board( CargoCarrier, 25 ) end @@ -162,7 +162,7 @@ function AI_CARGO_TROOPS:onafterBoard( CargoCarrier, From, Event, To ) if CargoCarrier and CargoCarrier:IsAlive() then self:F({ IsLoaded = self.CargoGroup:IsLoaded() } ) if not self.CargoGroup:IsLoaded() then - self:__Board( 1 ) + self:__Board( 10 ) else self:__Loaded( 1 ) end @@ -190,7 +190,7 @@ function AI_CARGO_TROOPS:onafterUnload( CargoCarrier, From, Event, To ) if CargoCarrier and CargoCarrier:IsAlive() then CargoCarrier:RouteStop() self.CargoGroup:UnBoard( ) - self:__Unboard( 1 ) + self:__Unboard( 10 ) end end @@ -202,9 +202,9 @@ function AI_CARGO_TROOPS:onafterUnboard( CargoCarrier, From, Event, To ) if CargoCarrier and CargoCarrier:IsAlive() then if not self.CargoGroup:IsUnLoaded() then - self:__Unboard( 1 ) + self:__Unboard( 10 ) else - self:Unloaded() + self:__Unloaded( 1 ) end end diff --git a/Moose Development/Moose/Core/Cargo.lua b/Moose Development/Moose/Core/Cargo.lua index 8cfa3037f..e0a42c690 100644 --- a/Moose Development/Moose/Core/Cargo.lua +++ b/Moose Development/Moose/Core/Cargo.lua @@ -771,25 +771,26 @@ do -- CARGO_UNIT -- if there is no ToPointVec2 given, then use the CargoRoutePointVec2 - ToPointVec2 = ToPointVec2 or CargoCarrierPointVec2:GetRandomCoordinateInRadius( NearRadius, 5 ) - local DirectionVec3 = CargoCarrierPointVec2:GetDirectionVec3(ToPointVec2) - local Angle = CargoCarrierPointVec2:GetAngleDegrees(DirectionVec3) - - local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( DeployDistance, Angle ) - local CargoDeployPointVec2 = CargoCarrierPointVec2:GetRandomCoordinateInRadius( NearRadius, 5 ) + local FromDirectionVec3 = CargoCarrierPointVec2:GetDirectionVec3( ToPointVec2 or CargoRoutePointVec2 ) + local FromAngle = CargoCarrierPointVec2:GetAngleDegrees(FromDirectionVec3) + local FromPointVec2 = CargoCarrierPointVec2:Translate( DeployDistance, FromAngle ) + --local CargoDeployPointVec2 = CargoCarrierPointVec2:GetRandomCoordinateInRadius( 10, 5 ) + + ToPointVec2 = ToPointVec2 or CargoCarrierPointVec2:GetRandomCoordinateInRadius( NearRadius, DeployDistance ) - local FromPointVec2 = CargoCarrierPointVec2 - -- Respawn the group... if self.CargoObject then - self.CargoObject:ReSpawn( CargoDeployPointVec2:GetVec3(), CargoDeployHeading ) + self.CargoObject:ReSpawn( FromPointVec2:GetVec3(), CargoDeployHeading ) self:F( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } ) self.CargoCarrier = nil local Points = {} - Points[#Points+1] = CargoCarrierPointVec2:WaypointGround( Speed ) - Points[#Points+1] = ToPointVec2:WaypointGround( Speed ) + -- From + Points[#Points+1] = FromPointVec2:WaypointGround( Speed, "Vee" ) + + -- To + Points[#Points+1] = ToPointVec2:WaypointGround( Speed, "Vee" ) local TaskRoute = self.CargoObject:TaskRoute( Points ) self.CargoObject:SetTask( TaskRoute, 1 ) @@ -1375,7 +1376,7 @@ function CARGO_GROUP:OnEventCargoDead( EventData ) function CARGO_GROUP:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) self:F( {From, Event, To, ToPointVec2, NearRadius } ) - NearRadius = NearRadius or 100 + NearRadius = NearRadius or 25 local Timer = 1 @@ -1390,7 +1391,7 @@ function CARGO_GROUP:OnEventCargoDead( EventData ) function( Cargo, NearRadius ) Cargo:__UnBoard( Timer, ToPointVec2, NearRadius ) - Timer = Timer + 10 + Timer = Timer + 3 end, { NearRadius } ) From 31a7a4e9935a54156a432c24b11b5620235a08b0 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Wed, 28 Mar 2018 16:48:03 +0200 Subject: [PATCH 014/420] Default of near radius is 25 meters. --- Moose Development/Moose/Core/Cargo.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Cargo.lua b/Moose Development/Moose/Core/Cargo.lua index e0a42c690..4a88b528b 100644 --- a/Moose Development/Moose/Core/Cargo.lua +++ b/Moose Development/Moose/Core/Cargo.lua @@ -749,7 +749,7 @@ do -- CARGO_UNIT function CARGO_UNIT:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius ) self:F( { From, Event, To, ToPointVec2, NearRadius } ) - NearRadius = NearRadius or 100 + NearRadius = NearRadius or 25 local Angle = 180 local Speed = 60 From b29ce9b45eca5ea5676a121d8f13828be943b0fb Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Wed, 28 Mar 2018 18:00:17 +0200 Subject: [PATCH 015/420] Boarding of troops --- Moose Development/Moose/AI/AI_Cargo_Troops.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Troops.lua b/Moose Development/Moose/AI/AI_Cargo_Troops.lua index f0d524be3..754409aeb 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Troops.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Troops.lua @@ -149,7 +149,7 @@ function AI_CARGO_TROOPS:onafterLoad( CargoCarrier, From, Event, To ) if CargoCarrier and CargoCarrier:IsAlive() then CargoCarrier:RouteStop() self:__Board( 10 ) - self.CargoGroup:Board( CargoCarrier, 25 ) + self.CargoGroup:Board( CargoCarrier, 100 ) end end From c37560275ecc68db55806b89aa0666442db14a2c Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Thu, 29 Mar 2018 12:00:11 +0200 Subject: [PATCH 016/420] -- A lot of fixes to the new Cargo handling model. Now also TASK_CARGO_TRANSPORT is working. --- Moose Development/Moose/Core/Cargo.lua | 208 ++++++++++++++++------- Moose Development/Moose/Wrapper/Unit.lua | 8 +- 2 files changed, 152 insertions(+), 64 deletions(-) diff --git a/Moose Development/Moose/Core/Cargo.lua b/Moose Development/Moose/Core/Cargo.lua index 4a88b528b..407e8bd87 100644 --- a/Moose Development/Moose/Core/Cargo.lua +++ b/Moose Development/Moose/Core/Cargo.lua @@ -418,11 +418,12 @@ end --- Smoke the CARGO. -- @param #CARGO self -function CARGO:Smoke( SmokeColor, Range ) - self:F2() +-- @param Utilities.Utils#SMOKECOLOR SmokeColor The color of the smoke. +-- @param #number Radius The radius of randomization around the center of the Cargo. +function CARGO:Smoke( SmokeColor, Radius ) if self:IsUnLoaded() then - if Range then - trigger.action.smoke( self.CargoObject:GetRandomVec3( Range ), SmokeColor ) + if Radius then + trigger.action.smoke( self.CargoObject:GetRandomVec3( Radius ), SmokeColor ) else trigger.action.smoke( self.CargoObject:GetVec3(), SmokeColor ) end @@ -626,31 +627,7 @@ end -- CARGO_REPRESENTABLE return self end - --- Check if CargoCarrier is in the ReportRadius for the Cargo to be Loaded. - -- @param #CARGO_REPORTABLE self - -- @param Core.Point#POINT_VEC2 PointVec2 - -- @return #boolean - function CARGO_REPORTABLE:IsInRadius( PointVec2 ) - self:F( { PointVec2 } ) - - local Distance = 0 - if self:IsLoaded() then - Distance = PointVec2:DistanceFromPointVec2( self.CargoCarrier:GetPointVec2() ) - else - Distance = PointVec2:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) - end - self:T( Distance ) - - if Distance <= self.ReportRadius then - return true - else - return false - end - - - end - - --- Send a CC message to a GROUP. + --- Send a CC message to a @{Group}. -- @param #CARGO_REPORTABLE self -- @param #string Message -- @param Wrapper.Group#GROUP TaskGroup @@ -663,37 +640,13 @@ end -- CARGO_REPRESENTABLE end - --- Get the range till cargo will board. + --- Get the Report radius, which is the radius when the Cargo is reporting itself. -- @param #CARGO_REPORTABLE self - -- @return #number The range till cargo will board. + -- @return #number The range till Cargo reports itself. function CARGO_REPORTABLE:GetBoardingRange() return self.ReportRadius end - --- Respawn the cargo. - -- @param #CARGO_REPORTABLE self - function CARGO_REPORTABLE:Respawn() - - self:F({"Respawning"}) - - for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do - local Cargo = CargoData -- #CARGO - Cargo:Destroy() - Cargo:SetStartState( "UnLoaded" ) - end - - local CargoObject = self.CargoObject -- Wrapper.Group#GROUP - CargoObject:Destroy() - local Template = CargoObject:GetTemplate() - CargoObject:Respawn( Template ) - - self:SetDeployed( false ) - - local WeightGroup = 0 - - self:SetStartState( "UnLoaded" ) - - end end @@ -1495,13 +1448,55 @@ function CARGO_GROUP:OnEventCargoDead( EventData ) end + --- Get the current Coordinate of the CargoGroup. + -- @param #CARGO_GROUP self + -- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup. + -- @return #nil There is no valid Cargo in the CargoGroup. + function CARGO_GROUP:GetCoordinate() + self:F() + + local Cargo = self.CargoSet:GetFirst() + + if Cargo then + return Cargo.CargoObject:GetCoordinate() + end + + return nil + end + + --- Check if the CargoGroup is alive. + -- @param #CARGO_GROUP self + -- @return #boolean true if the CargoGroup is alive. + -- @return #boolean false if the CargoGroup is dead. + function CARGO_GROUP:IsAlive() + + local Alive = true + + -- For each Cargo within the CargoSet, check if the Cargo is Alive. + -- When the Cargo is Loaded, the Cargo is in the CargoCarrier, so we check if the CargoCarrier is alive. + -- When the Cargo is not Loaded, the Cargo is the CargoObject, so we check if the CargoObject is alive. + self.CargoSet:ForEach( + function( Cargo ) + if self:IsLoaded() then + Alive = Alive == true and Cargo.CargoCarrier:IsAlive() + else + Alive = Alive == true and Cargo.CargoObject:IsAlive() + end + end + ) + + return Alive + + end + + --- Route Cargo to Coordinate and randomize locations. -- @param #CARGO_GROUP self -- @param Core.Point#COORDINATE Coordinate function CARGO_GROUP:RouteTo( Coordinate ) self:F( {Coordinate = Coordinate } ) - -- For each Cargo object within the CARGO_GROUPED, route each object to the CargoLoadPointVec2 + -- For each Cargo within the CargoSet, route each object to the Coordinate self.CargoSet:ForEach( function( Cargo ) Cargo.CargoObject:RouteGroundTo( Coordinate, 10, "vee", 0 ) @@ -1520,16 +1515,109 @@ function CARGO_GROUP:OnEventCargoDead( EventData ) function CARGO_GROUP:IsNear( CargoCarrier, NearRadius ) self:F( {NearRadius = NearRadius } ) - local FirstCargo = self.CargoSet:GetFirst() -- #CARGO + local Cargo = self.CargoSet:GetFirst() -- #CARGO - if FirstCargo then - if FirstCargo:IsNear( CargoCarrier:GetCoordinate(), NearRadius ) then - self:F( "Near" ) + if Cargo then + return Cargo:IsNear( CargoCarrier:GetCoordinate(), NearRadius ) + end + + return nil + end + + --- Check if CargoGroup is in the ReportRadius for the Cargo to be Loaded. + -- @param #CARGO_GROUP self + -- @param Core.Point#Coordinate Coordinate + -- @return #boolean true if the CargoGroup is within the reporting radius. + function CARGO_GROUP:IsInRadius( Coordinate ) + self:F( { Coordinate } ) + + local Cargo = self.CargoSet:GetFirst() -- #CARGO + + if Cargo then + local Distance = 0 + if Cargo:IsLoaded() then + Distance = Coordinate:DistanceFromPointVec2( Cargo.CargoCarrier:GetPointVec2() ) + else + Distance = Coordinate:DistanceFromPointVec2( Cargo.CargoObject:GetPointVec2() ) + end + self:T( Distance ) + + if Distance <= self.ReportRadius then return true + else + return false end end return nil + + end + + --- Respawn the CargoGroup. + -- @param #CARGO_GROUP self + function CARGO_GROUP:Respawn() + + self:F({"Respawning"}) + + for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do + local Cargo = CargoData -- #CARGO + Cargo:Destroy() + Cargo:SetStartState( "UnLoaded" ) + end + + local CargoObject = self.CargoObject -- Wrapper.Group#GROUP + CargoObject:Destroy() + local Template = CargoObject:GetTemplate() + CargoObject:Respawn( Template ) + + self:SetDeployed( false ) + + local WeightGroup = 0 + + self:SetStartState( "UnLoaded" ) + + end + + --- Signal a flare at the position of the CargoGroup. + -- @param #CARGO_GROUP self + -- @param Utilities.Utils#FLARECOLOR FlareColor + function CARGO_GROUP:Flare( FlareColor ) + + local Cargo = self.CargoSet:GetFirst() -- #CARGO + if Cargo then + Cargo:Flare( FlareColor ) + end + end + + --- Smoke the CargoGroup. + -- @param #CARGO_GROUP self + -- @param Utilities.Utils#SMOKECOLOR SmokeColor The color of the smoke. + -- @param #number Radius The radius of randomization around the center of the first element of the CargoGroup. + function CARGO_GROUP:Smoke( SmokeColor, Radius ) + + local Cargo = self.CargoSet:GetFirst() -- #CARGO + + if Cargo then + Cargo:Smoke( SmokeColor, Radius ) + end + end + + --- Check if the first element of the CargoGroup is the given @{Zone}. + -- @param #CARGO self + -- @param Core.Zone#ZONE_BASE Zone + -- @return #boolean **true** if the first element of the CargoGroup is in the Zone + -- @return #boolean **false** if there is no element of the CargoGroup in the Zone. + function CARGO_GROUP:IsInZone( Zone ) + self:F( { Zone } ) + + local Cargo = self.CargoSet:GetFirst() -- #CARGO + + if Cargo then + return Cargo:IsInZone( Zone ) + end + + return nil + end end -- CARGO_GROUP diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 9c0fcad57..219705f85 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -531,16 +531,16 @@ function UNIT:GetFuel() return nil end ---- Returns the UNIT in a UNIT list of one element. +--- Returns a list of one @{Unit}. -- @param #UNIT self --- @return #list The UNITs wrappers. +-- @return #list A list of one @{Unit}. function UNIT:GetUnits() self:F2( { self.UnitName } ) local DCSUnit = self:GetDCSObject() + local Units = {} + if DCSUnit then - local DCSUnits = DCSUnit:getUnits() - local Units = {} Units[1] = UNIT:Find( DCSUnit ) self:T3( Units ) return Units From 93640b1d8eaaf27b733e168db7e4160f2aa3140d Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Thu, 29 Mar 2018 12:02:07 +0200 Subject: [PATCH 017/420] -- Further fixes for TASK_CARGO_TRANSPORT --- .../Moose/Wrapper/Controllable.lua | 20 ------------------- Moose Development/Moose/Wrapper/Group.lua | 19 ++++++++++++++++++ 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 01fe228e4..7da5fe917 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -202,26 +202,6 @@ end -- Get methods ---- Returns the UNITs wrappers of the DCS Units of the Controllable (default is a GROUP). --- @param #CONTROLLABLE self --- @return #list The UNITs wrappers. -function CONTROLLABLE:GetUnits() - self:F2( { self.ControllableName } ) - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - local DCSUnits = DCSControllable:getUnits() - local Units = {} - for Index, UnitData in pairs( DCSUnits ) do - Units[#Units+1] = UNIT:Find( UnitData ) - end - self:T3( Units ) - return Units - end - - return nil -end - --- Returns the health. Dead controllables have health <= 1.0. -- @param #CONTROLLABLE self diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 0606b14a0..66d9068a3 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -330,6 +330,25 @@ function GROUP:GetCountry() return nil end +--- Returns a list of @{Unit} objects of the @{Group}. +-- @param #GROUP self +-- @return #list The list of @{Unit} objects of the @{Group}. +function GROUP:GetUnits() + self:F2( { self.GroupName } ) + local DCSGroup = self:GetDCSObject() + + if DCSGroup then + local DCSUnits = DCSGroup:getUnits() + local Units = {} + for Index, UnitData in pairs( DCSUnits ) do + Units[#Units+1] = UNIT:Find( UnitData ) + end + self:T3( Units ) + return Units + end + + return nil +end --- Returns the UNIT wrapper class with number UnitNumber. -- If the underlying DCS Unit does not exist, the method will return nil. . -- @param #GROUP self From 35f18d0d1f70e7bdc46bc1b3e8facde4e712b7b2 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Thu, 29 Mar 2018 12:48:24 +0200 Subject: [PATCH 018/420] -- Fixing respawning of CargoGroups after dead event. --- Moose Development/Moose/Core/Cargo.lua | 656 +++++++++++++------------ 1 file changed, 339 insertions(+), 317 deletions(-) diff --git a/Moose Development/Moose/Core/Cargo.lua b/Moose Development/Moose/Core/Cargo.lua index 407e8bd87..613fbc7b1 100644 --- a/Moose Development/Moose/Core/Cargo.lua +++ b/Moose Development/Moose/Core/Cargo.lua @@ -226,317 +226,316 @@ do -- CARGO Containable = false, } ---- @type CARGO.CargoObjects --- @map < #string, Wrapper.Positionable#POSITIONABLE > The alive POSITIONABLE objects representing the the cargo. - - ---- CARGO Constructor. This class is an abstract class and should not be instantiated. --- @param #CARGO self --- @param #string Type --- @param #string Name --- @param #number Weight --- @param #number NearRadius (optional) --- @return #CARGO -function CARGO:New( Type, Name, Weight ) --R2.1 - - local self = BASE:Inherit( self, FSM:New() ) -- #CARGO - self:F( { Type, Name, Weight } ) + --- @type CARGO.CargoObjects + -- @map < #string, Wrapper.Positionable#POSITIONABLE > The alive POSITIONABLE objects representing the the cargo. - self:SetStartState( "UnLoaded" ) - self:AddTransition( { "UnLoaded", "Boarding" }, "Board", "Boarding" ) - self:AddTransition( "Boarding" , "Boarding", "Boarding" ) - self:AddTransition( "Boarding", "CancelBoarding", "UnLoaded" ) - self:AddTransition( "Boarding", "Load", "Loaded" ) - self:AddTransition( "UnLoaded", "Load", "Loaded" ) - self:AddTransition( "Loaded", "UnBoard", "UnBoarding" ) - self:AddTransition( "UnBoarding", "UnBoarding", "UnBoarding" ) - self:AddTransition( "UnBoarding", "UnLoad", "UnLoaded" ) - self:AddTransition( "Loaded", "UnLoad", "UnLoaded" ) - self:AddTransition( "*", "Damaged", "Damaged" ) - self:AddTransition( "*", "Destroyed", "Destroyed" ) - self:AddTransition( "*", "Respawn", "UnLoaded" ) - - - self.Type = Type - self.Name = Name - self.Weight = Weight - self.CargoObject = nil - self.CargoCarrier = nil -- Wrapper.Client#CLIENT - self.Representable = false - self.Slingloadable = false - self.Moveable = false - self.Containable = false - self:SetDeployed( false ) - - self.CargoScheduler = SCHEDULER:New() - - CARGOS[self.Name] = self - + --- CARGO Constructor. This class is an abstract class and should not be instantiated. + -- @param #CARGO self + -- @param #string Type + -- @param #string Name + -- @param #number Weight + -- @param #number NearRadius (optional) + -- @return #CARGO + function CARGO:New( Type, Name, Weight ) --R2.1 - return self -end - ---- Destroy the cargo. --- @param #CARGO self -function CARGO:Destroy() - if self.CargoObject then - self.CargoObject:Destroy() - end - self:Destroyed() -end - ---- Get the name of the Cargo. --- @param #CARGO self --- @return #string The name of the Cargo. -function CARGO:GetName() --R2.1 - return self.Name -end - ---- Get the object name of the Cargo. --- @param #CARGO self --- @return #string The object name of the Cargo. -function CARGO:GetObjectName() --R2.1 - if self:IsLoaded() then - return self.CargoCarrier:GetName() - else - return self.CargoObject:GetName() - end -end - ---- Get the type of the Cargo. --- @param #CARGO self --- @return #string The type of the Cargo. -function CARGO:GetType() - return self.Type -end - ---- Get the current coordinates of the Cargo. --- @param #CARGO self --- @return Core.Point#COORDINATE The coordinates of the Cargo. -function CARGO:GetCoordinate() - return self.CargoObject:GetCoordinate() -end - ---- Check if cargo is destroyed. --- @param #CARGO self --- @return #boolean true if destroyed -function CARGO:IsDestroyed() - return self:Is( "Destroyed" ) -end - - ---- Check if cargo is loaded. --- @param #CARGO self --- @return #boolean true if loaded -function CARGO:IsLoaded() - return self:Is( "Loaded" ) -end - ---- Check if cargo is unloaded. --- @param #CARGO self --- @return #boolean true if unloaded -function CARGO:IsUnLoaded() - return self:Is( "UnLoaded" ) -end - ---- Check if cargo is boarding. --- @param #CARGO self --- @return #boolean true if boarding -function CARGO:IsBoarding() - return self:Is( "Boarding" ) -end - ---- Check if cargo is alive. --- @param #CARGO self --- @return #boolean true if unloaded -function CARGO:IsAlive() - - if self:IsLoaded() then - return self.CargoCarrier:IsAlive() - else - return self.CargoObject:IsAlive() - end -end - ---- Set the cargo as deployed --- @param #CARGO self -function CARGO:SetDeployed( Deployed ) - self.Deployed = Deployed -end - ---- Is the cargo deployed --- @param #CARGO self --- @return #boolean -function CARGO:IsDeployed() - return self.Deployed -end - - - - ---- Template method to spawn a new representation of the CARGO in the simulator. --- @param #CARGO self --- @return #CARGO -function CARGO:Spawn( PointVec2 ) - self:F() - -end - ---- Signal a flare at the position of the CARGO. --- @param #CARGO self --- @param Utilities.Utils#FLARECOLOR FlareColor -function CARGO:Flare( FlareColor ) - if self:IsUnLoaded() then - trigger.action.signalFlare( self.CargoObject:GetVec3(), FlareColor , 0 ) - end -end - ---- Signal a white flare at the position of the CARGO. --- @param #CARGO self -function CARGO:FlareWhite() - self:Flare( trigger.flareColor.White ) -end - ---- Signal a yellow flare at the position of the CARGO. --- @param #CARGO self -function CARGO:FlareYellow() - self:Flare( trigger.flareColor.Yellow ) -end - ---- Signal a green flare at the position of the CARGO. --- @param #CARGO self -function CARGO:FlareGreen() - self:Flare( trigger.flareColor.Green ) -end - ---- Signal a red flare at the position of the CARGO. --- @param #CARGO self -function CARGO:FlareRed() - self:Flare( trigger.flareColor.Red ) -end - ---- Smoke the CARGO. --- @param #CARGO self --- @param Utilities.Utils#SMOKECOLOR SmokeColor The color of the smoke. --- @param #number Radius The radius of randomization around the center of the Cargo. -function CARGO:Smoke( SmokeColor, Radius ) - if self:IsUnLoaded() then - if Radius then - trigger.action.smoke( self.CargoObject:GetRandomVec3( Radius ), SmokeColor ) - else - trigger.action.smoke( self.CargoObject:GetVec3(), SmokeColor ) - end - end -end - ---- Smoke the CARGO Green. --- @param #CARGO self -function CARGO:SmokeGreen() - self:Smoke( trigger.smokeColor.Green, Range ) -end - ---- Smoke the CARGO Red. --- @param #CARGO self -function CARGO:SmokeRed() - self:Smoke( trigger.smokeColor.Red, Range ) -end - ---- Smoke the CARGO White. --- @param #CARGO self -function CARGO:SmokeWhite() - self:Smoke( trigger.smokeColor.White, Range ) -end - ---- Smoke the CARGO Orange. --- @param #CARGO self -function CARGO:SmokeOrange() - self:Smoke( trigger.smokeColor.Orange, Range ) -end - ---- Smoke the CARGO Blue. --- @param #CARGO self -function CARGO:SmokeBlue() - self:Smoke( trigger.smokeColor.Blue, Range ) -end - - - - - - ---- Check if Cargo is the given @{Zone}. --- @param #CARGO self --- @param Core.Zone#ZONE_BASE Zone --- @return #boolean **true** if cargo is in the Zone, **false** if cargo is not in the Zone. -function CARGO:IsInZone( Zone ) - self:F( { Zone } ) - - if self:IsLoaded() then - return Zone:IsPointVec2InZone( self.CargoCarrier:GetPointVec2() ) - else - self:F( { Size = self.CargoObject:GetSize(), Units = self.CargoObject:GetUnits() } ) - if self.CargoObject:GetSize() ~= 0 then - return Zone:IsPointVec2InZone( self.CargoObject:GetPointVec2() ) - else - return false - end - end - - return nil - -end - - ---- Check if CargoCarrier is near the Cargo to be Loaded. --- @param #CARGO self --- @param Core.Point#POINT_VEC2 PointVec2 --- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). --- @return #boolean -function CARGO:IsNear( PointVec2, NearRadius ) - self:F( { PointVec2 = PointVec2, NearRadius = NearRadius } ) - - if self.CargoObject:IsAlive() then - --local Distance = PointVec2:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) - self:F( { CargoObjectName = self.CargoObject:GetName() } ) - self:F( { CargoObjectVec2 = self.CargoObject:GetVec2() } ) - self:F( { PointVec2 = PointVec2:GetVec2() } ) - local Distance = PointVec2:Get2DDistance( self.CargoObject:GetPointVec2() ) - self:T( Distance ) + local self = BASE:Inherit( self, FSM:New() ) -- #CARGO + self:F( { Type, Name, Weight } ) - if Distance <= NearRadius then - return true + self:SetStartState( "UnLoaded" ) + self:AddTransition( { "UnLoaded", "Boarding" }, "Board", "Boarding" ) + self:AddTransition( "Boarding" , "Boarding", "Boarding" ) + self:AddTransition( "Boarding", "CancelBoarding", "UnLoaded" ) + self:AddTransition( "Boarding", "Load", "Loaded" ) + self:AddTransition( "UnLoaded", "Load", "Loaded" ) + self:AddTransition( "Loaded", "UnBoard", "UnBoarding" ) + self:AddTransition( "UnBoarding", "UnBoarding", "UnBoarding" ) + self:AddTransition( "UnBoarding", "UnLoad", "UnLoaded" ) + self:AddTransition( "Loaded", "UnLoad", "UnLoaded" ) + self:AddTransition( "*", "Damaged", "Damaged" ) + self:AddTransition( "*", "Destroyed", "Destroyed" ) + self:AddTransition( "*", "Respawn", "UnLoaded" ) + + + self.Type = Type + self.Name = Name + self.Weight = Weight + self.CargoObject = nil + self.CargoCarrier = nil -- Wrapper.Client#CLIENT + self.Representable = false + self.Slingloadable = false + self.Moveable = false + self.Containable = false + + self:SetDeployed( false ) + + self.CargoScheduler = SCHEDULER:New() + + CARGOS[self.Name] = self + + + return self + end + + --- Destroy the cargo. + -- @param #CARGO self + function CARGO:Destroy() + if self.CargoObject then + self.CargoObject:Destroy() + end + self:Destroyed() + end + + --- Get the name of the Cargo. + -- @param #CARGO self + -- @return #string The name of the Cargo. + function CARGO:GetName() --R2.1 + return self.Name + end + + --- Get the object name of the Cargo. + -- @param #CARGO self + -- @return #string The object name of the Cargo. + function CARGO:GetObjectName() --R2.1 + if self:IsLoaded() then + return self.CargoCarrier:GetName() + else + return self.CargoObject:GetName() + end + end + + --- Get the type of the Cargo. + -- @param #CARGO self + -- @return #string The type of the Cargo. + function CARGO:GetType() + return self.Type + end + + --- Get the current coordinates of the Cargo. + -- @param #CARGO self + -- @return Core.Point#COORDINATE The coordinates of the Cargo. + function CARGO:GetCoordinate() + return self.CargoObject:GetCoordinate() + end + + --- Check if cargo is destroyed. + -- @param #CARGO self + -- @return #boolean true if destroyed + function CARGO:IsDestroyed() + return self:Is( "Destroyed" ) + end + + + --- Check if cargo is loaded. + -- @param #CARGO self + -- @return #boolean true if loaded + function CARGO:IsLoaded() + return self:Is( "Loaded" ) + end + + --- Check if cargo is unloaded. + -- @param #CARGO self + -- @return #boolean true if unloaded + function CARGO:IsUnLoaded() + return self:Is( "UnLoaded" ) + end + + --- Check if cargo is boarding. + -- @param #CARGO self + -- @return #boolean true if boarding + function CARGO:IsBoarding() + return self:Is( "Boarding" ) + end + + --- Check if cargo is alive. + -- @param #CARGO self + -- @return #boolean true if unloaded + function CARGO:IsAlive() + + if self:IsLoaded() then + return self.CargoCarrier:IsAlive() + else + return self.CargoObject:IsAlive() + end + end + + --- Set the cargo as deployed + -- @param #CARGO self + function CARGO:SetDeployed( Deployed ) + self.Deployed = Deployed + end + + --- Is the cargo deployed + -- @param #CARGO self + -- @return #boolean + function CARGO:IsDeployed() + return self.Deployed + end + + + + + --- Template method to spawn a new representation of the CARGO in the simulator. + -- @param #CARGO self + -- @return #CARGO + function CARGO:Spawn( PointVec2 ) + self:F() + + end + + --- Signal a flare at the position of the CARGO. + -- @param #CARGO self + -- @param Utilities.Utils#FLARECOLOR FlareColor + function CARGO:Flare( FlareColor ) + if self:IsUnLoaded() then + trigger.action.signalFlare( self.CargoObject:GetVec3(), FlareColor , 0 ) end end - return false -end - ---- Get the current PointVec2 of the cargo. --- @param #CARGO self --- @return Core.Point#POINT_VEC2 -function CARGO:GetPointVec2() - return self.CargoObject:GetPointVec2() -end - ---- Get the current Coordinate of the cargo. --- @param #CARGO self --- @return Core.Point#COORDINATE -function CARGO:GetCoordinate() - return self.CargoObject:GetCoordinate() -end - ---- Set the weight of the cargo. --- @param #CARGO self --- @param #number Weight The weight in kg. --- @return #CARGO -function CARGO:SetWeight( Weight ) - self.Weight = Weight - return self -end - -end + --- Signal a white flare at the position of the CARGO. + -- @param #CARGO self + function CARGO:FlareWhite() + self:Flare( trigger.flareColor.White ) + end + + --- Signal a yellow flare at the position of the CARGO. + -- @param #CARGO self + function CARGO:FlareYellow() + self:Flare( trigger.flareColor.Yellow ) + end + + --- Signal a green flare at the position of the CARGO. + -- @param #CARGO self + function CARGO:FlareGreen() + self:Flare( trigger.flareColor.Green ) + end + + --- Signal a red flare at the position of the CARGO. + -- @param #CARGO self + function CARGO:FlareRed() + self:Flare( trigger.flareColor.Red ) + end + + --- Smoke the CARGO. + -- @param #CARGO self + -- @param Utilities.Utils#SMOKECOLOR SmokeColor The color of the smoke. + -- @param #number Radius The radius of randomization around the center of the Cargo. + function CARGO:Smoke( SmokeColor, Radius ) + if self:IsUnLoaded() then + if Radius then + trigger.action.smoke( self.CargoObject:GetRandomVec3( Radius ), SmokeColor ) + else + trigger.action.smoke( self.CargoObject:GetVec3(), SmokeColor ) + end + end + end + + --- Smoke the CARGO Green. + -- @param #CARGO self + function CARGO:SmokeGreen() + self:Smoke( trigger.smokeColor.Green, Range ) + end + + --- Smoke the CARGO Red. + -- @param #CARGO self + function CARGO:SmokeRed() + self:Smoke( trigger.smokeColor.Red, Range ) + end + + --- Smoke the CARGO White. + -- @param #CARGO self + function CARGO:SmokeWhite() + self:Smoke( trigger.smokeColor.White, Range ) + end + + --- Smoke the CARGO Orange. + -- @param #CARGO self + function CARGO:SmokeOrange() + self:Smoke( trigger.smokeColor.Orange, Range ) + end + + --- Smoke the CARGO Blue. + -- @param #CARGO self + function CARGO:SmokeBlue() + self:Smoke( trigger.smokeColor.Blue, Range ) + end + + + + + + + --- Check if Cargo is the given @{Zone}. + -- @param #CARGO self + -- @param Core.Zone#ZONE_BASE Zone + -- @return #boolean **true** if cargo is in the Zone, **false** if cargo is not in the Zone. + function CARGO:IsInZone( Zone ) + self:F( { Zone } ) + + if self:IsLoaded() then + return Zone:IsPointVec2InZone( self.CargoCarrier:GetPointVec2() ) + else + self:F( { Size = self.CargoObject:GetSize(), Units = self.CargoObject:GetUnits() } ) + if self.CargoObject:GetSize() ~= 0 then + return Zone:IsPointVec2InZone( self.CargoObject:GetPointVec2() ) + else + return false + end + end + + return nil + + end + + + --- Check if CargoCarrier is near the Cargo to be Loaded. + -- @param #CARGO self + -- @param Core.Point#POINT_VEC2 PointVec2 + -- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). + -- @return #boolean + function CARGO:IsNear( PointVec2, NearRadius ) + self:F( { PointVec2 = PointVec2, NearRadius = NearRadius } ) + + if self.CargoObject:IsAlive() then + --local Distance = PointVec2:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) + self:F( { CargoObjectName = self.CargoObject:GetName() } ) + self:F( { CargoObjectVec2 = self.CargoObject:GetVec2() } ) + self:F( { PointVec2 = PointVec2:GetVec2() } ) + local Distance = PointVec2:Get2DDistance( self.CargoObject:GetPointVec2() ) + self:T( Distance ) + + if Distance <= NearRadius then + return true + end + end + + return false + end + + --- Get the current PointVec2 of the cargo. + -- @param #CARGO self + -- @return Core.Point#POINT_VEC2 + function CARGO:GetPointVec2() + return self.CargoObject:GetPointVec2() + end + + --- Get the current Coordinate of the cargo. + -- @param #CARGO self + -- @return Core.Point#COORDINATE + function CARGO:GetCoordinate() + return self.CargoObject:GetCoordinate() + end + + --- Set the weight of the cargo. + -- @param #CARGO self + -- @param #number Weight The weight in kg. + -- @return #CARGO + function CARGO:SetWeight( Weight ) + self.Weight = Weight + return self + end +end -- CARGO do -- CARGO_REPRESENTABLE @@ -600,7 +599,7 @@ do -- CARGO_REPRESENTABLE end -- CARGO_REPRESENTABLE - do -- CARGO_REPORTABLE +do -- CARGO_REPORTABLE --- @type CARGO_REPORTABLE -- @extends #CARGO @@ -989,7 +988,6 @@ do -- CARGO_UNIT end -- CARGO_UNIT - do -- CARGO_CRATE --- Models the behaviour of cargo crates, which can be slingloaded and boarded on helicopters using the DCS menus. @@ -1127,16 +1125,17 @@ function CARGO_GROUP:New( CargoGroup, Type, Name, ReportRadius ) self:SetDeployed( false ) local WeightGroup = 0 - local GroupName = CargoGroup:GetName() + + self.GroupName = CargoGroup:GetName() + self.CargoTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplate( self.GroupName ) ) CargoGroup:Destroy() -- We iterate through the group template and for each unit in the template, we create a new group with one unit. - for UnitID, UnitTemplate in pairs( _DATABASE:GetGroupTemplate(GroupName).units ) do + for UnitID, UnitTemplate in pairs( self.CargoTemplate.units ) do - local GroupTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplate(GroupName) ) + local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate ) local GroupName = env.getValueDictByKey( GroupTemplate.name ) - self:E( GroupName ) -- We create a new group object with one unit... -- First we prepare the template... @@ -1557,7 +1556,7 @@ function CARGO_GROUP:OnEventCargoDead( EventData ) -- @param #CARGO_GROUP self function CARGO_GROUP:Respawn() - self:F({"Respawning"}) + self:F( { "Respawning" } ) for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do local Cargo = CargoData -- #CARGO @@ -1565,15 +1564,38 @@ function CARGO_GROUP:OnEventCargoDead( EventData ) Cargo:SetStartState( "UnLoaded" ) end - local CargoObject = self.CargoObject -- Wrapper.Group#GROUP - CargoObject:Destroy() - local Template = CargoObject:GetTemplate() - CargoObject:Respawn( Template ) + + -- We iterate through the group template and for each unit in the template, we create a new group with one unit. + for UnitID, UnitTemplate in pairs( self.CargoTemplate.units ) do + + local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate ) + local GroupName = env.getValueDictByKey( GroupTemplate.name ) + + -- We create a new group object with one unit... + -- First we prepare the template... + GroupTemplate.name = GroupName .. "#CARGO#" .. UnitID + GroupTemplate.groupId = nil + GroupTemplate.units = {} + GroupTemplate.units[1] = UnitTemplate + local UnitName = UnitTemplate.name .. "#CARGO" + GroupTemplate.units[1].name = UnitTemplate.name .. "#CARGO" + + -- Then we register the new group in the database + local CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID) + + -- Now we spawn the new group based on the template created. + _DATABASE:Spawn( GroupTemplate ) + + -- And we register the spawned unit as part of the CargoSet. + local Unit = UNIT:FindByName( UnitName ) + --local WeightUnit = Unit:GetDesc().massEmpty + --WeightGroup = WeightGroup + WeightUnit + local CargoUnit = CARGO_UNIT:New( Unit, Type, UnitName, 10 ) + self.CargoSet:Add( UnitName, CargoUnit ) + end + self:SetDeployed( false ) - - local WeightGroup = 0 - self:SetStartState( "UnLoaded" ) end From 9688c606f07c2d8306ea2fea136bcc2983dfb955 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Thu, 29 Mar 2018 17:55:58 +0200 Subject: [PATCH 019/420] New Cargo files --- Moose Development/Moose/Cargo/Cargo.lua | 870 ++++++++ Moose Development/Moose/Cargo/CargoCrate.lua | 120 ++ Moose Development/Moose/Cargo/CargoGroup.lua | 577 ++++++ Moose Development/Moose/Cargo/CargoUnit.lua | 358 ++++ Moose Development/Moose/Core/Cargo.lua | 1860 ------------------ Moose Setup/Moose.files | 6 +- 6 files changed, 1930 insertions(+), 1861 deletions(-) create mode 100644 Moose Development/Moose/Cargo/Cargo.lua create mode 100644 Moose Development/Moose/Cargo/CargoCrate.lua create mode 100644 Moose Development/Moose/Cargo/CargoGroup.lua create mode 100644 Moose Development/Moose/Cargo/CargoUnit.lua delete mode 100644 Moose Development/Moose/Core/Cargo.lua diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua new file mode 100644 index 000000000..e6b3bde6e --- /dev/null +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -0,0 +1,870 @@ +--- **Core** -- Management of CARGO logistics, that can be transported from and to transportation carriers. +-- +-- === +-- +-- ![Banner Image](..\Presentations\CARGO\Dia1.JPG) +-- +-- === +-- +-- Cargo can be of various forms, always are composed out of ONE object ( one unit or one static or one slingload crate ): +-- +-- * CARGO_UNIT, represented by a @{Unit} in a singleton @{Group}: Cargo can be represented by a Unit in a Group. a CARGO_UNIT is representable... +-- * CARGO_GROUP, represented by a @{Group}. A CARGO_GROUP is reportable... +-- +-- This module is still under construction, but is described above works already, and will keep working ... +-- +-- === +-- +-- # Demo Missions +-- +-- ### [CARGO Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CGO%20-%20Cargo) +-- +-- ### [CARGO Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CGO%20-%20Cargo) +-- +-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- +-- === +-- +-- # YouTube Channel +-- +-- ### [CARGO YouTube Channel](https://www.youtube.com/watch?v=tM00lTlkpYs&list=PL7ZUrU4zZUl2zUTuKrLW5RsO9zLMqUtbf) +-- +-- === +-- +-- ### Author: **FlightControl** +-- ### Contributions: +-- +-- === +-- +-- @module Cargo + +-- Events + +-- Board + +--- Boards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo to the Carrier. +-- The cargo must be in the **UnLoaded** state. +-- @function [parent=#CARGO] Board +-- @param #CARGO self +-- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. +-- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). + +--- Boards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo to the Carrier. +-- The cargo must be in the **UnLoaded** state. +-- @function [parent=#CARGO] __Board +-- @param #CARGO self +-- @param #number DelaySeconds The amount of seconds to delay the action. +-- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. +-- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). + + +-- UnBoard + +--- UnBoards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo from the Carrier. +-- The cargo must be in the **Loaded** state. +-- @function [parent=#CARGO] UnBoard +-- @param #CARGO self +-- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location. + +--- UnBoards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo from the Carrier. +-- The cargo must be in the **Loaded** state. +-- @function [parent=#CARGO] __UnBoard +-- @param #CARGO self +-- @param #number DelaySeconds The amount of seconds to delay the action. +-- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location. + + +-- Load + +--- Loads the cargo to a Carrier. The event will load the cargo into the Carrier regardless of its position. There will be no movement simulated of the cargo loading. +-- The cargo must be in the **UnLoaded** state. +-- @function [parent=#CARGO] Load +-- @param #CARGO self +-- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. + +--- Loads the cargo to a Carrier. The event will load the cargo into the Carrier regardless of its position. There will be no movement simulated of the cargo loading. +-- The cargo must be in the **UnLoaded** state. +-- @function [parent=#CARGO] __Load +-- @param #CARGO self +-- @param #number DelaySeconds The amount of seconds to delay the action. +-- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. + + +-- UnLoad + +--- UnLoads the cargo to a Carrier. The event will unload the cargo from the Carrier. There will be no movement simulated of the cargo loading. +-- The cargo must be in the **Loaded** state. +-- @function [parent=#CARGO] UnLoad +-- @param #CARGO self +-- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location. + +--- UnLoads the cargo to a Carrier. The event will unload the cargo from the Carrier. There will be no movement simulated of the cargo loading. +-- The cargo must be in the **Loaded** state. +-- @function [parent=#CARGO] __UnLoad +-- @param #CARGO self +-- @param #number DelaySeconds The amount of seconds to delay the action. +-- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location. + +-- State Transition Functions + +-- UnLoaded + +--- @function [parent=#CARGO] OnLeaveUnLoaded +-- @param #CARGO self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable +-- @return #boolean + +--- @function [parent=#CARGO] OnEnterUnLoaded +-- @param #CARGO self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable + +-- Loaded + +--- @function [parent=#CARGO] OnLeaveLoaded +-- @param #CARGO self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable +-- @return #boolean + +--- @function [parent=#CARGO] OnEnterLoaded +-- @param #CARGO self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable + +-- Boarding + +--- @function [parent=#CARGO] OnLeaveBoarding +-- @param #CARGO self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable +-- @return #boolean + +--- @function [parent=#CARGO] OnEnterBoarding +-- @param #CARGO self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable +-- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). + +-- UnBoarding + +--- @function [parent=#CARGO] OnLeaveUnBoarding +-- @param #CARGO self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable +-- @return #boolean + +--- @function [parent=#CARGO] OnEnterUnBoarding +-- @param #CARGO self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable + + +-- TODO: Find all Carrier objects and make the type of the Carriers Wrapper.Unit#UNIT in the documentation. + +CARGOS = {} + +do -- CARGO + + --- @type CARGO + -- @extends Core.Fsm#FSM_PROCESS + -- @field #string Type A string defining the type of the cargo. eg. Engineers, Equipment, Screwdrivers. + -- @field #string Name A string defining the name of the cargo. The name is the unique identifier of the cargo. + -- @field #number Weight A number defining the weight of the cargo. The weight is expressed in kg. + -- @field #number NearRadius (optional) A number defining the radius in meters when the cargo is near to a Carrier, so that it can be loaded. + -- @field Wrapper.Unit#UNIT CargoObject The alive DCS object representing the cargo. This value can be nil, meaning, that the cargo is not represented anywhere... + -- @field Wrapper.Client#CLIENT CargoCarrier The alive DCS object carrying the cargo. This value can be nil, meaning, that the cargo is not contained anywhere... + -- @field #boolean Slingloadable This flag defines if the cargo can be slingloaded. + -- @field #boolean Moveable This flag defines if the cargo is moveable. + -- @field #boolean Representable This flag defines if the cargo can be represented by a DCS Unit. + -- @field #boolean Containable This flag defines if the cargo can be contained within a DCS Unit. + + --- # (R2.1) CARGO class, extends @{Fsm#FSM_PROCESS} + -- + -- The CARGO class defines the core functions that defines a cargo object within MOOSE. + -- A cargo is a logical object defined that is available for transport, and has a life status within a simulation. + -- + -- The CARGO is a state machine: it manages the different events and states of the cargo. + -- All derived classes from CARGO follow the same state machine, expose the same cargo event functions, and provide the same cargo states. + -- + -- ## CARGO Events: + -- + -- * @{#CARGO.Board}( ToCarrier ): Boards the cargo to a carrier. + -- * @{#CARGO.Load}( ToCarrier ): Loads the cargo into a carrier, regardless of its position. + -- * @{#CARGO.UnBoard}( ToPointVec2 ): UnBoard the cargo from a carrier. This will trigger a movement of the cargo to the option ToPointVec2. + -- * @{#CARGO.UnLoad}( ToPointVec2 ): UnLoads the cargo from a carrier. + -- * @{#CARGO.Dead}( Controllable ): The cargo is dead. The cargo process will be ended. + -- + -- ## CARGO States: + -- + -- * **UnLoaded**: The cargo is unloaded from a carrier. + -- * **Boarding**: The cargo is currently boarding (= running) into a carrier. + -- * **Loaded**: The cargo is loaded into a carrier. + -- * **UnBoarding**: The cargo is currently unboarding (=running) from a carrier. + -- * **Dead**: The cargo is dead ... + -- * **End**: The process has come to an end. + -- + -- ## CARGO state transition methods: + -- + -- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. + -- There are 2 moments when state transition methods will be called by the state machine: + -- + -- * **Leaving** the state. + -- The state transition method needs to start with the name **OnLeave + the name of the state**. + -- If the state transition method returns false, then the processing of the state transition will not be done! + -- If you want to change the behaviour of the AIControllable at this event, return false, + -- but then you'll need to specify your own logic using the AIControllable! + -- + -- * **Entering** the state. + -- The state transition method needs to start with the name **OnEnter + the name of the state**. + -- These state transition methods need to provide a return value, which is specified at the function description. + -- + -- @field #CARGO + CARGO = { + ClassName = "CARGO", + Type = nil, + Name = nil, + Weight = nil, + CargoObject = nil, + CargoCarrier = nil, + Representable = false, + Slingloadable = false, + Moveable = false, + Containable = false, + } + + --- @type CARGO.CargoObjects + -- @map < #string, Wrapper.Positionable#POSITIONABLE > The alive POSITIONABLE objects representing the the cargo. + + + --- CARGO Constructor. This class is an abstract class and should not be instantiated. + -- @param #CARGO self + -- @param #string Type + -- @param #string Name + -- @param #number Weight + -- @param #number NearRadius (optional) + -- @return #CARGO + function CARGO:New( Type, Name, Weight ) --R2.1 + + local self = BASE:Inherit( self, FSM:New() ) -- #CARGO + self:F( { Type, Name, Weight } ) + + self:SetStartState( "UnLoaded" ) + self:AddTransition( { "UnLoaded", "Boarding" }, "Board", "Boarding" ) + self:AddTransition( "Boarding" , "Boarding", "Boarding" ) + self:AddTransition( "Boarding", "CancelBoarding", "UnLoaded" ) + self:AddTransition( "Boarding", "Load", "Loaded" ) + self:AddTransition( "UnLoaded", "Load", "Loaded" ) + self:AddTransition( "Loaded", "UnBoard", "UnBoarding" ) + self:AddTransition( "UnBoarding", "UnBoarding", "UnBoarding" ) + self:AddTransition( "UnBoarding", "UnLoad", "UnLoaded" ) + self:AddTransition( "Loaded", "UnLoad", "UnLoaded" ) + self:AddTransition( "*", "Damaged", "Damaged" ) + self:AddTransition( "*", "Destroyed", "Destroyed" ) + self:AddTransition( "*", "Respawn", "UnLoaded" ) + + + self.Type = Type + self.Name = Name + self.Weight = Weight + self.CargoObject = nil + self.CargoCarrier = nil -- Wrapper.Client#CLIENT + self.Representable = false + self.Slingloadable = false + self.Moveable = false + self.Containable = false + + self:SetDeployed( false ) + + self.CargoScheduler = SCHEDULER:New() + + CARGOS[self.Name] = self + + + return self + end + + --- Destroy the cargo. + -- @param #CARGO self + function CARGO:Destroy() + if self.CargoObject then + self.CargoObject:Destroy() + end + self:Destroyed() + end + + --- Get the name of the Cargo. + -- @param #CARGO self + -- @return #string The name of the Cargo. + function CARGO:GetName() --R2.1 + return self.Name + end + + --- Get the object name of the Cargo. + -- @param #CARGO self + -- @return #string The object name of the Cargo. + function CARGO:GetObjectName() --R2.1 + if self:IsLoaded() then + return self.CargoCarrier:GetName() + else + return self.CargoObject:GetName() + end + end + + --- Get the type of the Cargo. + -- @param #CARGO self + -- @return #string The type of the Cargo. + function CARGO:GetType() + return self.Type + end + + --- Get the current coordinates of the Cargo. + -- @param #CARGO self + -- @return Core.Point#COORDINATE The coordinates of the Cargo. + function CARGO:GetCoordinate() + return self.CargoObject:GetCoordinate() + end + + --- Check if cargo is destroyed. + -- @param #CARGO self + -- @return #boolean true if destroyed + function CARGO:IsDestroyed() + return self:Is( "Destroyed" ) + end + + + --- Check if cargo is loaded. + -- @param #CARGO self + -- @return #boolean true if loaded + function CARGO:IsLoaded() + return self:Is( "Loaded" ) + end + + --- Check if cargo is unloaded. + -- @param #CARGO self + -- @return #boolean true if unloaded + function CARGO:IsUnLoaded() + return self:Is( "UnLoaded" ) + end + + --- Check if cargo is boarding. + -- @param #CARGO self + -- @return #boolean true if boarding + function CARGO:IsBoarding() + return self:Is( "Boarding" ) + end + + --- Check if cargo is alive. + -- @param #CARGO self + -- @return #boolean true if unloaded + function CARGO:IsAlive() + + if self:IsLoaded() then + return self.CargoCarrier:IsAlive() + else + return self.CargoObject:IsAlive() + end + end + + --- Set the cargo as deployed + -- @param #CARGO self + function CARGO:SetDeployed( Deployed ) + self.Deployed = Deployed + end + + --- Is the cargo deployed + -- @param #CARGO self + -- @return #boolean + function CARGO:IsDeployed() + return self.Deployed + end + + + + + --- Template method to spawn a new representation of the CARGO in the simulator. + -- @param #CARGO self + -- @return #CARGO + function CARGO:Spawn( PointVec2 ) + self:F() + + end + + --- Signal a flare at the position of the CARGO. + -- @param #CARGO self + -- @param Utilities.Utils#FLARECOLOR FlareColor + function CARGO:Flare( FlareColor ) + if self:IsUnLoaded() then + trigger.action.signalFlare( self.CargoObject:GetVec3(), FlareColor , 0 ) + end + end + + --- Signal a white flare at the position of the CARGO. + -- @param #CARGO self + function CARGO:FlareWhite() + self:Flare( trigger.flareColor.White ) + end + + --- Signal a yellow flare at the position of the CARGO. + -- @param #CARGO self + function CARGO:FlareYellow() + self:Flare( trigger.flareColor.Yellow ) + end + + --- Signal a green flare at the position of the CARGO. + -- @param #CARGO self + function CARGO:FlareGreen() + self:Flare( trigger.flareColor.Green ) + end + + --- Signal a red flare at the position of the CARGO. + -- @param #CARGO self + function CARGO:FlareRed() + self:Flare( trigger.flareColor.Red ) + end + + --- Smoke the CARGO. + -- @param #CARGO self + -- @param Utilities.Utils#SMOKECOLOR SmokeColor The color of the smoke. + -- @param #number Radius The radius of randomization around the center of the Cargo. + function CARGO:Smoke( SmokeColor, Radius ) + if self:IsUnLoaded() then + if Radius then + trigger.action.smoke( self.CargoObject:GetRandomVec3( Radius ), SmokeColor ) + else + trigger.action.smoke( self.CargoObject:GetVec3(), SmokeColor ) + end + end + end + + --- Smoke the CARGO Green. + -- @param #CARGO self + function CARGO:SmokeGreen() + self:Smoke( trigger.smokeColor.Green, Range ) + end + + --- Smoke the CARGO Red. + -- @param #CARGO self + function CARGO:SmokeRed() + self:Smoke( trigger.smokeColor.Red, Range ) + end + + --- Smoke the CARGO White. + -- @param #CARGO self + function CARGO:SmokeWhite() + self:Smoke( trigger.smokeColor.White, Range ) + end + + --- Smoke the CARGO Orange. + -- @param #CARGO self + function CARGO:SmokeOrange() + self:Smoke( trigger.smokeColor.Orange, Range ) + end + + --- Smoke the CARGO Blue. + -- @param #CARGO self + function CARGO:SmokeBlue() + self:Smoke( trigger.smokeColor.Blue, Range ) + end + + + + + + + --- Check if Cargo is the given @{Zone}. + -- @param #CARGO self + -- @param Core.Zone#ZONE_BASE Zone + -- @return #boolean **true** if cargo is in the Zone, **false** if cargo is not in the Zone. + function CARGO:IsInZone( Zone ) + self:F( { Zone } ) + + if self:IsLoaded() then + return Zone:IsPointVec2InZone( self.CargoCarrier:GetPointVec2() ) + else + self:F( { Size = self.CargoObject:GetSize(), Units = self.CargoObject:GetUnits() } ) + if self.CargoObject:GetSize() ~= 0 then + return Zone:IsPointVec2InZone( self.CargoObject:GetPointVec2() ) + else + return false + end + end + + return nil + + end + + + --- Check if CargoCarrier is near the Cargo to be Loaded. + -- @param #CARGO self + -- @param Core.Point#POINT_VEC2 PointVec2 + -- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). + -- @return #boolean + function CARGO:IsNear( PointVec2, NearRadius ) + self:F( { PointVec2 = PointVec2, NearRadius = NearRadius } ) + + if self.CargoObject:IsAlive() then + --local Distance = PointVec2:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) + self:F( { CargoObjectName = self.CargoObject:GetName() } ) + self:F( { CargoObjectVec2 = self.CargoObject:GetVec2() } ) + self:F( { PointVec2 = PointVec2:GetVec2() } ) + local Distance = PointVec2:Get2DDistance( self.CargoObject:GetPointVec2() ) + self:T( Distance ) + + if Distance <= NearRadius then + return true + end + end + + return false + end + + --- Get the current PointVec2 of the cargo. + -- @param #CARGO self + -- @return Core.Point#POINT_VEC2 + function CARGO:GetPointVec2() + return self.CargoObject:GetPointVec2() + end + + --- Get the current Coordinate of the cargo. + -- @param #CARGO self + -- @return Core.Point#COORDINATE + function CARGO:GetCoordinate() + return self.CargoObject:GetCoordinate() + end + + --- Set the weight of the cargo. + -- @param #CARGO self + -- @param #number Weight The weight in kg. + -- @return #CARGO + function CARGO:SetWeight( Weight ) + self.Weight = Weight + return self + end + +end -- CARGO + +do -- CARGO_REPRESENTABLE + + --- @type CARGO_REPRESENTABLE + -- @extends #CARGO + -- @field test + + --- Models CARGO that is representable by a Unit. + -- @field #CARGO_REPRESENTABLE CARGO_REPRESENTABLE + CARGO_REPRESENTABLE = { + ClassName = "CARGO_REPRESENTABLE" + } + + --- CARGO_REPRESENTABLE Constructor. + -- @param #CARGO_REPRESENTABLE self + -- @param #string Type + -- @param #string Name + -- @param #number Weight + -- @param #number ReportRadius (optional) + -- @param #number NearRadius (optional) + -- @return #CARGO_REPRESENTABLE + function CARGO_REPRESENTABLE:New( CargoObject, Type, Name, Weight, ReportRadius, NearRadius ) + local self = BASE:Inherit( self, CARGO:New( Type, Name, Weight, ReportRadius, NearRadius ) ) -- #CARGO_REPRESENTABLE + self:F( { Type, Name, Weight, ReportRadius, NearRadius } ) + + return self + end + + --- CARGO_REPRESENTABLE Destructor. + -- @param #CARGO_REPRESENTABLE self + -- @return #CARGO_REPRESENTABLE + function CARGO_REPRESENTABLE:Destroy() + + -- Cargo objects are deleted from the _DATABASE and SET_CARGO objects. + self:F( { CargoName = self:GetName() } ) + _EVENTDISPATCHER:CreateEventDeleteCargo( self ) + + return self + end + + --- Route a cargo unit to a PointVec2. + -- @param #CARGO_REPRESENTABLE self + -- @param Core.Point#POINT_VEC2 ToPointVec2 + -- @param #number Speed + -- @return #CARGO_REPRESENTABLE + function CARGO_REPRESENTABLE:RouteTo( ToPointVec2, Speed ) + self:F2( ToPointVec2 ) + + local Points = {} + + local PointStartVec2 = self.CargoObject:GetPointVec2() + + Points[#Points+1] = PointStartVec2:WaypointGround( Speed ) + Points[#Points+1] = ToPointVec2:WaypointGround( Speed ) + + local TaskRoute = self.CargoObject:TaskRoute( Points ) + self.CargoObject:SetTask( TaskRoute, 2 ) + return self + end + + +end -- CARGO_REPRESENTABLE + +do -- CARGO_REPORTABLE + + --- @type CARGO_REPORTABLE + -- @extends #CARGO + CARGO_REPORTABLE = { + ClassName = "CARGO_REPORTABLE" + } + + --- CARGO_REPORTABLE Constructor. + -- @param #CARGO_REPORTABLE self + -- @param #string Type + -- @param #string Name + -- @param #number Weight + -- @param #number ReportRadius (optional) + -- @param #number NearRadius (optional) + -- @return #CARGO_REPORTABLE + function CARGO_REPORTABLE:New( Type, Name, Weight, ReportRadius ) + local self = BASE:Inherit( self, CARGO:New( Type, Name, Weight ) ) -- #CARGO_REPORTABLE + self:F( { Type, Name, Weight, ReportRadius } ) + + self.ReportRadius = ReportRadius or 1000 + + return self + end + + --- Send a CC message to a @{Group}. + -- @param #CARGO_REPORTABLE self + -- @param #string Message + -- @param Wrapper.Group#GROUP TaskGroup + -- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown. + function CARGO_REPORTABLE:MessageToGroup( Message, TaskGroup, Name ) + + local Prefix = Name and "@ " .. Name .. ": " or "@ " .. TaskGroup:GetCallsign() .. ": " + Message = Prefix .. Message + MESSAGE:New( Message, 20, "Cargo: " .. self:GetName() ):ToGroup( TaskGroup ) + + end + + --- Get the Report radius, which is the radius when the Cargo is reporting itself. + -- @param #CARGO_REPORTABLE self + -- @return #number The range till Cargo reports itself. + function CARGO_REPORTABLE:GetBoardingRange() + return self.ReportRadius + end + + + +end + + + + + + + +do -- CARGO_PACKAGE + + --- @type CARGO_PACKAGE + -- @extends #CARGO_REPRESENTABLE + CARGO_PACKAGE = { + ClassName = "CARGO_PACKAGE" + } + +--- CARGO_PACKAGE Constructor. +-- @param #CARGO_PACKAGE self +-- @param Wrapper.Unit#UNIT CargoCarrier The UNIT carrying the package. +-- @param #string Type +-- @param #string Name +-- @param #number Weight +-- @param #number ReportRadius (optional) +-- @param #number NearRadius (optional) +-- @return #CARGO_PACKAGE +function CARGO_PACKAGE:New( CargoCarrier, Type, Name, Weight, ReportRadius, NearRadius ) + local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoCarrier, Type, Name, Weight, ReportRadius, NearRadius ) ) -- #CARGO_PACKAGE + self:F( { Type, Name, Weight, ReportRadius, NearRadius } ) + + self:T( CargoCarrier ) + self.CargoCarrier = CargoCarrier + + return self +end + +--- Board Event. +-- @param #CARGO_PACKAGE self +-- @param #string Event +-- @param #string From +-- @param #string To +-- @param Wrapper.Unit#UNIT CargoCarrier +-- @param #number Speed +-- @param #number BoardDistance +-- @param #number Angle +function CARGO_PACKAGE:onafterOnBoard( From, Event, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) + self:F() + + self.CargoInAir = self.CargoCarrier:InAir() + + self:T( self.CargoInAir ) + + -- Only move the CargoCarrier to the New CargoCarrier when the New CargoCarrier is not in the air. + if not self.CargoInAir then + + local Points = {} + + local StartPointVec2 = self.CargoCarrier:GetPointVec2() + local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + self:T( { CargoCarrierHeading, CargoDeployHeading } ) + local CargoDeployPointVec2 = CargoCarrier:GetPointVec2():Translate( BoardDistance, CargoDeployHeading ) + + Points[#Points+1] = StartPointVec2:WaypointGround( Speed ) + Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed ) + + local TaskRoute = self.CargoCarrier:TaskRoute( Points ) + self.CargoCarrier:SetTask( TaskRoute, 1 ) + end + + self:Boarded( CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) + +end + +--- Check if CargoCarrier is near the Cargo to be Loaded. +-- @param #CARGO_PACKAGE self +-- @param Wrapper.Unit#UNIT CargoCarrier +-- @return #boolean +function CARGO_PACKAGE:IsNear( CargoCarrier ) + self:F() + + local CargoCarrierPoint = CargoCarrier:GetPointVec2() + + local Distance = CargoCarrierPoint:DistanceFromPointVec2( self.CargoCarrier:GetPointVec2() ) + self:T( Distance ) + + if Distance <= self.NearRadius then + return true + else + return false + end +end + +--- Boarded Event. +-- @param #CARGO_PACKAGE self +-- @param #string Event +-- @param #string From +-- @param #string To +-- @param Wrapper.Unit#UNIT CargoCarrier +function CARGO_PACKAGE:onafterOnBoarded( From, Event, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) + self:F() + + if self:IsNear( CargoCarrier ) then + self:__Load( 1, CargoCarrier, Speed, LoadDistance, Angle ) + else + self:__Boarded( 1, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) + end +end + +--- UnBoard Event. +-- @param #CARGO_PACKAGE self +-- @param #string Event +-- @param #string From +-- @param #string To +-- @param #number Speed +-- @param #number UnLoadDistance +-- @param #number UnBoardDistance +-- @param #number Radius +-- @param #number Angle +function CARGO_PACKAGE:onafterUnBoard( From, Event, To, CargoCarrier, Speed, UnLoadDistance, UnBoardDistance, Radius, Angle ) + self:F() + + self.CargoInAir = self.CargoCarrier:InAir() + + self:T( self.CargoInAir ) + + -- Only unboard the cargo when the carrier is not in the air. + -- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea). + if not self.CargoInAir then + + self:_Next( self.FsmP.UnLoad, UnLoadDistance, Angle ) + + local Points = {} + + local StartPointVec2 = CargoCarrier:GetPointVec2() + local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + self:T( { CargoCarrierHeading, CargoDeployHeading } ) + local CargoDeployPointVec2 = StartPointVec2:Translate( UnBoardDistance, CargoDeployHeading ) + + Points[#Points+1] = StartPointVec2:WaypointGround( Speed ) + Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed ) + + local TaskRoute = CargoCarrier:TaskRoute( Points ) + CargoCarrier:SetTask( TaskRoute, 1 ) + end + + self:__UnBoarded( 1 , CargoCarrier, Speed ) + +end + +--- UnBoarded Event. +-- @param #CARGO_PACKAGE self +-- @param #string Event +-- @param #string From +-- @param #string To +-- @param Wrapper.Unit#UNIT CargoCarrier +function CARGO_PACKAGE:onafterUnBoarded( From, Event, To, CargoCarrier, Speed ) + self:F() + + if self:IsNear( CargoCarrier ) then + self:__UnLoad( 1, CargoCarrier, Speed ) + else + self:__UnBoarded( 1, CargoCarrier, Speed ) + end +end + +--- Load Event. +-- @param #CARGO_PACKAGE self +-- @param #string Event +-- @param #string From +-- @param #string To +-- @param Wrapper.Unit#UNIT CargoCarrier +-- @param #number Speed +-- @param #number LoadDistance +-- @param #number Angle +function CARGO_PACKAGE:onafterLoad( From, Event, To, CargoCarrier, Speed, LoadDistance, Angle ) + self:F() + + self.CargoCarrier = CargoCarrier + + local StartPointVec2 = self.CargoCarrier:GetPointVec2() + local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + local CargoDeployPointVec2 = StartPointVec2:Translate( LoadDistance, CargoDeployHeading ) + + local Points = {} + Points[#Points+1] = StartPointVec2:WaypointGround( Speed ) + Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed ) + + local TaskRoute = self.CargoCarrier:TaskRoute( Points ) + self.CargoCarrier:SetTask( TaskRoute, 1 ) + +end + +--- UnLoad Event. +-- @param #CARGO_PACKAGE self +-- @param #string Event +-- @param #string From +-- @param #string To +-- @param #number Distance +-- @param #number Angle +function CARGO_PACKAGE:onafterUnLoad( From, Event, To, CargoCarrier, Speed, Distance, Angle ) + self:F() + + local StartPointVec2 = self.CargoCarrier:GetPointVec2() + local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + local CargoDeployPointVec2 = StartPointVec2:Translate( Distance, CargoDeployHeading ) + + self.CargoCarrier = CargoCarrier + + local Points = {} + Points[#Points+1] = StartPointVec2:WaypointGround( Speed ) + Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed ) + + local TaskRoute = self.CargoCarrier:TaskRoute( Points ) + self.CargoCarrier:SetTask( TaskRoute, 1 ) + +end + + +end diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua new file mode 100644 index 000000000..3f943d1ed --- /dev/null +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -0,0 +1,120 @@ +--- **Cargo** -- Management of single cargo crates, which are based on a @{Static} object. +-- +-- === +-- +-- ![Banner Image](..\Presentations\CARGO\Dia1.JPG) +-- +-- === +-- +-- ### [Demo Missions]() +-- +-- ### [YouTube Playlist]() +-- +-- === +-- +-- ### Author: **FlightControl** +-- ### Contributions: +-- +-- === +-- +-- @module CargoCrate + +do -- CARGO_CRATE + + --- Models the behaviour of cargo crates, which can be slingloaded and boarded on helicopters. + -- @type CARGO_CRATE + -- @extends #CARGO_REPRESENTABLE + + --- # CARGO\_CRATE class, extends @{#CARGO_REPRESENTABLE} + -- + -- The CARGO\_CRATE class defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. + -- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO\_CRATE objects to and from carriers. + -- + -- === + -- + -- @field #CARGO_CRATE + CARGO_CRATE = { + ClassName = "CARGO_CRATE" + } + + --- CARGO_CRATE Constructor. + -- @param #CARGO_CRATE self + -- @param Wrapper.Static#STATIC CargoStatic + -- @param #string Type + -- @param #string Name + -- @param #number Weight + -- @param #number ReportRadius (optional) + -- @param #number NearRadius (optional) + -- @return #CARGO_CRATE + function CARGO_CRATE:New( CargoStatic, Type, Name, NearRadius ) + local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoStatic, Type, Name, nil, NearRadius ) ) -- #CARGO_CRATE + self:F( { Type, Name, NearRadius } ) + + self.CargoObject = CargoStatic + + self:T( self.ClassName ) + + self:SetEventPriority( 5 ) + + return self + end + + + + --- Enter UnLoaded State. + -- @param #CARGO_CRATE self + -- @param #string Event + -- @param #string From + -- @param #string To + -- @param Core.Point#POINT_VEC2 + function CARGO_CRATE:onenterUnLoaded( From, Event, To, ToPointVec2 ) + self:F( { ToPointVec2, From, Event, To } ) + + local Angle = 180 + local Speed = 10 + local Distance = 10 + + if From == "Loaded" then + local StartCoordinate = self.CargoCarrier:GetCoordinate() + local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + local CargoDeployCoord = StartCoordinate:Translate( Distance, CargoDeployHeading ) + + ToPointVec2 = ToPointVec2 or COORDINATE:NewFromVec2( { x= CargoDeployCoord.x, y = CargoDeployCoord.z } ) + + -- Respawn the group... + if self.CargoObject then + self.CargoObject:ReSpawn( ToPointVec2, 0 ) + self.CargoCarrier = nil + end + + end + + if self.OnUnLoadedCallBack then + self.OnUnLoadedCallBack( self, unpack( self.OnUnLoadedParameters ) ) + self.OnUnLoadedCallBack = nil + end + + end + + + --- Loaded State. + -- @param #CARGO_CRATE self + -- @param #string Event + -- @param #string From + -- @param #string To + -- @param Wrapper.Unit#UNIT CargoCarrier + function CARGO_CRATE:onenterLoaded( From, Event, To, CargoCarrier ) + self:F( { From, Event, To, CargoCarrier } ) + + self.CargoCarrier = CargoCarrier + + -- Only destroy the CargoObject is if there is a CargoObject (packages don't have CargoObjects). + if self.CargoObject then + self:T("Destroying") + self.CargoObject:Destroy() + end + end + +end + diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua new file mode 100644 index 000000000..2a5f8fee5 --- /dev/null +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -0,0 +1,577 @@ +--- **Cargo** -- Management of grouped cargo logistics, which are based on a @{Group} object. +-- +-- === +-- +-- ![Banner Image](..\Presentations\CARGO\Dia1.JPG) +-- +-- === +-- +-- ### [Demo Missions]() +-- +-- ### [YouTube Playlist]() +-- +-- === +-- +-- ### Author: **FlightControl** +-- ### Contributions: +-- +-- === +-- +-- @module CargoGroup + + +do -- CARGO_GROUP + + --- @type CARGO_GROUP + -- @extends #CARGO_REPORTABLE + -- @field Core.Set#SET_CARGO CargoSet The collection of derived CARGO objects. + -- @field #string GroupName The name of the CargoGroup. + + --- # CARGO\_GROUP class + -- + -- The CARGO\_GROUP class defines a cargo that is represented by a @{Group} object within the simulator. + -- The cargo can be Loaded, UnLoaded, Boarded, UnBoarded to and from Carriers. + -- + -- @field #CARGO_GROUP CARGO_GROUP + -- + CARGO_GROUP = { + ClassName = "CARGO_GROUP", + } + +--- CARGO_GROUP constructor. +-- This make a new CARGO_GROUP from a @{Group} object. +-- It will "ungroup" the group object within the sim, and will create a @{Set} of individual Unit objects. +-- @param #CARGO_GROUP self +-- @param Wrapper.Group#GROUP CargoGroup +-- @param #string Type +-- @param #string Name +-- @param #number ReportRadius (optional) +-- @param #number NearRadius (optional) +-- @return #CARGO_GROUP +function CARGO_GROUP:New( CargoGroup, Type, Name, ReportRadius ) + local self = BASE:Inherit( self, CARGO_REPORTABLE:New( Type, Name, 0, ReportRadius ) ) -- #CARGO_GROUP + self:F( { Type, Name, ReportRadius } ) + + self.CargoSet = SET_CARGO:New() + + self:SetDeployed( false ) + + local WeightGroup = 0 + + self.GroupName = CargoGroup:GetName() + self.CargoTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplate( self.GroupName ) ) + + CargoGroup:Destroy() + + -- We iterate through the group template and for each unit in the template, we create a new group with one unit. + for UnitID, UnitTemplate in pairs( self.CargoTemplate.units ) do + + local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate ) + local GroupName = env.getValueDictByKey( GroupTemplate.name ) + + -- We create a new group object with one unit... + -- First we prepare the template... + GroupTemplate.name = GroupName .. "#CARGO#" .. UnitID + GroupTemplate.groupId = nil + GroupTemplate.units = {} + GroupTemplate.units[1] = UnitTemplate + local UnitName = UnitTemplate.name .. "#CARGO" + GroupTemplate.units[1].name = UnitTemplate.name .. "#CARGO" + + + -- Then we register the new group in the database + local CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID) + + -- Now we spawn the new group based on the template created. + _DATABASE:Spawn( GroupTemplate ) + + -- And we register the spawned unit as part of the CargoSet. + local Unit = UNIT:FindByName( UnitName ) + --local WeightUnit = Unit:GetDesc().massEmpty + --WeightGroup = WeightGroup + WeightUnit + local CargoUnit = CARGO_UNIT:New( Unit, Type, UnitName, 10 ) + self.CargoSet:Add( UnitName, CargoUnit ) + end + + + self:SetWeight( WeightGroup ) + + self:T( { "Weight Cargo", WeightGroup } ) + + -- Cargo objects are added to the _DATABASE and SET_CARGO objects. + _EVENTDISPATCHER:CreateEventNewCargo( self ) + + self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead ) + self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead ) + self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead ) + + self:SetEventPriority( 4 ) + + return self +end + +--- @param #CARGO_GROUP self +-- @param Core.Event#EVENTDATA EventData +function CARGO_GROUP:OnEventCargoDead( EventData ) + + local Destroyed = false + + if self:IsDestroyed() or self:IsUnLoaded() or self:IsBoarding() then + Destroyed = true + for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do + local Cargo = CargoData -- #CARGO + if Cargo:IsAlive() then + Destroyed = false + else + Cargo:Destroyed() + end + end + else + local CarrierName = self.CargoCarrier:GetName() + if CarrierName == EventData.IniDCSUnitName then + MESSAGE:New( "Cargo is lost from carrier " .. CarrierName, 15 ):ToAll() + Destroyed = true + self.CargoCarrier:ClearCargo() + end + end + + if Destroyed then + self:Destroyed() + self:E( { "Cargo group destroyed" } ) + end + + end + + --- Enter Boarding State. + -- @param #CARGO_GROUP self + -- @param Wrapper.Unit#UNIT CargoCarrier + -- @param #string Event + -- @param #string From + -- @param #string To + function CARGO_GROUP:onenterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) + self:F( { CargoCarrier.UnitName, From, Event, To } ) + + local NearRadius = NearRadius or 25 + + if From == "UnLoaded" then + + -- For each Cargo object within the CARGO_GROUPED, route each object to the CargoLoadPointVec2 + self.CargoSet:ForEach( + function( Cargo, ... ) + Cargo:__Board( 1, CargoCarrier, NearRadius, ... ) + end, ... + ) + + self:__Boarding( 1, CargoCarrier, NearRadius, ... ) + end + + end + + --- Enter Loaded State. + -- @param #CARGO_GROUP self + -- @param Wrapper.Unit#UNIT CargoCarrier + -- @param #string Event + -- @param #string From + -- @param #string To + function CARGO_GROUP:onenterLoaded( From, Event, To, CargoCarrier, ... ) + self:F( { From, Event, To, CargoCarrier, ...} ) + + if From == "UnLoaded" then + -- For each Cargo object within the CARGO_GROUP, load each cargo to the CargoCarrier. + for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do + Cargo:Load( CargoCarrier ) + end + end + + --self.CargoObject:Destroy() + self.CargoCarrier = CargoCarrier + + end + + --- Leave Boarding State. + -- @param #CARGO_GROUP self + -- @param Wrapper.Unit#UNIT CargoCarrier + -- @param #string Event + -- @param #string From + -- @param #string To + function CARGO_GROUP:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) + self:F( { CargoCarrier.UnitName, From, Event, To } ) + + local NearRadius = NearRadius or 100 + + local Boarded = true + local Cancelled = false + local Dead = true + + self.CargoSet:Flush() + + -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 + for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do + self:T( { Cargo:GetName(), Cargo.current } ) + + + if not Cargo:is( "Loaded" ) + and (not Cargo:is( "Destroyed" )) then -- If one or more units of a group defined as CARGO_GROUP died, the CARGO_GROUP:Board() command does not trigger the CARGO_GRUOP:OnEnterLoaded() function. + Boarded = false + end + + if Cargo:is( "UnLoaded" ) then + Cancelled = true + end + + if not Cargo:is( "Destroyed" ) then + Dead = false + end + + end + + if not Dead then + + if not Cancelled then + if not Boarded then + self:__Boarding( 1, CargoCarrier, NearRadius, ... ) + else + self:F("Group Cargo is loaded") + self:__Load( 1, CargoCarrier, ... ) + end + else + self:__CancelBoarding( 1, CargoCarrier, NearRadius, ... ) + end + else + self:__Destroyed( 1, CargoCarrier, NearRadius, ... ) + end + + end + + --- Get the amount of cargo units in the group. + -- @param #CARGO_GROUP self + -- @return #CARGO_GROUP + function CARGO_GROUP:GetCount() + return self.CargoSet:Count() + end + + + --- Enter UnBoarding State. + -- @param #CARGO_GROUP self + -- @param Core.Point#POINT_VEC2 ToPointVec2 + -- @param #string Event + -- @param #string From + -- @param #string To + function CARGO_GROUP:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) + self:F( {From, Event, To, ToPointVec2, NearRadius } ) + + NearRadius = NearRadius or 25 + + local Timer = 1 + + if From == "Loaded" then + + if self.CargoObject then + self.CargoObject:Destroy() + end + + -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 + self.CargoSet:ForEach( + function( Cargo, NearRadius ) + + Cargo:__UnBoard( Timer, ToPointVec2, NearRadius ) + Timer = Timer + 3 + end, { NearRadius } + ) + + + self:__UnBoarding( 1, ToPointVec2, NearRadius, ... ) + end + + end + + --- Leave UnBoarding State. + -- @param #CARGO_GROUP self + -- @param Core.Point#POINT_VEC2 ToPointVec2 + -- @param #string Event + -- @param #string From + -- @param #string To + function CARGO_GROUP:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) + self:F( { From, Event, To, ToPointVec2, NearRadius } ) + + --local NearRadius = NearRadius or 25 + + local Angle = 180 + local Speed = 10 + local Distance = 5 + + if From == "UnBoarding" then + local UnBoarded = true + + -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 + for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do + self:T( Cargo.current ) + if not Cargo:is( "UnLoaded" ) and not Cargo:IsDestroyed() then + UnBoarded = false + end + end + + if UnBoarded then + return true + else + self:__UnBoarding( 1, ToPointVec2, NearRadius, ... ) + end + + return false + end + + end + + --- UnBoard Event. + -- @param #CARGO_GROUP self + -- @param Core.Point#POINT_VEC2 ToPointVec2 + -- @param #string Event + -- @param #string From + -- @param #string To + function CARGO_GROUP:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) + self:F( { From, Event, To, ToPointVec2, NearRadius } ) + + --local NearRadius = NearRadius or 25 + + self:__UnLoad( 1, ToPointVec2, ... ) + end + + + + --- Enter UnLoaded State. + -- @param #CARGO_GROUP self + -- @param Core.Point#POINT_VEC2 + -- @param #string Event + -- @param #string From + -- @param #string To + function CARGO_GROUP:onenterUnLoaded( From, Event, To, ToPointVec2, ... ) + self:F( { From, Event, To, ToPointVec2 } ) + + if From == "Loaded" then + + -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 + self.CargoSet:ForEach( + function( Cargo ) + --Cargo:UnLoad( ToPointVec2 ) + local RandomVec2=ToPointVec2:GetRandomPointVec2InRadius(10) + Cargo:UnLoad( RandomVec2 ) + end + ) + + end + + end + + + --- Respawn the cargo when destroyed + -- @param #CARGO_GROUP self + -- @param #boolean RespawnDestroyed + function CARGO_GROUP:RespawnOnDestroyed( RespawnDestroyed ) + self:F({"In function RespawnOnDestroyed"}) + if RespawnDestroyed then + self.onenterDestroyed = function( self ) + self:F("IN FUNCTION") + self:Respawn() + end + else + self.onenterDestroyed = nil + end + + end + + --- Get the current Coordinate of the CargoGroup. + -- @param #CARGO_GROUP self + -- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup. + -- @return #nil There is no valid Cargo in the CargoGroup. + function CARGO_GROUP:GetCoordinate() + self:F() + + local Cargo = self.CargoSet:GetFirst() + + if Cargo then + return Cargo.CargoObject:GetCoordinate() + end + + return nil + end + + --- Check if the CargoGroup is alive. + -- @param #CARGO_GROUP self + -- @return #boolean true if the CargoGroup is alive. + -- @return #boolean false if the CargoGroup is dead. + function CARGO_GROUP:IsAlive() + + local Alive = true + + -- For each Cargo within the CargoSet, check if the Cargo is Alive. + -- When the Cargo is Loaded, the Cargo is in the CargoCarrier, so we check if the CargoCarrier is alive. + -- When the Cargo is not Loaded, the Cargo is the CargoObject, so we check if the CargoObject is alive. + self.CargoSet:ForEach( + function( Cargo ) + if self:IsLoaded() then + Alive = Alive == true and Cargo.CargoCarrier:IsAlive() + else + Alive = Alive == true and Cargo.CargoObject:IsAlive() + end + end + ) + + return Alive + + end + + + --- Route Cargo to Coordinate and randomize locations. + -- @param #CARGO_GROUP self + -- @param Core.Point#COORDINATE Coordinate + function CARGO_GROUP:RouteTo( Coordinate ) + self:F( {Coordinate = Coordinate } ) + + -- For each Cargo within the CargoSet, route each object to the Coordinate + self.CargoSet:ForEach( + function( Cargo ) + Cargo.CargoObject:RouteGroundTo( Coordinate, 10, "vee", 0 ) + end + ) + + end + + --- Check if Cargo is near to the Carrier. + -- The Cargo is near to the Carrier if the first unit of the Cargo Group is within NearRadius. + -- @param #CARGO_GROUP self + -- @param Wrapper.Group#GROUP CargoCarrier + -- @param #number NearRadius + -- @return #boolean The Cargo is near to the Carrier. + -- @return #nil The Cargo is not near to the Carrier. + function CARGO_GROUP:IsNear( CargoCarrier, NearRadius ) + self:F( {NearRadius = NearRadius } ) + + local Cargo = self.CargoSet:GetFirst() -- #CARGO + + if Cargo then + return Cargo:IsNear( CargoCarrier:GetCoordinate(), NearRadius ) + end + + return nil + end + + --- Check if CargoGroup is in the ReportRadius for the Cargo to be Loaded. + -- @param #CARGO_GROUP self + -- @param Core.Point#Coordinate Coordinate + -- @return #boolean true if the CargoGroup is within the reporting radius. + function CARGO_GROUP:IsInRadius( Coordinate ) + self:F( { Coordinate } ) + + local Cargo = self.CargoSet:GetFirst() -- #CARGO + + if Cargo then + local Distance = 0 + if Cargo:IsLoaded() then + Distance = Coordinate:DistanceFromPointVec2( Cargo.CargoCarrier:GetPointVec2() ) + else + Distance = Coordinate:DistanceFromPointVec2( Cargo.CargoObject:GetPointVec2() ) + end + self:T( Distance ) + + if Distance <= self.ReportRadius then + return true + else + return false + end + end + + return nil + + end + + --- Respawn the CargoGroup. + -- @param #CARGO_GROUP self + function CARGO_GROUP:Respawn() + + self:F( { "Respawning" } ) + + for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do + local Cargo = CargoData -- #CARGO + Cargo:Destroy() + Cargo:SetStartState( "UnLoaded" ) + end + + + -- We iterate through the group template and for each unit in the template, we create a new group with one unit. + for UnitID, UnitTemplate in pairs( self.CargoTemplate.units ) do + + local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate ) + local GroupName = env.getValueDictByKey( GroupTemplate.name ) + + -- We create a new group object with one unit... + -- First we prepare the template... + GroupTemplate.name = GroupName .. "#CARGO#" .. UnitID + GroupTemplate.groupId = nil + GroupTemplate.units = {} + GroupTemplate.units[1] = UnitTemplate + local UnitName = UnitTemplate.name .. "#CARGO" + GroupTemplate.units[1].name = UnitTemplate.name .. "#CARGO" + + + -- Then we register the new group in the database + local CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID) + + -- Now we spawn the new group based on the template created. + _DATABASE:Spawn( GroupTemplate ) + + -- And we register the spawned unit as part of the CargoSet. + local Unit = UNIT:FindByName( UnitName ) + --local WeightUnit = Unit:GetDesc().massEmpty + --WeightGroup = WeightGroup + WeightUnit + local CargoUnit = CARGO_UNIT:New( Unit, Type, UnitName, 10 ) + self.CargoSet:Add( UnitName, CargoUnit ) + end + + self:SetDeployed( false ) + self:SetStartState( "UnLoaded" ) + + end + + --- Signal a flare at the position of the CargoGroup. + -- @param #CARGO_GROUP self + -- @param Utilities.Utils#FLARECOLOR FlareColor + function CARGO_GROUP:Flare( FlareColor ) + + local Cargo = self.CargoSet:GetFirst() -- #CARGO + if Cargo then + Cargo:Flare( FlareColor ) + end + end + + --- Smoke the CargoGroup. + -- @param #CARGO_GROUP self + -- @param Utilities.Utils#SMOKECOLOR SmokeColor The color of the smoke. + -- @param #number Radius The radius of randomization around the center of the first element of the CargoGroup. + function CARGO_GROUP:Smoke( SmokeColor, Radius ) + + local Cargo = self.CargoSet:GetFirst() -- #CARGO + + if Cargo then + Cargo:Smoke( SmokeColor, Radius ) + end + end + + --- Check if the first element of the CargoGroup is the given @{Zone}. + -- @param #CARGO self + -- @param Core.Zone#ZONE_BASE Zone + -- @return #boolean **true** if the first element of the CargoGroup is in the Zone + -- @return #boolean **false** if there is no element of the CargoGroup in the Zone. + function CARGO_GROUP:IsInZone( Zone ) + self:F( { Zone } ) + + local Cargo = self.CargoSet:GetFirst() -- #CARGO + + if Cargo then + return Cargo:IsInZone( Zone ) + end + + return nil + + end + +end -- CARGO_GROUP diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua new file mode 100644 index 000000000..ba9c17f61 --- /dev/null +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -0,0 +1,358 @@ +--- **Cargo** -- Management of single cargo logistics, which are based on a @{Unit} object. +-- +-- === +-- +-- ![Banner Image](..\Presentations\CARGO\Dia1.JPG) +-- +-- === +-- +-- ### [Demo Missions]() +-- +-- ### [YouTube Playlist]() +-- +-- === +-- +-- ### Author: **FlightControl** +-- ### Contributions: +-- +-- === +-- +-- @module CargoUnit + +do -- CARGO_UNIT + + --- Models CARGO in the form of units, which can be boarded, unboarded, loaded, unloaded. + -- @type CARGO_UNIT + -- @extends #CARGO_REPRESENTABLE + + --- # CARGO\_UNIT class, extends @{#CARGO_REPRESENTABLE} + -- + -- The CARGO\_UNIT class defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. + -- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO\_UNIT objects to and from carriers. + -- + -- === + -- + -- @field #CARGO_UNIT CARGO_UNIT + -- + CARGO_UNIT = { + ClassName = "CARGO_UNIT" + } + + --- CARGO_UNIT Constructor. + -- @param #CARGO_UNIT self + -- @param Wrapper.Unit#UNIT CargoUnit + -- @param #string Type + -- @param #string Name + -- @param #number Weight + -- @param #number ReportRadius (optional) + -- @param #number NearRadius (optional) + -- @return #CARGO_UNIT + function CARGO_UNIT:New( CargoUnit, Type, Name, Weight, NearRadius ) + local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, Weight, NearRadius ) ) -- #CARGO_UNIT + self:F( { Type, Name, Weight, NearRadius } ) + + self:T( CargoUnit ) + self.CargoObject = CargoUnit + + self:T( self.ClassName ) + + self:SetEventPriority( 5 ) + + return self + end + + --- Enter UnBoarding State. + -- @param #CARGO_UNIT self + -- @param #string Event + -- @param #string From + -- @param #string To + -- @param Core.Point#POINT_VEC2 ToPointVec2 + function CARGO_UNIT:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius ) + self:F( { From, Event, To, ToPointVec2, NearRadius } ) + + NearRadius = NearRadius or 25 + + local Angle = 180 + local Speed = 60 + local DeployDistance = 9 + local RouteDistance = 60 + + if From == "Loaded" then + + if not self:IsDestroyed() then + + local CargoCarrier = self.CargoCarrier -- Wrapper.Controllable#CONTROLLABLE + + local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() + local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + + + local CargoRoutePointVec2 = CargoCarrierPointVec2:Translate( RouteDistance, CargoDeployHeading ) + + + -- if there is no ToPointVec2 given, then use the CargoRoutePointVec2 + local FromDirectionVec3 = CargoCarrierPointVec2:GetDirectionVec3( ToPointVec2 or CargoRoutePointVec2 ) + local FromAngle = CargoCarrierPointVec2:GetAngleDegrees(FromDirectionVec3) + local FromPointVec2 = CargoCarrierPointVec2:Translate( DeployDistance, FromAngle ) + --local CargoDeployPointVec2 = CargoCarrierPointVec2:GetRandomCoordinateInRadius( 10, 5 ) + + ToPointVec2 = ToPointVec2 or CargoCarrierPointVec2:GetRandomCoordinateInRadius( NearRadius, DeployDistance ) + + -- Respawn the group... + if self.CargoObject then + self.CargoObject:ReSpawn( FromPointVec2:GetVec3(), CargoDeployHeading ) + self:F( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } ) + self.CargoCarrier = nil + + local Points = {} + + -- From + Points[#Points+1] = FromPointVec2:WaypointGround( Speed, "Vee" ) + + -- To + Points[#Points+1] = ToPointVec2:WaypointGround( Speed, "Vee" ) + + local TaskRoute = self.CargoObject:TaskRoute( Points ) + self.CargoObject:SetTask( TaskRoute, 1 ) + + + self:__UnBoarding( 1, ToPointVec2, NearRadius ) + end + end + end + + end + + --- Leave UnBoarding State. + -- @param #CARGO_UNIT self + -- @param #string Event + -- @param #string From + -- @param #string To + -- @param Core.Point#POINT_VEC2 ToPointVec2 + function CARGO_UNIT:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius ) + self:F( { From, Event, To, ToPointVec2, NearRadius } ) + + NearRadius = NearRadius or 100 + + local Angle = 180 + local Speed = 10 + local Distance = 5 + + if From == "UnBoarding" then + if self:IsNear( ToPointVec2, NearRadius ) then + return true + else + + self:__UnBoarding( 1, ToPointVec2, NearRadius ) + end + return false + end + + end + + --- UnBoard Event. + -- @param #CARGO_UNIT self + -- @param #string Event + -- @param #string From + -- @param #string To + -- @param Core.Point#POINT_VEC2 ToPointVec2 + function CARGO_UNIT:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius ) + self:F( { From, Event, To, ToPointVec2, NearRadius } ) + + NearRadius = NearRadius or 100 + + self.CargoInAir = self.CargoObject:InAir() + + self:T( self.CargoInAir ) + + -- Only unboard the cargo when the carrier is not in the air. + -- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea). + if not self.CargoInAir then + + end + + self:__UnLoad( 1, ToPointVec2, NearRadius ) + + end + + + + --- Enter UnLoaded State. + -- @param #CARGO_UNIT self + -- @param #string Event + -- @param #string From + -- @param #string To + -- @param Core.Point#POINT_VEC2 + function CARGO_UNIT:onenterUnLoaded( From, Event, To, ToPointVec2 ) + self:F( { ToPointVec2, From, Event, To } ) + + local Angle = 180 + local Speed = 10 + local Distance = 5 + + if From == "Loaded" then + local StartPointVec2 = self.CargoCarrier:GetPointVec2() + local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + local CargoDeployCoord = StartPointVec2:Translate( Distance, CargoDeployHeading ) + + ToPointVec2 = ToPointVec2 or COORDINATE:New( CargoDeployCoord.x, CargoDeployCoord.z ) + + -- Respawn the group... + if self.CargoObject then + self.CargoObject:ReSpawn( ToPointVec2:GetVec3(), 0 ) + self.CargoCarrier = nil + end + + end + + if self.OnUnLoadedCallBack then + self.OnUnLoadedCallBack( self, unpack( self.OnUnLoadedParameters ) ) + self.OnUnLoadedCallBack = nil + end + + end + + --- Board Event. + -- @param #CARGO_UNIT self + -- @param #string Event + -- @param #string From + -- @param #string To + function CARGO_UNIT:onafterBoard( From, Event, To, CargoCarrier, NearRadius, ... ) + self:F( { From, Event, To, CargoCarrier, NearRadius } ) + + local NearRadius = NearRadius or 25 + + self.CargoInAir = self.CargoObject:InAir() + + self:T( self.CargoInAir ) + + -- Only move the group to the carrier when the cargo is not in the air + -- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea). + if not self.CargoInAir then + if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then + self:Load( CargoCarrier, NearRadius, ... ) + else + local Speed = 90 + local Angle = 180 + local Distance = 5 + + NearRadius = NearRadius or 25 + + local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() + local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( Distance, CargoDeployHeading ) + + local Points = {} + + local PointStartVec2 = self.CargoObject:GetPointVec2() + + Points[#Points+1] = PointStartVec2:WaypointGround( Speed ) + Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed ) + + local TaskRoute = self.CargoObject:TaskRoute( Points ) + self.CargoObject:SetTask( TaskRoute, 2 ) + self:__Boarding( -1, CargoCarrier, NearRadius ) + self.RunCount = 0 + end + end + + end + + + --- Boarding Event. + -- @param #CARGO_UNIT self + -- @param #string Event + -- @param #string From + -- @param #string To + -- @param Wrapper.Client#CLIENT CargoCarrier + -- @param #number NearRadius + function CARGO_UNIT:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) + self:F( { From, Event, To, CargoCarrier.UnitName, NearRadius } ) + + + if CargoCarrier and CargoCarrier:IsAlive() and self.CargoObject and self.CargoObject:IsAlive() then + if CargoCarrier:InAir() == false then + if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then + self:__Load( 1, CargoCarrier, ... ) + else + self:__Boarding( -1, CargoCarrier, NearRadius, ... ) + self.RunCount = self.RunCount + 1 + if self.RunCount >= 60 then + self.RunCount = 0 + local Speed = 90 + local Angle = 180 + local Distance = 5 + + NearRadius = NearRadius or 25 + + local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() + local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( Distance, CargoDeployHeading ) + + local Points = {} + + local PointStartVec2 = self.CargoObject:GetPointVec2() + + Points[#Points+1] = PointStartVec2:WaypointGround( Speed ) + Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed ) + + local TaskRoute = self.CargoObject:TaskRoute( Points ) + self.CargoObject:SetTask( TaskRoute, 0.2 ) + end + end + else + self.CargoObject:MessageToGroup( "Cancelling Boarding... Get back on the ground!", 5, CargoCarrier:GetGroup(), self:GetName() ) + self:CancelBoarding( CargoCarrier, NearRadius, ... ) + self.CargoObject:SetCommand( self.CargoObject:CommandStopRoute( true ) ) + end + else + self:E("Something is wrong") + end + + end + + + --- Enter Boarding State. + -- @param #CARGO_UNIT self + -- @param #string Event + -- @param #string From + -- @param #string To + -- @param Wrapper.Unit#UNIT CargoCarrier + function CARGO_UNIT:onenterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) + self:F( { From, Event, To, CargoCarrier.UnitName, NearRadius } ) + + local Speed = 90 + local Angle = 180 + local Distance = 5 + + local NearRadius = NearRadius or 25 + + if From == "UnLoaded" or From == "Boarding" then + + end + + end + + --- Loaded State. + -- @param #CARGO_UNIT self + -- @param #string Event + -- @param #string From + -- @param #string To + -- @param Wrapper.Unit#UNIT CargoCarrier + function CARGO_UNIT:onenterLoaded( From, Event, To, CargoCarrier ) + self:F( { From, Event, To, CargoCarrier } ) + + self.CargoCarrier = CargoCarrier + + -- Only destroy the CargoObject is if there is a CargoObject (packages don't have CargoObjects). + if self.CargoObject then + self:T("Destroying") + self.CargoObject:Destroy() + end + end + +end -- CARGO_UNIT diff --git a/Moose Development/Moose/Core/Cargo.lua b/Moose Development/Moose/Core/Cargo.lua deleted file mode 100644 index 613fbc7b1..000000000 --- a/Moose Development/Moose/Core/Cargo.lua +++ /dev/null @@ -1,1860 +0,0 @@ ---- **Core** -- Management of CARGO logistics, that can be transported from and to transportation carriers. --- --- === --- --- ![Banner Image](..\Presentations\CARGO\Dia1.JPG) --- --- === --- --- Cargo can be of various forms, always are composed out of ONE object ( one unit or one static or one slingload crate ): --- --- * CARGO_UNIT, represented by a @{Unit} in a singleton @{Group}: Cargo can be represented by a Unit in a Group. a CARGO_UNIT is representable... --- * CARGO_GROUP, represented by a @{Group}. A CARGO_GROUP is reportable... --- --- This module is still under construction, but is described above works already, and will keep working ... --- --- === --- --- # Demo Missions --- --- ### [CARGO Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CGO%20-%20Cargo) --- --- ### [CARGO Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CGO%20-%20Cargo) --- --- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) --- --- === --- --- # YouTube Channel --- --- ### [CARGO YouTube Channel](https://www.youtube.com/watch?v=tM00lTlkpYs&list=PL7ZUrU4zZUl2zUTuKrLW5RsO9zLMqUtbf) --- --- === --- --- ### Author: **FlightControl** --- ### Contributions: --- --- === --- --- @module Cargo - --- Events - --- Board - ---- Boards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo to the Carrier. --- The cargo must be in the **UnLoaded** state. --- @function [parent=#CARGO] Board --- @param #CARGO self --- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. --- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). - ---- Boards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo to the Carrier. --- The cargo must be in the **UnLoaded** state. --- @function [parent=#CARGO] __Board --- @param #CARGO self --- @param #number DelaySeconds The amount of seconds to delay the action. --- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. --- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). - - --- UnBoard - ---- UnBoards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo from the Carrier. --- The cargo must be in the **Loaded** state. --- @function [parent=#CARGO] UnBoard --- @param #CARGO self --- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location. - ---- UnBoards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo from the Carrier. --- The cargo must be in the **Loaded** state. --- @function [parent=#CARGO] __UnBoard --- @param #CARGO self --- @param #number DelaySeconds The amount of seconds to delay the action. --- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location. - - --- Load - ---- Loads the cargo to a Carrier. The event will load the cargo into the Carrier regardless of its position. There will be no movement simulated of the cargo loading. --- The cargo must be in the **UnLoaded** state. --- @function [parent=#CARGO] Load --- @param #CARGO self --- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. - ---- Loads the cargo to a Carrier. The event will load the cargo into the Carrier regardless of its position. There will be no movement simulated of the cargo loading. --- The cargo must be in the **UnLoaded** state. --- @function [parent=#CARGO] __Load --- @param #CARGO self --- @param #number DelaySeconds The amount of seconds to delay the action. --- @param Wrapper.Controllable#CONTROLLABLE ToCarrier The Carrier that will hold the cargo. - - --- UnLoad - ---- UnLoads the cargo to a Carrier. The event will unload the cargo from the Carrier. There will be no movement simulated of the cargo loading. --- The cargo must be in the **Loaded** state. --- @function [parent=#CARGO] UnLoad --- @param #CARGO self --- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location. - ---- UnLoads the cargo to a Carrier. The event will unload the cargo from the Carrier. There will be no movement simulated of the cargo loading. --- The cargo must be in the **Loaded** state. --- @function [parent=#CARGO] __UnLoad --- @param #CARGO self --- @param #number DelaySeconds The amount of seconds to delay the action. --- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location. - --- State Transition Functions - --- UnLoaded - ---- @function [parent=#CARGO] OnLeaveUnLoaded --- @param #CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable --- @return #boolean - ---- @function [parent=#CARGO] OnEnterUnLoaded --- @param #CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable - --- Loaded - ---- @function [parent=#CARGO] OnLeaveLoaded --- @param #CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable --- @return #boolean - ---- @function [parent=#CARGO] OnEnterLoaded --- @param #CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable - --- Boarding - ---- @function [parent=#CARGO] OnLeaveBoarding --- @param #CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable --- @return #boolean - ---- @function [parent=#CARGO] OnEnterBoarding --- @param #CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable --- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). - --- UnBoarding - ---- @function [parent=#CARGO] OnLeaveUnBoarding --- @param #CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable --- @return #boolean - ---- @function [parent=#CARGO] OnEnterUnBoarding --- @param #CARGO self --- @param Wrapper.Controllable#CONTROLLABLE Controllable - - --- TODO: Find all Carrier objects and make the type of the Carriers Wrapper.Unit#UNIT in the documentation. - -CARGOS = {} - -do -- CARGO - - --- @type CARGO - -- @extends Core.Fsm#FSM_PROCESS - -- @field #string Type A string defining the type of the cargo. eg. Engineers, Equipment, Screwdrivers. - -- @field #string Name A string defining the name of the cargo. The name is the unique identifier of the cargo. - -- @field #number Weight A number defining the weight of the cargo. The weight is expressed in kg. - -- @field #number NearRadius (optional) A number defining the radius in meters when the cargo is near to a Carrier, so that it can be loaded. - -- @field Wrapper.Unit#UNIT CargoObject The alive DCS object representing the cargo. This value can be nil, meaning, that the cargo is not represented anywhere... - -- @field Wrapper.Client#CLIENT CargoCarrier The alive DCS object carrying the cargo. This value can be nil, meaning, that the cargo is not contained anywhere... - -- @field #boolean Slingloadable This flag defines if the cargo can be slingloaded. - -- @field #boolean Moveable This flag defines if the cargo is moveable. - -- @field #boolean Representable This flag defines if the cargo can be represented by a DCS Unit. - -- @field #boolean Containable This flag defines if the cargo can be contained within a DCS Unit. - - --- # (R2.1) CARGO class, extends @{Fsm#FSM_PROCESS} - -- - -- The CARGO class defines the core functions that defines a cargo object within MOOSE. - -- A cargo is a logical object defined that is available for transport, and has a life status within a simulation. - -- - -- The CARGO is a state machine: it manages the different events and states of the cargo. - -- All derived classes from CARGO follow the same state machine, expose the same cargo event functions, and provide the same cargo states. - -- - -- ## CARGO Events: - -- - -- * @{#CARGO.Board}( ToCarrier ): Boards the cargo to a carrier. - -- * @{#CARGO.Load}( ToCarrier ): Loads the cargo into a carrier, regardless of its position. - -- * @{#CARGO.UnBoard}( ToPointVec2 ): UnBoard the cargo from a carrier. This will trigger a movement of the cargo to the option ToPointVec2. - -- * @{#CARGO.UnLoad}( ToPointVec2 ): UnLoads the cargo from a carrier. - -- * @{#CARGO.Dead}( Controllable ): The cargo is dead. The cargo process will be ended. - -- - -- ## CARGO States: - -- - -- * **UnLoaded**: The cargo is unloaded from a carrier. - -- * **Boarding**: The cargo is currently boarding (= running) into a carrier. - -- * **Loaded**: The cargo is loaded into a carrier. - -- * **UnBoarding**: The cargo is currently unboarding (=running) from a carrier. - -- * **Dead**: The cargo is dead ... - -- * **End**: The process has come to an end. - -- - -- ## CARGO state transition methods: - -- - -- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. - -- There are 2 moments when state transition methods will be called by the state machine: - -- - -- * **Leaving** the state. - -- The state transition method needs to start with the name **OnLeave + the name of the state**. - -- If the state transition method returns false, then the processing of the state transition will not be done! - -- If you want to change the behaviour of the AIControllable at this event, return false, - -- but then you'll need to specify your own logic using the AIControllable! - -- - -- * **Entering** the state. - -- The state transition method needs to start with the name **OnEnter + the name of the state**. - -- These state transition methods need to provide a return value, which is specified at the function description. - -- - -- @field #CARGO - CARGO = { - ClassName = "CARGO", - Type = nil, - Name = nil, - Weight = nil, - CargoObject = nil, - CargoCarrier = nil, - Representable = false, - Slingloadable = false, - Moveable = false, - Containable = false, - } - - --- @type CARGO.CargoObjects - -- @map < #string, Wrapper.Positionable#POSITIONABLE > The alive POSITIONABLE objects representing the the cargo. - - - --- CARGO Constructor. This class is an abstract class and should not be instantiated. - -- @param #CARGO self - -- @param #string Type - -- @param #string Name - -- @param #number Weight - -- @param #number NearRadius (optional) - -- @return #CARGO - function CARGO:New( Type, Name, Weight ) --R2.1 - - local self = BASE:Inherit( self, FSM:New() ) -- #CARGO - self:F( { Type, Name, Weight } ) - - self:SetStartState( "UnLoaded" ) - self:AddTransition( { "UnLoaded", "Boarding" }, "Board", "Boarding" ) - self:AddTransition( "Boarding" , "Boarding", "Boarding" ) - self:AddTransition( "Boarding", "CancelBoarding", "UnLoaded" ) - self:AddTransition( "Boarding", "Load", "Loaded" ) - self:AddTransition( "UnLoaded", "Load", "Loaded" ) - self:AddTransition( "Loaded", "UnBoard", "UnBoarding" ) - self:AddTransition( "UnBoarding", "UnBoarding", "UnBoarding" ) - self:AddTransition( "UnBoarding", "UnLoad", "UnLoaded" ) - self:AddTransition( "Loaded", "UnLoad", "UnLoaded" ) - self:AddTransition( "*", "Damaged", "Damaged" ) - self:AddTransition( "*", "Destroyed", "Destroyed" ) - self:AddTransition( "*", "Respawn", "UnLoaded" ) - - - self.Type = Type - self.Name = Name - self.Weight = Weight - self.CargoObject = nil - self.CargoCarrier = nil -- Wrapper.Client#CLIENT - self.Representable = false - self.Slingloadable = false - self.Moveable = false - self.Containable = false - - self:SetDeployed( false ) - - self.CargoScheduler = SCHEDULER:New() - - CARGOS[self.Name] = self - - - return self - end - - --- Destroy the cargo. - -- @param #CARGO self - function CARGO:Destroy() - if self.CargoObject then - self.CargoObject:Destroy() - end - self:Destroyed() - end - - --- Get the name of the Cargo. - -- @param #CARGO self - -- @return #string The name of the Cargo. - function CARGO:GetName() --R2.1 - return self.Name - end - - --- Get the object name of the Cargo. - -- @param #CARGO self - -- @return #string The object name of the Cargo. - function CARGO:GetObjectName() --R2.1 - if self:IsLoaded() then - return self.CargoCarrier:GetName() - else - return self.CargoObject:GetName() - end - end - - --- Get the type of the Cargo. - -- @param #CARGO self - -- @return #string The type of the Cargo. - function CARGO:GetType() - return self.Type - end - - --- Get the current coordinates of the Cargo. - -- @param #CARGO self - -- @return Core.Point#COORDINATE The coordinates of the Cargo. - function CARGO:GetCoordinate() - return self.CargoObject:GetCoordinate() - end - - --- Check if cargo is destroyed. - -- @param #CARGO self - -- @return #boolean true if destroyed - function CARGO:IsDestroyed() - return self:Is( "Destroyed" ) - end - - - --- Check if cargo is loaded. - -- @param #CARGO self - -- @return #boolean true if loaded - function CARGO:IsLoaded() - return self:Is( "Loaded" ) - end - - --- Check if cargo is unloaded. - -- @param #CARGO self - -- @return #boolean true if unloaded - function CARGO:IsUnLoaded() - return self:Is( "UnLoaded" ) - end - - --- Check if cargo is boarding. - -- @param #CARGO self - -- @return #boolean true if boarding - function CARGO:IsBoarding() - return self:Is( "Boarding" ) - end - - --- Check if cargo is alive. - -- @param #CARGO self - -- @return #boolean true if unloaded - function CARGO:IsAlive() - - if self:IsLoaded() then - return self.CargoCarrier:IsAlive() - else - return self.CargoObject:IsAlive() - end - end - - --- Set the cargo as deployed - -- @param #CARGO self - function CARGO:SetDeployed( Deployed ) - self.Deployed = Deployed - end - - --- Is the cargo deployed - -- @param #CARGO self - -- @return #boolean - function CARGO:IsDeployed() - return self.Deployed - end - - - - - --- Template method to spawn a new representation of the CARGO in the simulator. - -- @param #CARGO self - -- @return #CARGO - function CARGO:Spawn( PointVec2 ) - self:F() - - end - - --- Signal a flare at the position of the CARGO. - -- @param #CARGO self - -- @param Utilities.Utils#FLARECOLOR FlareColor - function CARGO:Flare( FlareColor ) - if self:IsUnLoaded() then - trigger.action.signalFlare( self.CargoObject:GetVec3(), FlareColor , 0 ) - end - end - - --- Signal a white flare at the position of the CARGO. - -- @param #CARGO self - function CARGO:FlareWhite() - self:Flare( trigger.flareColor.White ) - end - - --- Signal a yellow flare at the position of the CARGO. - -- @param #CARGO self - function CARGO:FlareYellow() - self:Flare( trigger.flareColor.Yellow ) - end - - --- Signal a green flare at the position of the CARGO. - -- @param #CARGO self - function CARGO:FlareGreen() - self:Flare( trigger.flareColor.Green ) - end - - --- Signal a red flare at the position of the CARGO. - -- @param #CARGO self - function CARGO:FlareRed() - self:Flare( trigger.flareColor.Red ) - end - - --- Smoke the CARGO. - -- @param #CARGO self - -- @param Utilities.Utils#SMOKECOLOR SmokeColor The color of the smoke. - -- @param #number Radius The radius of randomization around the center of the Cargo. - function CARGO:Smoke( SmokeColor, Radius ) - if self:IsUnLoaded() then - if Radius then - trigger.action.smoke( self.CargoObject:GetRandomVec3( Radius ), SmokeColor ) - else - trigger.action.smoke( self.CargoObject:GetVec3(), SmokeColor ) - end - end - end - - --- Smoke the CARGO Green. - -- @param #CARGO self - function CARGO:SmokeGreen() - self:Smoke( trigger.smokeColor.Green, Range ) - end - - --- Smoke the CARGO Red. - -- @param #CARGO self - function CARGO:SmokeRed() - self:Smoke( trigger.smokeColor.Red, Range ) - end - - --- Smoke the CARGO White. - -- @param #CARGO self - function CARGO:SmokeWhite() - self:Smoke( trigger.smokeColor.White, Range ) - end - - --- Smoke the CARGO Orange. - -- @param #CARGO self - function CARGO:SmokeOrange() - self:Smoke( trigger.smokeColor.Orange, Range ) - end - - --- Smoke the CARGO Blue. - -- @param #CARGO self - function CARGO:SmokeBlue() - self:Smoke( trigger.smokeColor.Blue, Range ) - end - - - - - - - --- Check if Cargo is the given @{Zone}. - -- @param #CARGO self - -- @param Core.Zone#ZONE_BASE Zone - -- @return #boolean **true** if cargo is in the Zone, **false** if cargo is not in the Zone. - function CARGO:IsInZone( Zone ) - self:F( { Zone } ) - - if self:IsLoaded() then - return Zone:IsPointVec2InZone( self.CargoCarrier:GetPointVec2() ) - else - self:F( { Size = self.CargoObject:GetSize(), Units = self.CargoObject:GetUnits() } ) - if self.CargoObject:GetSize() ~= 0 then - return Zone:IsPointVec2InZone( self.CargoObject:GetPointVec2() ) - else - return false - end - end - - return nil - - end - - - --- Check if CargoCarrier is near the Cargo to be Loaded. - -- @param #CARGO self - -- @param Core.Point#POINT_VEC2 PointVec2 - -- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). - -- @return #boolean - function CARGO:IsNear( PointVec2, NearRadius ) - self:F( { PointVec2 = PointVec2, NearRadius = NearRadius } ) - - if self.CargoObject:IsAlive() then - --local Distance = PointVec2:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) - self:F( { CargoObjectName = self.CargoObject:GetName() } ) - self:F( { CargoObjectVec2 = self.CargoObject:GetVec2() } ) - self:F( { PointVec2 = PointVec2:GetVec2() } ) - local Distance = PointVec2:Get2DDistance( self.CargoObject:GetPointVec2() ) - self:T( Distance ) - - if Distance <= NearRadius then - return true - end - end - - return false - end - - --- Get the current PointVec2 of the cargo. - -- @param #CARGO self - -- @return Core.Point#POINT_VEC2 - function CARGO:GetPointVec2() - return self.CargoObject:GetPointVec2() - end - - --- Get the current Coordinate of the cargo. - -- @param #CARGO self - -- @return Core.Point#COORDINATE - function CARGO:GetCoordinate() - return self.CargoObject:GetCoordinate() - end - - --- Set the weight of the cargo. - -- @param #CARGO self - -- @param #number Weight The weight in kg. - -- @return #CARGO - function CARGO:SetWeight( Weight ) - self.Weight = Weight - return self - end - -end -- CARGO - -do -- CARGO_REPRESENTABLE - - --- @type CARGO_REPRESENTABLE - -- @extends #CARGO - -- @field test - - --- Models CARGO that is representable by a Unit. - -- @field #CARGO_REPRESENTABLE CARGO_REPRESENTABLE - CARGO_REPRESENTABLE = { - ClassName = "CARGO_REPRESENTABLE" - } - - --- CARGO_REPRESENTABLE Constructor. - -- @param #CARGO_REPRESENTABLE self - -- @param #string Type - -- @param #string Name - -- @param #number Weight - -- @param #number ReportRadius (optional) - -- @param #number NearRadius (optional) - -- @return #CARGO_REPRESENTABLE - function CARGO_REPRESENTABLE:New( CargoObject, Type, Name, Weight, ReportRadius, NearRadius ) - local self = BASE:Inherit( self, CARGO:New( Type, Name, Weight, ReportRadius, NearRadius ) ) -- #CARGO_REPRESENTABLE - self:F( { Type, Name, Weight, ReportRadius, NearRadius } ) - - return self - end - - --- CARGO_REPRESENTABLE Destructor. - -- @param #CARGO_REPRESENTABLE self - -- @return #CARGO_REPRESENTABLE - function CARGO_REPRESENTABLE:Destroy() - - -- Cargo objects are deleted from the _DATABASE and SET_CARGO objects. - self:F( { CargoName = self:GetName() } ) - _EVENTDISPATCHER:CreateEventDeleteCargo( self ) - - return self - end - - --- Route a cargo unit to a PointVec2. - -- @param #CARGO_REPRESENTABLE self - -- @param Core.Point#POINT_VEC2 ToPointVec2 - -- @param #number Speed - -- @return #CARGO_REPRESENTABLE - function CARGO_REPRESENTABLE:RouteTo( ToPointVec2, Speed ) - self:F2( ToPointVec2 ) - - local Points = {} - - local PointStartVec2 = self.CargoObject:GetPointVec2() - - Points[#Points+1] = PointStartVec2:WaypointGround( Speed ) - Points[#Points+1] = ToPointVec2:WaypointGround( Speed ) - - local TaskRoute = self.CargoObject:TaskRoute( Points ) - self.CargoObject:SetTask( TaskRoute, 2 ) - return self - end - - -end -- CARGO_REPRESENTABLE - -do -- CARGO_REPORTABLE - - --- @type CARGO_REPORTABLE - -- @extends #CARGO - CARGO_REPORTABLE = { - ClassName = "CARGO_REPORTABLE" - } - - --- CARGO_REPORTABLE Constructor. - -- @param #CARGO_REPORTABLE self - -- @param #string Type - -- @param #string Name - -- @param #number Weight - -- @param #number ReportRadius (optional) - -- @param #number NearRadius (optional) - -- @return #CARGO_REPORTABLE - function CARGO_REPORTABLE:New( Type, Name, Weight, ReportRadius ) - local self = BASE:Inherit( self, CARGO:New( Type, Name, Weight ) ) -- #CARGO_REPORTABLE - self:F( { Type, Name, Weight, ReportRadius } ) - - self.CargoSet = SET_CARGO:New() -- Core.Set#SET_CARGO - - self.ReportRadius = ReportRadius or 1000 - - return self - end - - --- Send a CC message to a @{Group}. - -- @param #CARGO_REPORTABLE self - -- @param #string Message - -- @param Wrapper.Group#GROUP TaskGroup - -- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown. - function CARGO_REPORTABLE:MessageToGroup( Message, TaskGroup, Name ) - - local Prefix = Name and "@ " .. Name .. ": " or "@ " .. TaskGroup:GetCallsign() .. ": " - Message = Prefix .. Message - MESSAGE:New( Message, 20, "Cargo: " .. self:GetName() ):ToGroup( TaskGroup ) - - end - - --- Get the Report radius, which is the radius when the Cargo is reporting itself. - -- @param #CARGO_REPORTABLE self - -- @return #number The range till Cargo reports itself. - function CARGO_REPORTABLE:GetBoardingRange() - return self.ReportRadius - end - - - -end - -do -- CARGO_UNIT - - --- Models CARGO in the form of units, which can be boarded, unboarded, loaded, unloaded. - -- @type CARGO_UNIT - -- @extends #CARGO_REPRESENTABLE - - --- # CARGO\_UNIT class, extends @{#CARGO_REPRESENTABLE} - -- - -- The CARGO\_UNIT class defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. - -- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO\_UNIT objects to and from carriers. - -- - -- === - -- - -- @field #CARGO_UNIT CARGO_UNIT - -- - CARGO_UNIT = { - ClassName = "CARGO_UNIT" - } - - --- CARGO_UNIT Constructor. - -- @param #CARGO_UNIT self - -- @param Wrapper.Unit#UNIT CargoUnit - -- @param #string Type - -- @param #string Name - -- @param #number Weight - -- @param #number ReportRadius (optional) - -- @param #number NearRadius (optional) - -- @return #CARGO_UNIT - function CARGO_UNIT:New( CargoUnit, Type, Name, Weight, NearRadius ) - local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, Weight, NearRadius ) ) -- #CARGO_UNIT - self:F( { Type, Name, Weight, NearRadius } ) - - self:T( CargoUnit ) - self.CargoObject = CargoUnit - - self:T( self.ClassName ) - - self:SetEventPriority( 5 ) - - return self - end - - --- Enter UnBoarding State. - -- @param #CARGO_UNIT self - -- @param #string Event - -- @param #string From - -- @param #string To - -- @param Core.Point#POINT_VEC2 ToPointVec2 - function CARGO_UNIT:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius ) - self:F( { From, Event, To, ToPointVec2, NearRadius } ) - - NearRadius = NearRadius or 25 - - local Angle = 180 - local Speed = 60 - local DeployDistance = 9 - local RouteDistance = 60 - - if From == "Loaded" then - - if not self:IsDestroyed() then - - local CargoCarrier = self.CargoCarrier -- Wrapper.Controllable#CONTROLLABLE - - local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - - - local CargoRoutePointVec2 = CargoCarrierPointVec2:Translate( RouteDistance, CargoDeployHeading ) - - - -- if there is no ToPointVec2 given, then use the CargoRoutePointVec2 - local FromDirectionVec3 = CargoCarrierPointVec2:GetDirectionVec3( ToPointVec2 or CargoRoutePointVec2 ) - local FromAngle = CargoCarrierPointVec2:GetAngleDegrees(FromDirectionVec3) - local FromPointVec2 = CargoCarrierPointVec2:Translate( DeployDistance, FromAngle ) - --local CargoDeployPointVec2 = CargoCarrierPointVec2:GetRandomCoordinateInRadius( 10, 5 ) - - ToPointVec2 = ToPointVec2 or CargoCarrierPointVec2:GetRandomCoordinateInRadius( NearRadius, DeployDistance ) - - -- Respawn the group... - if self.CargoObject then - self.CargoObject:ReSpawn( FromPointVec2:GetVec3(), CargoDeployHeading ) - self:F( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } ) - self.CargoCarrier = nil - - local Points = {} - - -- From - Points[#Points+1] = FromPointVec2:WaypointGround( Speed, "Vee" ) - - -- To - Points[#Points+1] = ToPointVec2:WaypointGround( Speed, "Vee" ) - - local TaskRoute = self.CargoObject:TaskRoute( Points ) - self.CargoObject:SetTask( TaskRoute, 1 ) - - - self:__UnBoarding( 1, ToPointVec2, NearRadius ) - end - end - end - - end - - --- Leave UnBoarding State. - -- @param #CARGO_UNIT self - -- @param #string Event - -- @param #string From - -- @param #string To - -- @param Core.Point#POINT_VEC2 ToPointVec2 - function CARGO_UNIT:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius ) - self:F( { From, Event, To, ToPointVec2, NearRadius } ) - - NearRadius = NearRadius or 100 - - local Angle = 180 - local Speed = 10 - local Distance = 5 - - if From == "UnBoarding" then - if self:IsNear( ToPointVec2, NearRadius ) then - return true - else - - self:__UnBoarding( 1, ToPointVec2, NearRadius ) - end - return false - end - - end - - --- UnBoard Event. - -- @param #CARGO_UNIT self - -- @param #string Event - -- @param #string From - -- @param #string To - -- @param Core.Point#POINT_VEC2 ToPointVec2 - function CARGO_UNIT:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius ) - self:F( { From, Event, To, ToPointVec2, NearRadius } ) - - NearRadius = NearRadius or 100 - - self.CargoInAir = self.CargoObject:InAir() - - self:T( self.CargoInAir ) - - -- Only unboard the cargo when the carrier is not in the air. - -- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea). - if not self.CargoInAir then - - end - - self:__UnLoad( 1, ToPointVec2, NearRadius ) - - end - - - - --- Enter UnLoaded State. - -- @param #CARGO_UNIT self - -- @param #string Event - -- @param #string From - -- @param #string To - -- @param Core.Point#POINT_VEC2 - function CARGO_UNIT:onenterUnLoaded( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) - - local Angle = 180 - local Speed = 10 - local Distance = 5 - - if From == "Loaded" then - local StartPointVec2 = self.CargoCarrier:GetPointVec2() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local CargoDeployCoord = StartPointVec2:Translate( Distance, CargoDeployHeading ) - - ToPointVec2 = ToPointVec2 or COORDINATE:New( CargoDeployCoord.x, CargoDeployCoord.z ) - - -- Respawn the group... - if self.CargoObject then - self.CargoObject:ReSpawn( ToPointVec2:GetVec3(), 0 ) - self.CargoCarrier = nil - end - - end - - if self.OnUnLoadedCallBack then - self.OnUnLoadedCallBack( self, unpack( self.OnUnLoadedParameters ) ) - self.OnUnLoadedCallBack = nil - end - - end - - --- Board Event. - -- @param #CARGO_UNIT self - -- @param #string Event - -- @param #string From - -- @param #string To - function CARGO_UNIT:onafterBoard( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { From, Event, To, CargoCarrier, NearRadius } ) - - local NearRadius = NearRadius or 25 - - self.CargoInAir = self.CargoObject:InAir() - - self:T( self.CargoInAir ) - - -- Only move the group to the carrier when the cargo is not in the air - -- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea). - if not self.CargoInAir then - if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then - self:Load( CargoCarrier, NearRadius, ... ) - else - local Speed = 90 - local Angle = 180 - local Distance = 5 - - NearRadius = NearRadius or 25 - - local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() - local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( Distance, CargoDeployHeading ) - - local Points = {} - - local PointStartVec2 = self.CargoObject:GetPointVec2() - - Points[#Points+1] = PointStartVec2:WaypointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed ) - - local TaskRoute = self.CargoObject:TaskRoute( Points ) - self.CargoObject:SetTask( TaskRoute, 2 ) - self:__Boarding( -1, CargoCarrier, NearRadius ) - self.RunCount = 0 - end - end - - end - - - --- Boarding Event. - -- @param #CARGO_UNIT self - -- @param #string Event - -- @param #string From - -- @param #string To - -- @param Wrapper.Client#CLIENT CargoCarrier - -- @param #number NearRadius - function CARGO_UNIT:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { From, Event, To, CargoCarrier.UnitName, NearRadius } ) - - - if CargoCarrier and CargoCarrier:IsAlive() and self.CargoObject and self.CargoObject:IsAlive() then - if CargoCarrier:InAir() == false then - if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then - self:__Load( 1, CargoCarrier, ... ) - else - self:__Boarding( -1, CargoCarrier, NearRadius, ... ) - self.RunCount = self.RunCount + 1 - if self.RunCount >= 60 then - self.RunCount = 0 - local Speed = 90 - local Angle = 180 - local Distance = 5 - - NearRadius = NearRadius or 25 - - local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() - local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( Distance, CargoDeployHeading ) - - local Points = {} - - local PointStartVec2 = self.CargoObject:GetPointVec2() - - Points[#Points+1] = PointStartVec2:WaypointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed ) - - local TaskRoute = self.CargoObject:TaskRoute( Points ) - self.CargoObject:SetTask( TaskRoute, 0.2 ) - end - end - else - self.CargoObject:MessageToGroup( "Cancelling Boarding... Get back on the ground!", 5, CargoCarrier:GetGroup(), self:GetName() ) - self:CancelBoarding( CargoCarrier, NearRadius, ... ) - self.CargoObject:SetCommand( self.CargoObject:CommandStopRoute( true ) ) - end - else - self:E("Something is wrong") - end - - end - - - --- Enter Boarding State. - -- @param #CARGO_UNIT self - -- @param #string Event - -- @param #string From - -- @param #string To - -- @param Wrapper.Unit#UNIT CargoCarrier - function CARGO_UNIT:onenterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { From, Event, To, CargoCarrier.UnitName, NearRadius } ) - - local Speed = 90 - local Angle = 180 - local Distance = 5 - - local NearRadius = NearRadius or 25 - - if From == "UnLoaded" or From == "Boarding" then - - end - - end - - --- Loaded State. - -- @param #CARGO_UNIT self - -- @param #string Event - -- @param #string From - -- @param #string To - -- @param Wrapper.Unit#UNIT CargoCarrier - function CARGO_UNIT:onenterLoaded( From, Event, To, CargoCarrier ) - self:F( { From, Event, To, CargoCarrier } ) - - self.CargoCarrier = CargoCarrier - - -- Only destroy the CargoObject is if there is a CargoObject (packages don't have CargoObjects). - if self.CargoObject then - self:T("Destroying") - self.CargoObject:Destroy() - end - end - -end -- CARGO_UNIT - -do -- CARGO_CRATE - - --- Models the behaviour of cargo crates, which can be slingloaded and boarded on helicopters using the DCS menus. - -- @type CARGO_CRATE - -- @extends #CARGO_REPRESENTABLE - - --- # CARGO\_CRATE class, extends @{#CARGO_REPRESENTABLE} - -- - -- The CARGO\_CRATE class defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. - -- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO\_CRATE objects to and from carriers. - -- - -- === - -- - -- @field #CARGO_CRATE - CARGO_CRATE = { - ClassName = "CARGO_CRATE" - } - - --- CARGO_CRATE Constructor. - -- @param #CARGO_CRATE self - -- @param #string CrateName - -- @param #string Type - -- @param #string Name - -- @param #number Weight - -- @param #number ReportRadius (optional) - -- @param #number NearRadius (optional) - -- @return #CARGO_CRATE - function CARGO_CRATE:New( CargoCrateName, Type, Name, NearRadius ) - local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoCrateName, Type, Name, nil, NearRadius ) ) -- #CARGO_CRATE - self:F( { Type, Name, NearRadius } ) - - self:T( CargoCrateName ) - _DATABASE:AddStatic( CargoCrateName ) - - self.CargoObject = STATIC:FindByName( CargoCrateName ) - - self:T( self.ClassName ) - - self:SetEventPriority( 5 ) - - return self - end - - - - --- Enter UnLoaded State. - -- @param #CARGO_CRATE self - -- @param #string Event - -- @param #string From - -- @param #string To - -- @param Core.Point#POINT_VEC2 - function CARGO_CRATE:onenterUnLoaded( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) - - local Angle = 180 - local Speed = 10 - local Distance = 10 - - if From == "Loaded" then - local StartCoordinate = self.CargoCarrier:GetCoordinate() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local CargoDeployCoord = StartCoordinate:Translate( Distance, CargoDeployHeading ) - - ToPointVec2 = ToPointVec2 or COORDINATE:NewFromVec2( { x= CargoDeployCoord.x, y = CargoDeployCoord.z } ) - - -- Respawn the group... - if self.CargoObject then - self.CargoObject:ReSpawn( ToPointVec2, 0 ) - self.CargoCarrier = nil - end - - end - - if self.OnUnLoadedCallBack then - self.OnUnLoadedCallBack( self, unpack( self.OnUnLoadedParameters ) ) - self.OnUnLoadedCallBack = nil - end - - end - - - --- Loaded State. - -- @param #CARGO_CRATE self - -- @param #string Event - -- @param #string From - -- @param #string To - -- @param Wrapper.Unit#UNIT CargoCarrier - function CARGO_CRATE:onenterLoaded( From, Event, To, CargoCarrier ) - self:F( { From, Event, To, CargoCarrier } ) - - self.CargoCarrier = CargoCarrier - - -- Only destroy the CargoObject is if there is a CargoObject (packages don't have CargoObjects). - if self.CargoObject then - self:T("Destroying") - self.CargoObject:Destroy() - end - end - - - -end - -do -- CARGO_GROUP - - --- @type CARGO_GROUP - -- @extends #CARGO_REPORTABLE - - --- # CARGO\_GROUP class - -- - -- The CARGO\_GROUP class defines a cargo that is represented by a @{Group} object within the simulator, and can be transported by a carrier. - -- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO\_GROUP to and from carrier. - -- - -- @field #CARGO_GROUP CARGO_GROUP - -- - CARGO_GROUP = { - ClassName = "CARGO_GROUP", - } - ---- CARGO_GROUP constructor. --- This make a new CARGO_GROUP from a @{Group} object. --- It will "ungroup" the group object within the sim, and will create a @{Set} of individual Unit objects. --- @param #CARGO_GROUP self --- @param Wrapper.Group#GROUP CargoGroup --- @param #string Type --- @param #string Name --- @param #number ReportRadius (optional) --- @param #number NearRadius (optional) --- @return #CARGO_GROUP -function CARGO_GROUP:New( CargoGroup, Type, Name, ReportRadius ) - local self = BASE:Inherit( self, CARGO_REPORTABLE:New( Type, Name, 0, ReportRadius ) ) -- #CARGO_GROUP - self:F( { Type, Name, ReportRadius } ) - - self:SetDeployed( false ) - - local WeightGroup = 0 - - self.GroupName = CargoGroup:GetName() - self.CargoTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplate( self.GroupName ) ) - - CargoGroup:Destroy() - - -- We iterate through the group template and for each unit in the template, we create a new group with one unit. - for UnitID, UnitTemplate in pairs( self.CargoTemplate.units ) do - - local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate ) - local GroupName = env.getValueDictByKey( GroupTemplate.name ) - - -- We create a new group object with one unit... - -- First we prepare the template... - GroupTemplate.name = GroupName .. "#CARGO#" .. UnitID - GroupTemplate.groupId = nil - GroupTemplate.units = {} - GroupTemplate.units[1] = UnitTemplate - local UnitName = UnitTemplate.name .. "#CARGO" - GroupTemplate.units[1].name = UnitTemplate.name .. "#CARGO" - - - -- Then we register the new group in the database - local CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID) - - -- Now we spawn the new group based on the template created. - _DATABASE:Spawn( GroupTemplate ) - - -- And we register the spawned unit as part of the CargoSet. - local Unit = UNIT:FindByName( UnitName ) - --local WeightUnit = Unit:GetDesc().massEmpty - --WeightGroup = WeightGroup + WeightUnit - local CargoUnit = CARGO_UNIT:New( Unit, Type, UnitName, 10 ) - self.CargoSet:Add( UnitName, CargoUnit ) - end - - - self:SetWeight( WeightGroup ) - - self:T( { "Weight Cargo", WeightGroup } ) - - -- Cargo objects are added to the _DATABASE and SET_CARGO objects. - _EVENTDISPATCHER:CreateEventNewCargo( self ) - - self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead ) - self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead ) - self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead ) - - self:SetEventPriority( 4 ) - - return self -end - ---- @param #CARGO_GROUP self --- @param Core.Event#EVENTDATA EventData -function CARGO_GROUP:OnEventCargoDead( EventData ) - - local Destroyed = false - - if self:IsDestroyed() or self:IsUnLoaded() or self:IsBoarding() then - Destroyed = true - for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do - local Cargo = CargoData -- #CARGO - if Cargo:IsAlive() then - Destroyed = false - else - Cargo:Destroyed() - end - end - else - local CarrierName = self.CargoCarrier:GetName() - if CarrierName == EventData.IniDCSUnitName then - MESSAGE:New( "Cargo is lost from carrier " .. CarrierName, 15 ):ToAll() - Destroyed = true - self.CargoCarrier:ClearCargo() - end - end - - if Destroyed then - self:Destroyed() - self:E( { "Cargo group destroyed" } ) - end - - end - - --- Enter Boarding State. - -- @param #CARGO_GROUP self - -- @param Wrapper.Unit#UNIT CargoCarrier - -- @param #string Event - -- @param #string From - -- @param #string To - function CARGO_GROUP:onenterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) - - local NearRadius = NearRadius or 25 - - if From == "UnLoaded" then - - -- For each Cargo object within the CARGO_GROUPED, route each object to the CargoLoadPointVec2 - self.CargoSet:ForEach( - function( Cargo, ... ) - Cargo:__Board( 1, CargoCarrier, NearRadius, ... ) - end, ... - ) - - self:__Boarding( 1, CargoCarrier, NearRadius, ... ) - end - - end - - --- Enter Loaded State. - -- @param #CARGO_GROUP self - -- @param Wrapper.Unit#UNIT CargoCarrier - -- @param #string Event - -- @param #string From - -- @param #string To - function CARGO_GROUP:onenterLoaded( From, Event, To, CargoCarrier, ... ) - self:F( { From, Event, To, CargoCarrier, ...} ) - - if From == "UnLoaded" then - -- For each Cargo object within the CARGO_GROUP, load each cargo to the CargoCarrier. - for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do - Cargo:Load( CargoCarrier ) - end - end - - --self.CargoObject:Destroy() - self.CargoCarrier = CargoCarrier - - end - - --- Leave Boarding State. - -- @param #CARGO_GROUP self - -- @param Wrapper.Unit#UNIT CargoCarrier - -- @param #string Event - -- @param #string From - -- @param #string To - function CARGO_GROUP:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) - - local NearRadius = NearRadius or 100 - - local Boarded = true - local Cancelled = false - local Dead = true - - self.CargoSet:Flush() - - -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 - for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do - self:T( { Cargo:GetName(), Cargo.current } ) - - - if not Cargo:is( "Loaded" ) - and (not Cargo:is( "Destroyed" )) then -- If one or more units of a group defined as CARGO_GROUP died, the CARGO_GROUP:Board() command does not trigger the CARGO_GRUOP:OnEnterLoaded() function. - Boarded = false - end - - if Cargo:is( "UnLoaded" ) then - Cancelled = true - end - - if not Cargo:is( "Destroyed" ) then - Dead = false - end - - end - - if not Dead then - - if not Cancelled then - if not Boarded then - self:__Boarding( 1, CargoCarrier, NearRadius, ... ) - else - self:F("Group Cargo is loaded") - self:__Load( 1, CargoCarrier, ... ) - end - else - self:__CancelBoarding( 1, CargoCarrier, NearRadius, ... ) - end - else - self:__Destroyed( 1, CargoCarrier, NearRadius, ... ) - end - - end - - --- Get the amount of cargo units in the group. - -- @param #CARGO_GROUP self - -- @return #CARGO_GROUP - function CARGO_GROUP:GetCount() - return self.CargoSet:Count() - end - - - --- Enter UnBoarding State. - -- @param #CARGO_GROUP self - -- @param Core.Point#POINT_VEC2 ToPointVec2 - -- @param #string Event - -- @param #string From - -- @param #string To - function CARGO_GROUP:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) - self:F( {From, Event, To, ToPointVec2, NearRadius } ) - - NearRadius = NearRadius or 25 - - local Timer = 1 - - if From == "Loaded" then - - if self.CargoObject then - self.CargoObject:Destroy() - end - - -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 - self.CargoSet:ForEach( - function( Cargo, NearRadius ) - - Cargo:__UnBoard( Timer, ToPointVec2, NearRadius ) - Timer = Timer + 3 - end, { NearRadius } - ) - - - self:__UnBoarding( 1, ToPointVec2, NearRadius, ... ) - end - - end - - --- Leave UnBoarding State. - -- @param #CARGO_GROUP self - -- @param Core.Point#POINT_VEC2 ToPointVec2 - -- @param #string Event - -- @param #string From - -- @param #string To - function CARGO_GROUP:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) - self:F( { From, Event, To, ToPointVec2, NearRadius } ) - - --local NearRadius = NearRadius or 25 - - local Angle = 180 - local Speed = 10 - local Distance = 5 - - if From == "UnBoarding" then - local UnBoarded = true - - -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 - for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do - self:T( Cargo.current ) - if not Cargo:is( "UnLoaded" ) and not Cargo:IsDestroyed() then - UnBoarded = false - end - end - - if UnBoarded then - return true - else - self:__UnBoarding( 1, ToPointVec2, NearRadius, ... ) - end - - return false - end - - end - - --- UnBoard Event. - -- @param #CARGO_GROUP self - -- @param Core.Point#POINT_VEC2 ToPointVec2 - -- @param #string Event - -- @param #string From - -- @param #string To - function CARGO_GROUP:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) - self:F( { From, Event, To, ToPointVec2, NearRadius } ) - - --local NearRadius = NearRadius or 25 - - self:__UnLoad( 1, ToPointVec2, ... ) - end - - - - --- Enter UnLoaded State. - -- @param #CARGO_GROUP self - -- @param Core.Point#POINT_VEC2 - -- @param #string Event - -- @param #string From - -- @param #string To - function CARGO_GROUP:onenterUnLoaded( From, Event, To, ToPointVec2, ... ) - self:F( { From, Event, To, ToPointVec2 } ) - - if From == "Loaded" then - - -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 - self.CargoSet:ForEach( - function( Cargo ) - --Cargo:UnLoad( ToPointVec2 ) - local RandomVec2=ToPointVec2:GetRandomPointVec2InRadius(10) - Cargo:UnLoad( RandomVec2 ) - end - ) - - end - - end - - - --- Respawn the cargo when destroyed - -- @param #CARGO_GROUP self - -- @param #boolean RespawnDestroyed - function CARGO_GROUP:RespawnOnDestroyed( RespawnDestroyed ) - self:F({"In function RespawnOnDestroyed"}) - if RespawnDestroyed then - self.onenterDestroyed = function( self ) - self:F("IN FUNCTION") - self:Respawn() - end - else - self.onenterDestroyed = nil - end - - end - - --- Get the current Coordinate of the CargoGroup. - -- @param #CARGO_GROUP self - -- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup. - -- @return #nil There is no valid Cargo in the CargoGroup. - function CARGO_GROUP:GetCoordinate() - self:F() - - local Cargo = self.CargoSet:GetFirst() - - if Cargo then - return Cargo.CargoObject:GetCoordinate() - end - - return nil - end - - --- Check if the CargoGroup is alive. - -- @param #CARGO_GROUP self - -- @return #boolean true if the CargoGroup is alive. - -- @return #boolean false if the CargoGroup is dead. - function CARGO_GROUP:IsAlive() - - local Alive = true - - -- For each Cargo within the CargoSet, check if the Cargo is Alive. - -- When the Cargo is Loaded, the Cargo is in the CargoCarrier, so we check if the CargoCarrier is alive. - -- When the Cargo is not Loaded, the Cargo is the CargoObject, so we check if the CargoObject is alive. - self.CargoSet:ForEach( - function( Cargo ) - if self:IsLoaded() then - Alive = Alive == true and Cargo.CargoCarrier:IsAlive() - else - Alive = Alive == true and Cargo.CargoObject:IsAlive() - end - end - ) - - return Alive - - end - - - --- Route Cargo to Coordinate and randomize locations. - -- @param #CARGO_GROUP self - -- @param Core.Point#COORDINATE Coordinate - function CARGO_GROUP:RouteTo( Coordinate ) - self:F( {Coordinate = Coordinate } ) - - -- For each Cargo within the CargoSet, route each object to the Coordinate - self.CargoSet:ForEach( - function( Cargo ) - Cargo.CargoObject:RouteGroundTo( Coordinate, 10, "vee", 0 ) - end - ) - - end - - --- Check if Cargo is near to the Carrier. - -- The Cargo is near to the Carrier if the first unit of the Cargo Group is within NearRadius. - -- @param #CARGO_GROUP self - -- @param Wrapper.Group#GROUP CargoCarrier - -- @param #number NearRadius - -- @return #boolean The Cargo is near to the Carrier. - -- @return #nil The Cargo is not near to the Carrier. - function CARGO_GROUP:IsNear( CargoCarrier, NearRadius ) - self:F( {NearRadius = NearRadius } ) - - local Cargo = self.CargoSet:GetFirst() -- #CARGO - - if Cargo then - return Cargo:IsNear( CargoCarrier:GetCoordinate(), NearRadius ) - end - - return nil - end - - --- Check if CargoGroup is in the ReportRadius for the Cargo to be Loaded. - -- @param #CARGO_GROUP self - -- @param Core.Point#Coordinate Coordinate - -- @return #boolean true if the CargoGroup is within the reporting radius. - function CARGO_GROUP:IsInRadius( Coordinate ) - self:F( { Coordinate } ) - - local Cargo = self.CargoSet:GetFirst() -- #CARGO - - if Cargo then - local Distance = 0 - if Cargo:IsLoaded() then - Distance = Coordinate:DistanceFromPointVec2( Cargo.CargoCarrier:GetPointVec2() ) - else - Distance = Coordinate:DistanceFromPointVec2( Cargo.CargoObject:GetPointVec2() ) - end - self:T( Distance ) - - if Distance <= self.ReportRadius then - return true - else - return false - end - end - - return nil - - end - - --- Respawn the CargoGroup. - -- @param #CARGO_GROUP self - function CARGO_GROUP:Respawn() - - self:F( { "Respawning" } ) - - for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do - local Cargo = CargoData -- #CARGO - Cargo:Destroy() - Cargo:SetStartState( "UnLoaded" ) - end - - - -- We iterate through the group template and for each unit in the template, we create a new group with one unit. - for UnitID, UnitTemplate in pairs( self.CargoTemplate.units ) do - - local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate ) - local GroupName = env.getValueDictByKey( GroupTemplate.name ) - - -- We create a new group object with one unit... - -- First we prepare the template... - GroupTemplate.name = GroupName .. "#CARGO#" .. UnitID - GroupTemplate.groupId = nil - GroupTemplate.units = {} - GroupTemplate.units[1] = UnitTemplate - local UnitName = UnitTemplate.name .. "#CARGO" - GroupTemplate.units[1].name = UnitTemplate.name .. "#CARGO" - - - -- Then we register the new group in the database - local CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID) - - -- Now we spawn the new group based on the template created. - _DATABASE:Spawn( GroupTemplate ) - - -- And we register the spawned unit as part of the CargoSet. - local Unit = UNIT:FindByName( UnitName ) - --local WeightUnit = Unit:GetDesc().massEmpty - --WeightGroup = WeightGroup + WeightUnit - local CargoUnit = CARGO_UNIT:New( Unit, Type, UnitName, 10 ) - self.CargoSet:Add( UnitName, CargoUnit ) - end - - self:SetDeployed( false ) - self:SetStartState( "UnLoaded" ) - - end - - --- Signal a flare at the position of the CargoGroup. - -- @param #CARGO_GROUP self - -- @param Utilities.Utils#FLARECOLOR FlareColor - function CARGO_GROUP:Flare( FlareColor ) - - local Cargo = self.CargoSet:GetFirst() -- #CARGO - if Cargo then - Cargo:Flare( FlareColor ) - end - end - - --- Smoke the CargoGroup. - -- @param #CARGO_GROUP self - -- @param Utilities.Utils#SMOKECOLOR SmokeColor The color of the smoke. - -- @param #number Radius The radius of randomization around the center of the first element of the CargoGroup. - function CARGO_GROUP:Smoke( SmokeColor, Radius ) - - local Cargo = self.CargoSet:GetFirst() -- #CARGO - - if Cargo then - Cargo:Smoke( SmokeColor, Radius ) - end - end - - --- Check if the first element of the CargoGroup is the given @{Zone}. - -- @param #CARGO self - -- @param Core.Zone#ZONE_BASE Zone - -- @return #boolean **true** if the first element of the CargoGroup is in the Zone - -- @return #boolean **false** if there is no element of the CargoGroup in the Zone. - function CARGO_GROUP:IsInZone( Zone ) - self:F( { Zone } ) - - local Cargo = self.CargoSet:GetFirst() -- #CARGO - - if Cargo then - return Cargo:IsInZone( Zone ) - end - - return nil - - end - -end -- CARGO_GROUP - -do -- CARGO_PACKAGE - - --- @type CARGO_PACKAGE - -- @extends #CARGO_REPRESENTABLE - CARGO_PACKAGE = { - ClassName = "CARGO_PACKAGE" - } - ---- CARGO_PACKAGE Constructor. --- @param #CARGO_PACKAGE self --- @param Wrapper.Unit#UNIT CargoCarrier The UNIT carrying the package. --- @param #string Type --- @param #string Name --- @param #number Weight --- @param #number ReportRadius (optional) --- @param #number NearRadius (optional) --- @return #CARGO_PACKAGE -function CARGO_PACKAGE:New( CargoCarrier, Type, Name, Weight, ReportRadius, NearRadius ) - local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoCarrier, Type, Name, Weight, ReportRadius, NearRadius ) ) -- #CARGO_PACKAGE - self:F( { Type, Name, Weight, ReportRadius, NearRadius } ) - - self:T( CargoCarrier ) - self.CargoCarrier = CargoCarrier - - return self -end - ---- Board Event. --- @param #CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #number Speed --- @param #number BoardDistance --- @param #number Angle -function CARGO_PACKAGE:onafterOnBoard( From, Event, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) - self:F() - - self.CargoInAir = self.CargoCarrier:InAir() - - self:T( self.CargoInAir ) - - -- Only move the CargoCarrier to the New CargoCarrier when the New CargoCarrier is not in the air. - if not self.CargoInAir then - - local Points = {} - - local StartPointVec2 = self.CargoCarrier:GetPointVec2() - local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - self:T( { CargoCarrierHeading, CargoDeployHeading } ) - local CargoDeployPointVec2 = CargoCarrier:GetPointVec2():Translate( BoardDistance, CargoDeployHeading ) - - Points[#Points+1] = StartPointVec2:WaypointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed ) - - local TaskRoute = self.CargoCarrier:TaskRoute( Points ) - self.CargoCarrier:SetTask( TaskRoute, 1 ) - end - - self:Boarded( CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) - -end - ---- Check if CargoCarrier is near the Cargo to be Loaded. --- @param #CARGO_PACKAGE self --- @param Wrapper.Unit#UNIT CargoCarrier --- @return #boolean -function CARGO_PACKAGE:IsNear( CargoCarrier ) - self:F() - - local CargoCarrierPoint = CargoCarrier:GetPointVec2() - - local Distance = CargoCarrierPoint:DistanceFromPointVec2( self.CargoCarrier:GetPointVec2() ) - self:T( Distance ) - - if Distance <= self.NearRadius then - return true - else - return false - end -end - ---- Boarded Event. --- @param #CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier -function CARGO_PACKAGE:onafterOnBoarded( From, Event, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) - self:F() - - if self:IsNear( CargoCarrier ) then - self:__Load( 1, CargoCarrier, Speed, LoadDistance, Angle ) - else - self:__Boarded( 1, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) - end -end - ---- UnBoard Event. --- @param #CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param #number Speed --- @param #number UnLoadDistance --- @param #number UnBoardDistance --- @param #number Radius --- @param #number Angle -function CARGO_PACKAGE:onafterUnBoard( From, Event, To, CargoCarrier, Speed, UnLoadDistance, UnBoardDistance, Radius, Angle ) - self:F() - - self.CargoInAir = self.CargoCarrier:InAir() - - self:T( self.CargoInAir ) - - -- Only unboard the cargo when the carrier is not in the air. - -- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea). - if not self.CargoInAir then - - self:_Next( self.FsmP.UnLoad, UnLoadDistance, Angle ) - - local Points = {} - - local StartPointVec2 = CargoCarrier:GetPointVec2() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - self:T( { CargoCarrierHeading, CargoDeployHeading } ) - local CargoDeployPointVec2 = StartPointVec2:Translate( UnBoardDistance, CargoDeployHeading ) - - Points[#Points+1] = StartPointVec2:WaypointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed ) - - local TaskRoute = CargoCarrier:TaskRoute( Points ) - CargoCarrier:SetTask( TaskRoute, 1 ) - end - - self:__UnBoarded( 1 , CargoCarrier, Speed ) - -end - ---- UnBoarded Event. --- @param #CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier -function CARGO_PACKAGE:onafterUnBoarded( From, Event, To, CargoCarrier, Speed ) - self:F() - - if self:IsNear( CargoCarrier ) then - self:__UnLoad( 1, CargoCarrier, Speed ) - else - self:__UnBoarded( 1, CargoCarrier, Speed ) - end -end - ---- Load Event. --- @param #CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT CargoCarrier --- @param #number Speed --- @param #number LoadDistance --- @param #number Angle -function CARGO_PACKAGE:onafterLoad( From, Event, To, CargoCarrier, Speed, LoadDistance, Angle ) - self:F() - - self.CargoCarrier = CargoCarrier - - local StartPointVec2 = self.CargoCarrier:GetPointVec2() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local CargoDeployPointVec2 = StartPointVec2:Translate( LoadDistance, CargoDeployHeading ) - - local Points = {} - Points[#Points+1] = StartPointVec2:WaypointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed ) - - local TaskRoute = self.CargoCarrier:TaskRoute( Points ) - self.CargoCarrier:SetTask( TaskRoute, 1 ) - -end - ---- UnLoad Event. --- @param #CARGO_PACKAGE self --- @param #string Event --- @param #string From --- @param #string To --- @param #number Distance --- @param #number Angle -function CARGO_PACKAGE:onafterUnLoad( From, Event, To, CargoCarrier, Speed, Distance, Angle ) - self:F() - - local StartPointVec2 = self.CargoCarrier:GetPointVec2() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local CargoDeployPointVec2 = StartPointVec2:Translate( Distance, CargoDeployHeading ) - - self.CargoCarrier = CargoCarrier - - local Points = {} - Points[#Points+1] = StartPointVec2:WaypointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed ) - - local TaskRoute = self.CargoCarrier:TaskRoute( Points ) - self.CargoCarrier:SetTask( TaskRoute, 1 ) - -end - - -end diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index 49a8fc08b..ebc56985c 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -21,7 +21,6 @@ Core/Radio.lua Core/Spawn.lua Core/SpawnStatic.lua Core/Goal.lua -Core/Cargo.lua Core/Spot.lua Wrapper/Object.lua @@ -35,6 +34,11 @@ Wrapper/Static.lua Wrapper/Airbase.lua Wrapper/Scenery.lua +Cargo/Cargo.lua +Cargo/CargoUnit.lua +Cargo/CargoCrate.lua +Cargo/CargoGroup.lua + Functional/Scoring.lua Functional/CleanUp.lua Functional/Movement.lua From a94e7440284d60c04f372f8ec90d7a9e65212a62 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sat, 31 Mar 2018 07:35:18 +0200 Subject: [PATCH 020/420] -- New modules for CSAR tasking. --- .../Moose/Tasking/Task_CARGO.lua | 5 +- .../Moose/Tasking/Task_Cargo_CSAR.lua | 187 +++++++++ .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 362 ++++++++++++++++++ .../Moose/Tasking/Task_Cargo_Transport.lua | 187 +++++++++ .../Moose/Tasking/Task_Manager.lua | 150 ++++++++ Moose Setup/Moose.files | 4 + 6 files changed, 892 insertions(+), 3 deletions(-) create mode 100644 Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua create mode 100644 Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua create mode 100644 Moose Development/Moose/Tasking/Task_Cargo_Transport.lua create mode 100644 Moose Development/Moose/Tasking/Task_Manager.lua diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 7d11cae8d..c9127d3fe 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -726,10 +726,9 @@ do -- TASK_CARGO end --- @param #TASK_CARGO self - -- @param @list DeployZones - -- @param Wrapper.Unit#UNIT TaskUnit + -- @param #list DeployZones -- @return #TASK_CARGO - function TASK_CARGO:SetDeployZones( DeployZones, TaskUnit ) + function TASK_CARGO:SetDeployZones( DeployZones ) for DeployZoneID, DeployZone in pairs( DeployZones ) do self.DeployZones[DeployZone:GetName()] = DeployZone diff --git a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua new file mode 100644 index 000000000..b45dc7e26 --- /dev/null +++ b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua @@ -0,0 +1,187 @@ +--- **Tasking** -- Models tasks for players to execute CSAR @{Cargo} downed pilots. +-- +-- ![Banner Image](..\Presentations\TASK_CARGO\Dia1.JPG) +-- +-- === + + +do -- TASK_CARGO_CSAR + + --- The TASK_CARGO_CSAR class + -- @type TASK_CARGO_CSAR + -- @extends Tasking.Task_Cargo#TASK_CARGO + TASK_CARGO_CSAR = { + ClassName = "TASK_CARGO_CSAR", + } + + --- Instantiates a new TASK_CARGO_CSAR. + -- @param #TASK_CARGO_CSAR self + -- @param Tasking.Mission#MISSION Mission + -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param #string TaskName The name of the Task. + -- @param Core.Set#SET_CARGO SetCargo The scope of the cargo to be transported. + -- @param #string TaskBriefing The Cargo Task briefing. + -- @return #TASK_CARGO_CSAR self + function TASK_CARGO_CSAR:New( Mission, SetGroup, TaskName, SetCargo, TaskBriefing ) + local self = BASE:Inherit( self, TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, "Transport", TaskBriefing ) ) -- #TASK_CARGO_CSAR + self:F() + + Mission:AddTask( self ) + + + -- Events + + self:AddTransition( "*", "CargoPickedUp", "*" ) + self:AddTransition( "*", "CargoDeployed", "*" ) + + self:F( { CargoDeployed = self.CargoDeployed ~= nil and "true" or "false" } ) + + --- OnBefore Transition Handler for Event CargoPickedUp. + -- @function [parent=#TASK_CARGO_CSAR] OnBeforeCargoPickedUp + -- @param #TASK_CARGO_CSAR self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event CargoPickedUp. + -- @function [parent=#TASK_CARGO_CSAR] OnAfterCargoPickedUp + -- @param #TASK_CARGO_CSAR self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + + --- Synchronous Event Trigger for Event CargoPickedUp. + -- @function [parent=#TASK_CARGO_CSAR] CargoPickedUp + -- @param #TASK_CARGO_CSAR self + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + + --- Asynchronous Event Trigger for Event CargoPickedUp. + -- @function [parent=#TASK_CARGO_CSAR] __CargoPickedUp + -- @param #TASK_CARGO_CSAR self + -- @param #number Delay The delay in seconds. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + + --- OnBefore Transition Handler for Event CargoDeployed. + -- @function [parent=#TASK_CARGO_CSAR] OnBeforeCargoDeployed + -- @param #TASK_CARGO_CSAR self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event CargoDeployed. + -- @function [parent=#TASK_CARGO_CSAR] OnAfterCargoDeployed + -- @param #TASK_CARGO_CSAR self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. + + --- Synchronous Event Trigger for Event CargoDeployed. + -- @function [parent=#TASK_CARGO_CSAR] CargoDeployed + -- @param #TASK_CARGO_CSAR self + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. + + --- Asynchronous Event Trigger for Event CargoDeployed. + -- @function [parent=#TASK_CARGO_CSAR] __CargoDeployed + -- @param #TASK_CARGO_CSAR self + -- @param #number Delay The delay in seconds. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. + + local Fsm = self:GetUnitProcess() + + local CargoReport = REPORT:New( "Rescue a downed pilot from the following position:") + + SetCargo:ForEachCargo( + --- @param Core.Cargo#CARGO Cargo + function( Cargo ) + local CargoType = Cargo:GetType() + local CargoName = Cargo:GetName() + local CargoCoordinate = Cargo:GetCoordinate() + CargoReport:Add( string.format( '- "%s" (%s) at %s', CargoName, CargoType, CargoCoordinate:ToStringMGRS() ) ) + end + ) + + self:SetBriefing( + TaskBriefing or + CargoReport:Text() + ) + + + return self + end + + function TASK_CARGO_CSAR:ReportOrder( ReportGroup ) + + return 0 + end + + + --- + -- @param #TASK_CARGO_CSAR self + -- @return #boolean + function TASK_CARGO_CSAR:IsAllCargoTransported() + + local CargoSet = self:GetCargoSet() + local Set = CargoSet:GetSet() + + local DeployZones = self:GetDeployZones() + + local CargoDeployed = true + + -- Loop the CargoSet (so evaluate each Cargo in the SET_CARGO ). + for CargoID, CargoData in pairs( Set ) do + local Cargo = CargoData -- Core.Cargo#CARGO + + self:F( { Cargo = Cargo:GetName(), CargoDeployed = Cargo:IsDeployed() } ) + + if Cargo:IsDeployed() then + +-- -- Loop the DeployZones set for the TASK_CARGO_CSAR. +-- for DeployZoneID, DeployZone in pairs( DeployZones ) do +-- +-- -- If all cargo is in one of the deploy zones, then all is good. +-- self:T( { Cargo.CargoObject } ) +-- if Cargo:IsInZone( DeployZone ) == false then +-- CargoDeployed = false +-- end +-- end + else + CargoDeployed = false + end + end + + self:F( { CargoDeployed = CargoDeployed } ) + + return CargoDeployed + end + + --- @param #TASK_CARGO_CSAR self + function TASK_CARGO_CSAR:onafterGoal( TaskUnit, From, Event, To ) + local CargoSet = self.CargoSet + + if self:IsAllCargoTransported() then + self:Success() + end + + self:__Goal( -10 ) + end + +end + diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua new file mode 100644 index 000000000..3b22c9e01 --- /dev/null +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -0,0 +1,362 @@ +--- **Tasking** - Creates and manages player TASK_CARGO tasks. +-- +-- === +-- +-- ### Author: **FlightControl** +-- +-- ### Contributions: +-- +-- === +-- +-- @module Task_Cargo_Dispatcher + +do -- TASK_CARGO_DISPATCHER + + --- TASK_CARGO_DISPATCHER class. + -- @type TASK_CARGO_DISPATCHER + -- @extends Tasking.Task_Manager#TASK_MANAGER + -- @field TASK_CARGO_DISPATCHER.CSAR CSAR + + --- @type TASK_CARGO_DISPATCHER.CSAR + -- @field Wrapper.Unit#UNIT PilotUnit + -- @field Tasking.Task#TASK Task + + + --- # TASK_CARGO_DISPATCHER class, extends @{Task_Manager#TASK_MANAGER} + -- + -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia1.JPG) + -- + -- The @{#TASK_CARGO_DISPATCHER} class implements the dynamic dispatching of cargo tasks. + -- + -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia3.JPG) + -- + -- The EWR will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched. + -- Find a summary below describing for which situation a task type is created: + -- + -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia9.JPG) + -- + -- * **CSAR Task**: Is created when a fiendly pilot has ejected from a plane, and needs to be rescued (sometimes behind enemy lines). + -- + -- ## 1. TASK\_A2A\_DISPATCHER constructor: + -- + -- The @{#TASK_CARGO_DISPATCHER.New}() method creates a new TASK\_A2A\_DISPATCHER instance. + -- + -- ### 1.1. Define or set the **Mission**: + -- + -- Tasking is executed to accomplish missions. Therefore, a MISSION object needs to be given as the first parameter. + -- + -- local HQ = GROUP:FindByName( "HQ", "Bravo" ) + -- local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) + -- local Mission = MISSION:New( CommandCenter, "A2A Mission", "High", "Watch the air enemy units being detected.", coalition.side.RED ) + -- + -- Missions are governed by COMMANDCENTERS, so, ensure you have a COMMANDCENTER object installed and setup within your mission. + -- Create the MISSION object, and hook it under the command center. + -- + -- ### 1.2. Build a set of the groups seated by human players: + -- + -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia6.JPG) + -- + -- A set or collection of the groups wherein human players can be seated, these can be clients or units that can be joined as a slot or jumping into. + -- + -- local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Defender" ):FilterStart() + -- + -- The set is built using the SET_GROUP class. Apply any filter criteria to identify the correct groups for your mission. + -- Only these slots or units will be able to execute the mission and will receive tasks for this mission, once available. + -- + -- ### 1.3. Define the **EWR network**: + -- + -- As part of the TASK\_A2A\_DISPATCHER constructor, an EWR network must be given as the third parameter. + -- An EWR network, or, Early Warning Radar network, is used to early detect potential airborne targets and to understand the position of patrolling targets of the enemy. + -- + -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia5.JPG) + -- + -- Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units. + -- These radars have different ranges and 55G6 EWR and 1L13 EWR radars are Eastern Bloc units (eg Russia, Ukraine, Georgia) while the Hawk and Patriot radars are Western (eg US). + -- Additionally, ANY other radar capable unit can be part of the EWR network! Also AWACS airborne units, planes, helicopters can help to detect targets, as long as they have radar. + -- The position of these units is very important as they need to provide enough coverage + -- to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them. + -- + -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia7.JPG) + -- + -- Additionally in a hot war situation where the border is no longer respected the placement of radars has a big effect on how fast the war escalates. + -- For example if they are a long way forward and can detect enemy planes on the ground and taking off + -- they will start to vector CAP and GCI flights to attack them straight away which will immediately draw a response from the other coalition. + -- Having the radars further back will mean a slower escalation because fewer targets will be detected and + -- therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map. + -- It all depends on what the desired effect is. + -- + -- EWR networks are **dynamically constructed**, that is, they form part of the @{Functional#DETECTION_BASE} object that is given as the input parameter of the TASK\_A2A\_DISPATCHER class. + -- By defining in a **smart way the names or name prefixes of the groups** with EWR capable units, these groups will be **automatically added or deleted** from the EWR network, + -- increasing or decreasing the radar coverage of the Early Warning System. + -- + -- See the following example to setup an EWR network containing EWR stations and AWACS. + -- + -- local EWRSet = SET_GROUP:New():FilterPrefixes( "EWR" ):FilterCoalitions("red"):FilterStart() + -- + -- local EWRDetection = DETECTION_AREAS:New( EWRSet, 6000 ) + -- EWRDetection:SetFriendliesRange( 10000 ) + -- EWRDetection:SetRefreshTimeInterval(30) + -- + -- -- Setup the A2A dispatcher, and initialize it. + -- A2ADispatcher = TASK_CARGO_DISPATCHER:New( Mission, AttackGroups, EWRDetection ) + -- + -- The above example creates a SET_GROUP instance, and stores this in the variable (object) **EWRSet**. + -- **EWRSet** is then being configured to filter all active groups with a group name starting with **EWR** to be included in the Set. + -- **EWRSet** is then being ordered to start the dynamic filtering. Note that any destroy or new spawn of a group with the above names will be removed or added to the Set. + -- Then a new **EWRDetection** object is created from the class DETECTION_AREAS. A grouping radius of 6000 is choosen, which is 6km. + -- The **EWRDetection** object is then passed to the @{#TASK_CARGO_DISPATCHER.New}() method to indicate the EWR network configuration and setup the A2A tasking and detection mechanism. + -- + -- ### 2. Define the detected **target grouping radius**: + -- + -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia8.JPG) + -- + -- The target grouping radius is a property of the Detection object, that was passed to the AI\_A2A\_DISPATCHER object, but can be changed. + -- The grouping radius should not be too small, but also depends on the types of planes and the era of the simulation. + -- Fast planes like in the 80s, need a larger radius than WWII planes. + -- Typically I suggest to use 30000 for new generation planes and 10000 for older era aircraft. + -- + -- Note that detected targets are constantly re-grouped, that is, when certain detected aircraft are moving further than the group radius, then these aircraft will become a separate + -- group being detected. This may result in additional GCI being started by the dispatcher! So don't make this value too small! + -- + -- ## 3. Set the **Engage radius**: + -- + -- Define the radius to engage any target by airborne friendlies, which are executing cap or returning from an intercept mission. + -- + -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia11.JPG) + -- + -- So, if there is a target area detected and reported, + -- then any friendlies that are airborne near this target area, + -- will be commanded to (re-)engage that target when available (if no other tasks were commanded). + -- For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target, + -- will be considered to receive the command to engage that target area. + -- You need to evaluate the value of this parameter carefully. + -- If too small, more intercept missions may be triggered upon detected target areas. + -- If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far. + -- + -- ## 4. Set **Scoring** and **Messages**: + -- + -- The TASK\_A2A\_DISPATCHER is a state machine. It triggers the event Assign when a new player joins a @{Task} dispatched by the TASK\_A2A\_DISPATCHER. + -- An _event handler_ can be defined to catch the **Assign** event, and add **additional processing** to set _scoring_ and to _define messages_, + -- when the player reaches certain achievements in the task. + -- + -- The prototype to handle the **Assign** event needs to be developed as follows: + -- + -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( ... ) + -- + -- --- @param #TaskDispatcher self + -- -- @param #string From Contains the name of the state from where the Event was triggered. + -- -- @param #string Event Contains the name of the event that was triggered. In this case Assign. + -- -- @param #string To Contains the name of the state that will be transitioned to. + -- -- @param Tasking.Task_A2A#TASK_A2A Task The Task object, which is any derived object from TASK_A2A. + -- -- @param Wrapper.Unit#UNIT TaskUnit The Unit or Client that contains the Player. + -- -- @param #string PlayerName The name of the Player that joined the TaskUnit. + -- function TaskDispatcher:OnAfterAssign( From, Event, To, Task, TaskUnit, PlayerName ) + -- Task:SetScoreOnProgress( PlayerName, 20, TaskUnit ) + -- Task:SetScoreOnSuccess( PlayerName, 200, TaskUnit ) + -- Task:SetScoreOnFail( PlayerName, -100, TaskUnit ) + -- end + -- + -- The **OnAfterAssign** method (function) is added to the TaskDispatcher object. + -- This method will be called when a new player joins a unit in the set of groups in scope of the dispatcher. + -- So, this method will be called only **ONCE** when a player joins a unit in scope of the task. + -- + -- The TASK class implements various methods to additional **set scoring** for player achievements: + -- + -- * @{Tasking.Task#TASK.SetScoreOnProgress}() will add additional scores when a player achieves **Progress** while executing the task. + -- Examples of **task progress** can be destroying units, arriving at zones etc. + -- + -- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional scores when the task goes into **Success** state. + -- This means the **task has been successfully completed**. + -- + -- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional (negative) scores when the task goes into **Failed** state. + -- This means the **task has not been successfully completed**, and the scores must be given with a negative value! + -- + -- @field #TASK_CARGO_DISPATCHER + TASK_CARGO_DISPATCHER = { + ClassName = "TASK_CARGO_DISPATCHER", + Mission = nil, + Tasks = {}, + CSAR = {}, + } + + + --- TASK_CARGO_DISPATCHER constructor. + -- @param #TASK_CARGO_DISPATCHER self + -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. + -- @param Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission. + -- @return #TASK_CARGO_DISPATCHER self + function TASK_CARGO_DISPATCHER:New( Mission, SetGroup ) + + -- Inherits from DETECTION_MANAGER + local self = BASE:Inherit( self, TASK_MANAGER:New( SetGroup ) ) -- #TASK_CARGO_DISPATCHER + + self.Mission = Mission + + self:AddTransition( "Started", "Assign", "Started" ) + + --- OnAfter Transition Handler for Event Assign. + -- @function [parent=#TASK_CARGO_DISPATCHER] OnAfterAssign + -- @param #TASK_CARGO_DISPATCHER self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Tasking.Task_A2A#TASK_A2A Task + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param #string PlayerName + + self:SetCSARRadius() + self:__Start( 5 ) + + -- For CSAR missions, we process the event when a pilot ejects. + + self:HandleEvent( EVENTS.Ejection ) + + return self + end + + + --- Handle the event when a pilot ejects. + -- @param #TASK_CARGO_DISPATCHER self + -- @param Core.Event#EVENTDATA EventData + function TASK_CARGO_DISPATCHER:OnEventEjection( EventData ) + + self:E( { EventData = EventData } ) + + local PilotUnit = EventData.IniUnit + local CSARName = EventData.IniUnitName + + self.CSAR[CSARName] = {} + self.CSAR[CSARName].PilotUnit = PilotUnit + self.CSAR[CSARName].Task = nil + + return self + end + + + --- Define the radius to when a CSAR task will be generated for any downed pilot within range of the nearest CSAR airbase. + -- @param #TASK_CARGO_DISPATCHER self + -- @param #number CSARRadius (Optional, Default = 50000) The radius in meters to decide whether a CSAR needs to be created. + -- @return #TASK_CARGO_DISPATCHER + -- @usage + -- + -- -- Set 20km as the radius to CSAR any downed pilot within range of the nearest CSAR airbase. + -- TaskA2ADispatcher:SetEngageRadius( 20000 ) + -- + -- -- Set 50km as the radius to to CSAR any downed pilot within range of the nearest CSAR airbase. + -- TaskA2ADispatcher:SetEngageRadius() -- 50000 is the default value. + -- + function TASK_CARGO_DISPATCHER:SetCSARRadius( CSARRadius ) + + self.Detection:SetFriendliesRange( CSARRadius or 50000 ) + + return self + end + + + --- Define one deploy zone for the CSAR tasks. + -- @param #TASK_CARGO_DISPATCHER self + -- @param DeployZone A deploy zone. + -- @return #TASK_CARGO_DISPATCHER + function TASK_CARGO_DISPATCHER:SetCSARDeployZone( CSARDeployZone ) + + self.CSARDeployZones = { CSARDeployZone } + + return self + end + + + --- Define the deploy zones for the CSAR tasks. + -- @param #TASK_CARGO_DISPATCHER self + -- @param DeployZones A list of the deploy zones. + -- @return #TASK_CARGO_DISPATCHER + function TASK_CARGO_DISPATCHER:SetCSARDeployZones( CSARDeployZones ) + + self.CSARDeployZones = CSARDeployZones + + return self + end + + + --- Evaluates of a CSAR task needs to be started. + -- @param #TASK_CARGO_DISPATCHER self + -- @return Set#SET_CARGO The SetCargo to be rescued. + -- @return #nil If there is no CSAR task required. + function TASK_CARGO_DISPATCHER:EvaluateCSAR( CSARUnit ) + + local CSARCargo = CARGO_UNIT:New( CSARUnit, "Pilot", CSARUnit:GetName(), 80, 1500, 10 ) + + local SetCargo = SET_CARGO:New() + SetCargo:AddCargosByName( CSARUnit:GetName() ) + + SetCargo:Flush(self) + + return SetCargo + + end + + + + --- Assigns tasks to the @{Set#SET_GROUP}. + -- @param #TASK_CARGO_DISPATCHER self + -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. + function TASK_CARGO_DISPATCHER:ManageTasks() + self:F() + + local AreaMsg = {} + local TaskMsg = {} + local ChangeMsg = {} + + local Mission = self.Mission + + if Mission:IsIDLE() or Mission:IsENGAGED() then + + local TaskReport = REPORT:New() + + -- Checking the task queue for the dispatcher, and removing any obsolete task! + for TaskIndex, TaskData in pairs( self.Tasks ) do + local Task = TaskData -- Tasking.Task#TASK + if Task:IsStatePlanned() then + -- Here we need to check if the pilot is still existing. +-- local DetectedItem = Detection:GetDetectedItemByIndex( TaskIndex ) +-- if not DetectedItem then +-- local TaskText = Task:GetName() +-- for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do +-- Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2A task %s for %s removed.", TaskText, Mission:GetShortText() ), TaskGroup ) +-- end +-- Task = self:RemoveTask( TaskIndex ) +-- end + end + end + + -- Now that all obsolete tasks are removed, loop through the CSAR pilots. + for CSARID, CSARData in pairs( self.CSAR ) do + + if CSARData.Task then + else + -- New CSAR Task + local SetCargo = self:EvaluateCSAR( CSARData.PilotUnit ) + local CSARTask = TASK_CARGO_CSAR:New( Mission, self.SetGroup, "Rescue Pilot", SetCargo ) + CSARTask:SetDeployZones( { self.CSARDeployZones } ) + Mission:AddTask( CSARTask ) + TaskReport:Add( CSARTask:GetName() ) + end + end + + + -- TODO set menus using the HQ coordinator + Mission:GetCommandCenter():SetMenu() + + local TaskText = TaskReport:Text(", ") + + for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do + if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" then + Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup ) + end + end + + end + + return true + end + +end diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua new file mode 100644 index 000000000..0deff06e4 --- /dev/null +++ b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua @@ -0,0 +1,187 @@ +--- **Tasking** -- The TASK_CARGO models tasks for players to transport @{Cargo}. +-- +-- ![Banner Image](..\Presentations\TASK_CARGO\Dia1.JPG) +-- +-- === + + +do -- TASK_CARGO_TRANSPORT + + --- The TASK_CARGO_TRANSPORT class + -- @type TASK_CARGO_TRANSPORT + -- @extends Tasking.Task_Cargo#TASK_CARGO + TASK_CARGO_TRANSPORT = { + ClassName = "TASK_CARGO_TRANSPORT", + } + + --- Instantiates a new TASK_CARGO_TRANSPORT. + -- @param #TASK_CARGO_TRANSPORT self + -- @param Tasking.Mission#MISSION Mission + -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param #string TaskName The name of the Task. + -- @param Core.Set#SET_CARGO SetCargo The scope of the cargo to be transported. + -- @param #string TaskBriefing The Cargo Task briefing. + -- @return #TASK_CARGO_TRANSPORT self + function TASK_CARGO_TRANSPORT:New( Mission, SetGroup, TaskName, SetCargo, TaskBriefing ) + local self = BASE:Inherit( self, TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, "Transport", TaskBriefing ) ) -- #TASK_CARGO_TRANSPORT + self:F() + + Mission:AddTask( self ) + + + -- Events + + self:AddTransition( "*", "CargoPickedUp", "*" ) + self:AddTransition( "*", "CargoDeployed", "*" ) + + self:F( { CargoDeployed = self.CargoDeployed ~= nil and "true" or "false" } ) + + --- OnBefore Transition Handler for Event CargoPickedUp. + -- @function [parent=#TASK_CARGO_TRANSPORT] OnBeforeCargoPickedUp + -- @param #TASK_CARGO_TRANSPORT self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event CargoPickedUp. + -- @function [parent=#TASK_CARGO_TRANSPORT] OnAfterCargoPickedUp + -- @param #TASK_CARGO_TRANSPORT self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + + --- Synchronous Event Trigger for Event CargoPickedUp. + -- @function [parent=#TASK_CARGO_TRANSPORT] CargoPickedUp + -- @param #TASK_CARGO_TRANSPORT self + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + + --- Asynchronous Event Trigger for Event CargoPickedUp. + -- @function [parent=#TASK_CARGO_TRANSPORT] __CargoPickedUp + -- @param #TASK_CARGO_TRANSPORT self + -- @param #number Delay The delay in seconds. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + + --- OnBefore Transition Handler for Event CargoDeployed. + -- @function [parent=#TASK_CARGO_TRANSPORT] OnBeforeCargoDeployed + -- @param #TASK_CARGO_TRANSPORT self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event CargoDeployed. + -- @function [parent=#TASK_CARGO_TRANSPORT] OnAfterCargoDeployed + -- @param #TASK_CARGO_TRANSPORT self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. + + --- Synchronous Event Trigger for Event CargoDeployed. + -- @function [parent=#TASK_CARGO_TRANSPORT] CargoDeployed + -- @param #TASK_CARGO_TRANSPORT self + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. + + --- Asynchronous Event Trigger for Event CargoDeployed. + -- @function [parent=#TASK_CARGO_TRANSPORT] __CargoDeployed + -- @param #TASK_CARGO_TRANSPORT self + -- @param #number Delay The delay in seconds. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. + + local Fsm = self:GetUnitProcess() + + local CargoReport = REPORT:New( "Transport Cargo. The following cargo needs to be transported including initial positions:") + + SetCargo:ForEachCargo( + --- @param Core.Cargo#CARGO Cargo + function( Cargo ) + local CargoType = Cargo:GetType() + local CargoName = Cargo:GetName() + local CargoCoordinate = Cargo:GetCoordinate() + CargoReport:Add( string.format( '- "%s" (%s) at %s', CargoName, CargoType, CargoCoordinate:ToStringMGRS() ) ) + end + ) + + self:SetBriefing( + TaskBriefing or + CargoReport:Text() + ) + + + return self + end + + function TASK_CARGO_TRANSPORT:ReportOrder( ReportGroup ) + + return 0 + end + + + --- + -- @param #TASK_CARGO_TRANSPORT self + -- @return #boolean + function TASK_CARGO_TRANSPORT:IsAllCargoTransported() + + local CargoSet = self:GetCargoSet() + local Set = CargoSet:GetSet() + + local DeployZones = self:GetDeployZones() + + local CargoDeployed = true + + -- Loop the CargoSet (so evaluate each Cargo in the SET_CARGO ). + for CargoID, CargoData in pairs( Set ) do + local Cargo = CargoData -- Core.Cargo#CARGO + + self:F( { Cargo = Cargo:GetName(), CargoDeployed = Cargo:IsDeployed() } ) + + if Cargo:IsDeployed() then + +-- -- Loop the DeployZones set for the TASK_CARGO_TRANSPORT. +-- for DeployZoneID, DeployZone in pairs( DeployZones ) do +-- +-- -- If all cargo is in one of the deploy zones, then all is good. +-- self:T( { Cargo.CargoObject } ) +-- if Cargo:IsInZone( DeployZone ) == false then +-- CargoDeployed = false +-- end +-- end + else + CargoDeployed = false + end + end + + self:F( { CargoDeployed = CargoDeployed } ) + + return CargoDeployed + end + + --- @param #TASK_CARGO_TRANSPORT self + function TASK_CARGO_TRANSPORT:onafterGoal( TaskUnit, From, Event, To ) + local CargoSet = self.CargoSet + + if self:IsAllCargoTransported() then + self:Success() + end + + self:__Goal( -10 ) + end + +end + diff --git a/Moose Development/Moose/Tasking/Task_Manager.lua b/Moose Development/Moose/Tasking/Task_Manager.lua new file mode 100644 index 000000000..2bf10fea6 --- /dev/null +++ b/Moose Development/Moose/Tasking/Task_Manager.lua @@ -0,0 +1,150 @@ +--- This module contains the TASK_MANAGER class and derived classes. +-- +-- === +-- +-- 1) @{Task_Manager#TASK_MANAGER} class, extends @{Fsm#FSM} +-- === +-- The @{Task_Manager#TASK_MANAGER} class defines the core functions to report tasks to groups. +-- Reportings can be done in several manners, and it is up to the derived classes if TASK_MANAGER to model the reporting behaviour. +-- +-- 1.1) TASK_MANAGER constructor: +-- ----------------------------------- +-- * @{Task_Manager#TASK_MANAGER.New}(): Create a new TASK_MANAGER instance. +-- +-- 1.2) TASK_MANAGER reporting: +-- --------------------------------- +-- Derived TASK_MANAGER classes will manage tasks using the method @{Task_Manager#TASK_MANAGER.ManageTasks}(). This method implements polymorphic behaviour. +-- +-- The time interval in seconds of the task management can be changed using the methods @{Task_Manager#TASK_MANAGER.SetRefreshTimeInterval}(). +-- To control how long a reporting message is displayed, use @{Task_Manager#TASK_MANAGER.SetReportDisplayTime}(). +-- Derived classes need to implement the method @{Task_Manager#TASK_MANAGER.GetReportDisplayTime}() to use the correct display time for displayed messages during a report. +-- +-- Task management can be started and stopped using the methods @{Task_Manager#TASK_MANAGER.StartTasks}() and @{Task_Manager#TASK_MANAGER.StopTasks}() respectively. +-- If an ad-hoc report is requested, use the method @{Task_Manager#TASK_MANAGER#ManageTasks}(). +-- +-- The default task management interval is every 60 seconds. +-- +-- === +-- +-- ### Contributions: Mechanist, Prof_Hilactic, FlightControl - Concept & Testing +-- ### Author: FlightControl - Framework Design & Programming +-- +-- @module Task_Manager + +do -- TASK_MANAGER + + --- TASK_MANAGER class. + -- @type TASK_MANAGER + -- @field Set#SET_GROUP SetGroup The set of group objects containing players for which tasks are managed. + -- @extends Core.Fsm#FSM + TASK_MANAGER = { + ClassName = "TASK_MANAGER", + SetGroup = nil, + } + + --- TASK\_MANAGER constructor. + -- @param #TASK_MANAGER self + -- @param Set#SET_GROUP SetGroup The set of group objects containing players for which tasks are managed. + -- @return #TASK_MANAGER self + function TASK_MANAGER:New( SetGroup ) + + -- Inherits from BASE + local self = BASE:Inherit( self, FSM:New() ) -- #TASK_MANAGER + + self.SetGroup = SetGroup + + self:SetStartState( "Stopped" ) + self:AddTransition( "Stopped", "StartTasks", "Started" ) + + --- StartTasks Handler OnBefore for TASK_MANAGER + -- @function [parent=#TASK_MANAGER] OnBeforeStartTasks + -- @param #TASK_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- StartTasks Handler OnAfter for TASK_MANAGER + -- @function [parent=#TASK_MANAGER] OnAfterStartTasks + -- @param #TASK_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- StartTasks Trigger for TASK_MANAGER + -- @function [parent=#TASK_MANAGER] StartTasks + -- @param #TASK_MANAGER self + + --- StartTasks Asynchronous Trigger for TASK_MANAGER + -- @function [parent=#TASK_MANAGER] __StartTasks + -- @param #TASK_MANAGER self + -- @param #number Delay + + + + self:AddTransition( "Started", "StopTasks", "Stopped" ) + + --- StopTasks Handler OnBefore for TASK_MANAGER + -- @function [parent=#TASK_MANAGER] OnBeforeStopTasks + -- @param #TASK_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- StopTasks Handler OnAfter for TASK_MANAGER + -- @function [parent=#TASK_MANAGER] OnAfterStopTasks + -- @param #TASK_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- StopTasks Trigger for TASK_MANAGER + -- @function [parent=#TASK_MANAGER] StopTasks + -- @param #TASK_MANAGER self + + --- StopTasks Asynchronous Trigger for TASK_MANAGER + -- @function [parent=#TASK_MANAGER] __StopTasks + -- @param #TASK_MANAGER self + -- @param #number Delay + + + self:AddTransition( "Started", "Manage", "Started" ) + + self:SetRefreshTimeInterval( 30 ) + + return self + end + + function TASK_MANAGER:onafterStartTasks( From, Event, To ) + self:Report() + end + + function TASK_MANAGER:onafterManage( From, Event, To ) + + self:__Manage( -self._RefreshTimeInterval ) + + self:ManageTasks() + end + + --- Set the refresh time interval in seconds when a new task management action needs to be done. + -- @param #TASK_MANAGER self + -- @param #number RefreshTimeInterval The refresh time interval in seconds when a new task management action needs to be done. + -- @return #TASK_MANAGER self + function TASK_MANAGER:SetRefreshTimeInterval( RefreshTimeInterval ) + self:F2() + + self._RefreshTimeInterval = RefreshTimeInterval + end + + + --- Manages the tasks for the @{Set#SET_GROUP}. + -- @param #TASK_MANAGER self + -- @return #TASK_MANAGER self + function TASK_MANAGER:ManageTasks() + self:E() + + end + +end + diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index ebc56985c..d1328835d 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -76,12 +76,16 @@ Tasking/CommandCenter.lua Tasking/Mission.lua Tasking/Task.lua Tasking/TaskInfo.lua +Tasking/Task_Manager.lua Tasking/DetectionManager.lua Tasking/Task_A2G_Dispatcher.lua Tasking/Task_A2G.lua Tasking/Task_A2A_Dispatcher.lua Tasking/Task_A2A.lua Tasking/Task_Cargo.lua +Tasking/Task_Cargo_Transport.lua +Tasking/Task_Cargo_CSAR.lua +Tasking/Task_Cargo_Dispatcher.lua Tasking/TaskZoneCapture.lua Moose.lua From a4d3089fdb6e204d9fd4a49d91dd852835854c88 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sat, 31 Mar 2018 09:10:11 +0200 Subject: [PATCH 021/420] Updates Csar --- Moose Development/Moose/Cargo/Cargo.lua | 11 ++++ .../Moose/Tasking/Task_CARGO.lua | 5 +- .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 54 ++++++++++++++++--- .../Moose/Tasking/Task_Manager.lua | 2 +- 4 files changed, 61 insertions(+), 11 deletions(-) diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index e6b3bde6e..07ee37a6d 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -277,6 +277,17 @@ do -- CARGO return self end + + --- Find a CARGO in the _DATABASE. + -- @param #CARGO self + -- @param #string CargoName The Cargo Name. + -- @return #CARGO self + function CARGO:FindByName( CargoName ) + + local CargoFound = _DATABASE:FindCargo( CargoName ) + return CargoFound + end + --- Destroy the cargo. -- @param #CARGO self function CARGO:Destroy() diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index c9127d3fe..d59473047 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -727,10 +727,11 @@ do -- TASK_CARGO --- @param #TASK_CARGO self -- @param #list DeployZones + -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK_CARGO - function TASK_CARGO:SetDeployZones( DeployZones ) + function TASK_CARGO:SetDeployZones( DeployZones, TaskUnit ) - for DeployZoneID, DeployZone in pairs( DeployZones ) do + for DeployZoneID, DeployZone in pairs( DeployZones or {} ) do self.DeployZones[DeployZone:GetName()] = DeployZone end diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index 3b22c9e01..7e07deb90 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -205,7 +205,7 @@ do -- TASK_CARGO_DISPATCHER -- @param #string PlayerName self:SetCSARRadius() - self:__Start( 5 ) + self:__StartTasks( 5 ) -- For CSAR missions, we process the event when a pilot ejects. @@ -221,14 +221,52 @@ do -- TASK_CARGO_DISPATCHER function TASK_CARGO_DISPATCHER:OnEventEjection( EventData ) self:E( { EventData = EventData } ) - - local PilotUnit = EventData.IniUnit + + local PlaneUnit = EventData.IniUnit local CSARName = EventData.IniUnitName - self.CSAR[CSARName] = {} - self.CSAR[CSARName].PilotUnit = PilotUnit - self.CSAR[CSARName].Task = nil + local PilotUnit = nil + + self:ScheduleOnce( 1, + function() + + -- Search for the ejected pilot + + local PlaneCoord = PlaneUnit:GetCoordinate() + + local SphereSearch = { + id = world.VolumeType.SPHERE, + params = { + point = PlaneCoord:GetVec3(), + radius = 100, + } + + } + + --- @param Dcs.DCSWrapper.Unit#Unit FoundDCSUnit + -- @param Wrapper.Group#GROUP ReportGroup + -- @param Set#SET_GROUP ReportSetGroup + local FindEjectedPilot = function( FoundDCSUnit ) + + local UnitName = FoundDCSUnit:getName() + + self:E( { "Units near Plane:", UnitName } ) + + PilotUnit = UNIT:Register( UnitName ) + + return true + end + + world.searchObjects( { Object.Category.UNIT, Object.Category.STATIC, Object.Category.SCENERY, Object.Category.WEAPON }, SphereSearch, FindEjectedPilot ) + + self.CSAR[CSARName] = {} + self.CSAR[CSARName].PilotUnit = PlaneUnit + self.CSAR[CSARName].Task = nil + end + ) + + return self end @@ -247,7 +285,7 @@ do -- TASK_CARGO_DISPATCHER -- function TASK_CARGO_DISPATCHER:SetCSARRadius( CSARRadius ) - self.Detection:SetFriendliesRange( CSARRadius or 50000 ) + self.CSARRadius = CSARRadius or 50000 return self end @@ -336,7 +374,7 @@ do -- TASK_CARGO_DISPATCHER -- New CSAR Task local SetCargo = self:EvaluateCSAR( CSARData.PilotUnit ) local CSARTask = TASK_CARGO_CSAR:New( Mission, self.SetGroup, "Rescue Pilot", SetCargo ) - CSARTask:SetDeployZones( { self.CSARDeployZones } ) + CSARTask:SetDeployZones( self.CSARDeployZones or {} ) Mission:AddTask( CSARTask ) TaskReport:Add( CSARTask:GetName() ) end diff --git a/Moose Development/Moose/Tasking/Task_Manager.lua b/Moose Development/Moose/Tasking/Task_Manager.lua index 2bf10fea6..340fd8233 100644 --- a/Moose Development/Moose/Tasking/Task_Manager.lua +++ b/Moose Development/Moose/Tasking/Task_Manager.lua @@ -117,7 +117,7 @@ do -- TASK_MANAGER end function TASK_MANAGER:onafterStartTasks( From, Event, To ) - self:Report() + self:Manage() end function TASK_MANAGER:onafterManage( From, Event, To ) From d0925ddfc189bbab0944df2b359a476de90b5787 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 2 Apr 2018 09:29:51 +0200 Subject: [PATCH 022/420] Progress --- Moose Development/Moose/Core/Spawn.lua | 249 ++++++++++++++++-- .../Moose/Tasking/Task_Cargo_CSAR.lua | 2 +- .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 71 +++-- 3 files changed, 251 insertions(+), 71 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 5342a6e92..1e4640a62 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -288,20 +288,20 @@ function SPAWN:New( SpawnTemplatePrefix ) self.SpawnIndex = 0 self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart. self.AliveUnits = 0 -- Contains the counter how many units are currently alive - self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not. + self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not. self.SpawnTemplate = self._GetTemplate( self, SpawnTemplatePrefix ) -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!! - self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning. - self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts. - self.SpawnInitLimit = false -- By default, no InitLimit - self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time. - self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned. - self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false. - self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned. - self.AIOnOff = true -- The AI is on by default when spawning a group. + self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning. + self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts. + self.SpawnInitLimit = false -- By default, no InitLimit + self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time. + self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned. + self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false. + self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned. + self.AIOnOff = true -- The AI is on by default when spawning a group. self.SpawnUnControlled = false - self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. - self.DelayOnOff = false -- No intial delay when spawning the first group. - self.Grouping = nil -- No grouping + self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. + self.DelayOnOff = false -- No intial delay when spawning the first group. + self.Grouping = nil -- No grouping self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. else @@ -334,19 +334,19 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix ) self.SpawnIndex = 0 self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart. self.AliveUnits = 0 -- Contains the counter how many units are currently alive - self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not. + self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not. self.SpawnTemplate = self._GetTemplate( self, SpawnTemplatePrefix ) -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!! - self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning. - self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts. - self.SpawnInitLimit = false -- By default, no InitLimit - self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time. - self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned. - self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false. - self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned. - self.AIOnOff = true -- The AI is on by default when spawning a group. + self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning. + self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts. + self.SpawnInitLimit = false -- By default, no InitLimit + self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time. + self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned. + self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false. + self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned. + self.AIOnOff = true -- The AI is on by default when spawning a group. self.SpawnUnControlled = false - self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. - self.DelayOnOff = false -- No intial delay when spawning the first group. + self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. + self.DelayOnOff = false -- No intial delay when spawning the first group. self.Grouping = nil self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. @@ -361,6 +361,55 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix ) end +--- Creates a new SPAWN instance to create new groups based on the provided template. +-- @param #SPAWN self +-- @param #table SpawnTemplate is the Template of the Group. This must be a valid Group Template structure! +-- @param #string SpawnTemplatePrefix is the name of the Group that will be given at each spawn. +-- @param #string SpawnAliasPrefix (optional) is the name that will be given to the Group at runtime. +-- @return #SPAWN +-- @usage +-- -- Create a new SPAWN object based on a Group Template defined from scratch. +-- Spawn_BE_KA50 = SPAWN:NewWithAlias( 'BE KA-50@RAMP-Ground Defense', 'Helicopter Attacking a City' ) +-- @usage +-- -- Create a new CSAR_Spawn object based on a normal Group Template to spawn a soldier. +-- local CSAR_Spawn = SPAWN:NewWithFromTemplate( Template, "CSAR", "Pilot" ) +function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix ) + local self = BASE:Inherit( self, BASE:New() ) + self:F( { SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix } ) + + if SpawnTemplate then + self.SpawnTemplate = SpawnTemplate -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!! + self.SpawnTemplatePrefix = SpawnTemplatePrefix + self.SpawnAliasPrefix = SpawnAliasPrefix + self.SpawnIndex = 0 + self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart. + self.AliveUnits = 0 -- Contains the counter how many units are currently alive + self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not. + self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning. + self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts. + self.SpawnInitLimit = false -- By default, no InitLimit. + self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time. + self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned. + self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false. + self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned. + self.AIOnOff = true -- The AI is on by default when spawning a group. + self.SpawnUnControlled = false + self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. + self.DelayOnOff = false -- No intial delay when spawning the first group. + self.Grouping = nil + + self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. + else + error( "There is no template provided for SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" ) + end + + self:SetEventPriority( 5 ) + self.SpawnHookScheduler = SCHEDULER:New( nil ) + + return self +end + + --- Limits the Maximum amount of Units that can be alive at the same time, and the maximum amount of groups that can be spawned. -- Note that this method is exceptionally important to balance the performance of the mission. Depending on the machine etc, a mission can only process a maximum amount of units. -- If the time interval must be short, but there should not be more Units or Groups alive than a maximum amount of units, then this method should be used... @@ -404,6 +453,57 @@ function SPAWN:InitKeepUnitNames() return self end +--- Defines the Heading for the new spawned units. +-- The heading can be given as one fixed degree, or can be randomized between minimum and maximum degrees. +-- @param #SPAWN self +-- @param #number HeadingMin The minimum or fixed heading in degrees. +-- @param #number HeadingMax (optional) The maximum heading in degrees. This there is no maximum heading, then the heading will be fixed for all units using minimum heading. +-- @return #SPAWN self +-- @usage +-- +-- Spawn = SPAWN:New( ... ) +-- +-- -- Spawn the units pointing to 100 degrees. +-- Spawn:InitHeading( 100 ) +-- +-- -- Spawn the units pointing between 100 and 150 degrees. +-- Spawn:InitHeading( 100, 150 ) +-- +function SPAWN:InitHeading( HeadingMin, HeadingMax ) + self:F( ) + + self.SpawnInitHeadingMin = HeadingMin + self.SpawnInitHeadingMax = HeadingMax + + return self +end + + +function SPAWN:InitCoalition( Coalition ) + self:F( ) + + self.SpawnInitCoalition = Coalition + + return self +end + + +function SPAWN:InitCountry( Country ) + self:F( ) + + self.SpawnInitCountry = Country + + return self +end + + +function SPAWN:InitCategory( Category ) + self:F( ) + + self.SpawnInitCategory = Category + + return self +end --- Randomizes the defined route of the SpawnTemplatePrefix group in the ME. This is very useful to define extra variation of the behaviour of groups. -- @param #SPAWN self @@ -941,6 +1041,18 @@ function SPAWN:SpawnWithIndex( SpawnIndex ) end end + -- If Heading is given, point all the units towards the given Heading. + if self.SpawnInitHeadingMin then + for UnitID = 1, #SpawnTemplate.units do + SpawnTemplate.units[UnitID].heading = self.SpawnInitHeadingMax and math.random( self.SpawnInitHeadingMin, self.SpawnInitHeadingMax ) or self.SpawnInitHeadingMin + end + end + + SpawnTemplate.CategoryID = self.SpawnInitCategory or SpawnTemplate.CategoryID + SpawnTemplate.CountryID = self.SpawnInitCountry or SpawnTemplate.CountryID + SpawnTemplate.CoalitionID = self.SpawnInitCoalition or SpawnTemplate.CoalitionID + + if SpawnTemplate.CategoryID == Group.Category.HELICOPTER or SpawnTemplate.CategoryID == Group.Category.AIRPLANE then if SpawnTemplate.route.points[1].type == "TakeOffParking" then SpawnTemplate.uncontrolled = self.SpawnUnControlled @@ -1247,14 +1359,20 @@ function SPAWN:SpawnFromVec3( Vec3, SpawnIndex ) self:T( { "Current point of ", self.SpawnTemplatePrefix, Vec3 } ) - local TemplateHeight = SpawnTemplate.route.points[1].alt + local TemplateHeight = SpawnTemplate.route and SpawnTemplate.route.points[1].alt or nil + + SpawnTemplate.route = SpawnTemplate.route or {} + SpawnTemplate.route.points = SpawnTemplate.route.points or {} + SpawnTemplate.route.points[1] = SpawnTemplate.route.points[1] or {} + SpawnTemplate.route.points[1].x = SpawnTemplate.route.points[1].x or 0 + SpawnTemplate.route.points[1].y = SpawnTemplate.route.points[1].y or 0 -- Translate the position of the Group Template to the Vec3. for UnitID = 1, #SpawnTemplate.units do - self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y ) + --self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y ) local UnitTemplate = SpawnTemplate.units[UnitID] - local SX = UnitTemplate.x - local SY = UnitTemplate.y + local SX = UnitTemplate.x or 0 + local SY = UnitTemplate.y or 0 local BX = SpawnTemplate.route.points[1].x local BY = SpawnTemplate.route.points[1].y local TX = Vec3.x + ( SX - BX ) @@ -1266,7 +1384,6 @@ function SPAWN:SpawnFromVec3( Vec3, SpawnIndex ) end self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y ) end - SpawnTemplate.route.points[1].x = Vec3.x SpawnTemplate.route.points[1].y = Vec3.z if SpawnTemplate.CategoryID ~= Group.Category.SHIP then @@ -1283,6 +1400,47 @@ function SPAWN:SpawnFromVec3( Vec3, SpawnIndex ) return nil end + +--- Will spawn a group from a Coordinate in 3D space. +-- This method is mostly advisable to be used if you want to simulate spawning units in the air, like helicopters or airplanes. +-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. +-- You can use the returned group to further define the route to be followed. +-- @param #SPAWN self +-- @param Core.Point#Coordinate Coordinate The Coordinate coordinates where to spawn the group. +-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. +-- @return Wrapper.Group#GROUP that was spawned. +-- @return #nil Nothing was spawned. +function SPAWN:SpawnFromCoordinate( Coordinate, SpawnIndex ) + self:F( { self.SpawnTemplatePrefix, SpawnIndex } ) + + return self:SpawnFromVec3( Coordinate:GetVec3(), SpawnIndex ) +end + + + +--- Will spawn a group from a PointVec3 in 3D space. +-- This method is mostly advisable to be used if you want to simulate spawning units in the air, like helicopters or airplanes. +-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. +-- You can use the returned group to further define the route to be followed. +-- @param #SPAWN self +-- @param Core.Point#POINT_VEC3 PointVec3 The PointVec3 coordinates where to spawn the group. +-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. +-- @return Wrapper.Group#GROUP that was spawned. +-- @return #nil Nothing was spawned. +-- @usage +-- +-- local SpawnPointVec3 = ZONE:New( ZoneName ):GetPointVec3( 2000 ) -- Get the center of the ZONE object at 2000 meters from the ground. +-- +-- -- Spawn at the zone center position at 2000 meters from the ground! +-- SpawnAirplanes:SpawnFromPointVec3( SpawnPointVec3 ) +-- +function SPAWN:SpawnFromPointVec3( PointVec3, SpawnIndex ) + self:F( { self.SpawnTemplatePrefix, SpawnIndex } ) + + return self:SpawnFromVec3( PointVec3:GetVec3(), SpawnIndex ) +end + + --- Will spawn a group from a Vec2 in 3D space. -- This method is mostly advisable to be used if you want to simulate spawning groups on the ground from air units, like vehicles. -- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. @@ -1317,6 +1475,35 @@ function SPAWN:SpawnFromVec2( Vec2, MinHeight, MaxHeight, SpawnIndex ) end +--- Will spawn a group from a POINT_VEC2 in 3D space. +-- This method is mostly advisable to be used if you want to simulate spawning groups on the ground from air units, like vehicles. +-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. +-- You can use the returned group to further define the route to be followed. +-- @param #SPAWN self +-- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 coordinates where to spawn the group. +-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone. +-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone. +-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. +-- @return Wrapper.Group#GROUP that was spawned. +-- @return #nil Nothing was spawned. +-- @usage +-- +-- local SpawnPointVec2 = ZONE:New( ZoneName ):GetPointVec2() +-- +-- -- Spawn at the zone center position at the height specified in the ME of the group template! +-- SpawnAirplanes:SpawnFromPointVec2( SpawnPointVec2 ) +-- +-- -- Spawn from the static position at the height randomized between 2000 and 4000 meters. +-- SpawnAirplanes:SpawnFromPointVec2( SpawnPointVec2, 2000, 4000 ) +-- +function SPAWN:SpawnFromPointVec2( PointVec2, MinHeight, MaxHeight, SpawnIndex ) + self:F( { self.SpawnTemplatePrefix, self.SpawnIndex } ) + + return self:SpawnFromVec2( PointVec2:GetVec2(), MinHeight, MaxHeight, SpawnIndex ) +end + + + --- Will spawn a group from a hosting unit. This method is mostly advisable to be used if you want to simulate spawning from air units, like helicopters, which are dropping infantry into a defined Landing Zone. -- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. -- You can use the returned group to further define the route to be followed. @@ -1715,7 +1902,11 @@ end function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) --R2.2 self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix } ) - local SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix ) + if not self.SpawnTemplate then + self.SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix ) + end + + local SpawnTemplate = self.SpawnTemplate SpawnTemplate.name = self:SpawnGroupName( SpawnIndex ) SpawnTemplate.groupId = nil diff --git a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua index b45dc7e26..b2e2aebdb 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua @@ -23,7 +23,7 @@ do -- TASK_CARGO_CSAR -- @param #string TaskBriefing The Cargo Task briefing. -- @return #TASK_CARGO_CSAR self function TASK_CARGO_CSAR:New( Mission, SetGroup, TaskName, SetCargo, TaskBriefing ) - local self = BASE:Inherit( self, TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, "Transport", TaskBriefing ) ) -- #TASK_CARGO_CSAR + local self = BASE:Inherit( self, TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, "CSAR", TaskBriefing ) ) -- #TASK_CARGO_CSAR self:F() Mission:AddTask( self ) diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index 7e07deb90..3832d4a7a 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -210,6 +210,26 @@ do -- TASK_CARGO_DISPATCHER -- For CSAR missions, we process the event when a pilot ejects. self:HandleEvent( EVENTS.Ejection ) + + -- Create the CSAR Pilot SPAWN object. + -- Let us create the Template for the replacement Pilot :-) + local Template = { + ["visible"] = false, + ["taskSelected"] = true, + ["hidden"] = false, + ["units"] = + { + [1] = + { + ["type"] = "Soldier M4", + ["skill"] = "Excellent", + ["playerCanDrive"] = false, + }, -- end of [1] + }, -- end of ["units"] + ["task"] = "Ground Nothing", + } + + self.PilotSpawn = SPAWN:NewFromTemplate( Template, "CSAR Pilot" ) return self end @@ -225,47 +245,16 @@ do -- TASK_CARGO_DISPATCHER local PlaneUnit = EventData.IniUnit local CSARName = EventData.IniUnitName - local PilotUnit = nil - - self:ScheduleOnce( 1, - function() - - -- Search for the ejected pilot - - local PlaneCoord = PlaneUnit:GetCoordinate() - - local SphereSearch = { - id = world.VolumeType.SPHERE, - params = { - point = PlaneCoord:GetVec3(), - radius = 100, - } - - } - - --- @param Dcs.DCSWrapper.Unit#Unit FoundDCSUnit - -- @param Wrapper.Group#GROUP ReportGroup - -- @param Set#SET_GROUP ReportSetGroup - local FindEjectedPilot = function( FoundDCSUnit ) - - local UnitName = FoundDCSUnit:getName() - - self:E( { "Units near Plane:", UnitName } ) - - PilotUnit = UNIT:Register( UnitName ) - - return true - end - - world.searchObjects( { Object.Category.UNIT, Object.Category.STATIC, Object.Category.SCENERY, Object.Category.WEAPON }, SphereSearch, FindEjectedPilot ) + local PointVec2Spawn = EventData.IniUnit:GetPointVec2() - self.CSAR[CSARName] = {} - self.CSAR[CSARName].PilotUnit = PlaneUnit - self.CSAR[CSARName].Task = nil - - end - ) - + self.PilotSpawn:InitHeading( EventData.IniUnit:GetHeading() ) -- This will ensure that the new pilot will point towards the same heading as the plane. + self.PilotSpawn:InitCategory( Group.Category.GROUND ) + self.PilotSpawn:InitCountry( EventData.IniUnit:GetCountry() ) + self.PilotSpawn:InitCoalition( EventData.IniUnit:GetCoalition() ) + + self.CSAR[#self.CSAR+1] = {} + self.CSAR[#self.CSAR].PilotUnit = self.PilotSpawn:SpawnFromPointVec2( PointVec2Spawn ) + self.CSAR[#self.CSAR].Task = nil return self end @@ -373,7 +362,7 @@ do -- TASK_CARGO_DISPATCHER else -- New CSAR Task local SetCargo = self:EvaluateCSAR( CSARData.PilotUnit ) - local CSARTask = TASK_CARGO_CSAR:New( Mission, self.SetGroup, "Rescue Pilot", SetCargo ) + local CSARTask = TASK_CARGO_CSAR:New( Mission, self.SetGroup, string.format( "CSAR.%03d", CSARID ), SetCargo ) CSARTask:SetDeployZones( self.CSARDeployZones or {} ) Mission:AddTask( CSARTask ) TaskReport:Add( CSARTask:GetName() ) From 25831db0571e07aefef31f30fed07b12f9bb0962 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 2 Apr 2018 19:33:44 +0200 Subject: [PATCH 023/420] Progress -- Still need to fix an issue with some strang objects appearing and a crash. --- Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index 3832d4a7a..5795b947b 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -253,7 +253,7 @@ do -- TASK_CARGO_DISPATCHER self.PilotSpawn:InitCoalition( EventData.IniUnit:GetCoalition() ) self.CSAR[#self.CSAR+1] = {} - self.CSAR[#self.CSAR].PilotUnit = self.PilotSpawn:SpawnFromPointVec2( PointVec2Spawn ) + self.CSAR[#self.CSAR].PilotGroup = self.PilotSpawn:SpawnFromPointVec2( PointVec2Spawn ) self.CSAR[#self.CSAR].Task = nil return self @@ -310,7 +310,7 @@ do -- TASK_CARGO_DISPATCHER -- @return #nil If there is no CSAR task required. function TASK_CARGO_DISPATCHER:EvaluateCSAR( CSARUnit ) - local CSARCargo = CARGO_UNIT:New( CSARUnit, "Pilot", CSARUnit:GetName(), 80, 1500, 10 ) + local CSARCargo = CARGO_GROUP:New( CSARUnit, "Pilot", CSARUnit:GetName(), 80, 1500, 10 ) local SetCargo = SET_CARGO:New() SetCargo:AddCargosByName( CSARUnit:GetName() ) @@ -361,7 +361,8 @@ do -- TASK_CARGO_DISPATCHER if CSARData.Task then else -- New CSAR Task - local SetCargo = self:EvaluateCSAR( CSARData.PilotUnit ) + self:F( { PilotGroup = CSARData.PilotGroup } ) + local SetCargo = self:EvaluateCSAR( CSARData.PilotGroup ) local CSARTask = TASK_CARGO_CSAR:New( Mission, self.SetGroup, string.format( "CSAR.%03d", CSARID ), SetCargo ) CSARTask:SetDeployZones( self.CSARDeployZones or {} ) Mission:AddTask( CSARTask ) From ec973fcc76944a5c012aee5a98349f8edb02b757 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Tue, 3 Apr 2018 07:43:55 +0200 Subject: [PATCH 024/420] Working CSAR ... huray --- .../Moose/Dcs/DCSCoalitionObject.lua | 2 +- Moose Development/Moose/Dcs/DCScoalition.lua | 2 + .../Moose/Tasking/CommandCenter.lua | 8 ++ Moose Development/Moose/Tasking/Mission.lua | 4 +- .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 74 +++++++++++-------- Moose Development/Moose/Wrapper/Group.lua | 2 +- 6 files changed, 58 insertions(+), 34 deletions(-) diff --git a/Moose Development/Moose/Dcs/DCSCoalitionObject.lua b/Moose Development/Moose/Dcs/DCSCoalitionObject.lua index be7dc106b..119c9a807 100644 --- a/Moose Development/Moose/Dcs/DCSCoalitionObject.lua +++ b/Moose Development/Moose/Dcs/DCSCoalitionObject.lua @@ -4,7 +4,7 @@ --- @type CoalitionObject -- @extends Dcs.DCSWrapper.Object#Object -coalition = {} --#coalition + --- Returns coalition of the object. -- @function [parent=#CoalitionObject] getCoalition diff --git a/Moose Development/Moose/Dcs/DCScoalition.lua b/Moose Development/Moose/Dcs/DCScoalition.lua index 4ec5f6f10..e51508a3e 100644 --- a/Moose Development/Moose/Dcs/DCScoalition.lua +++ b/Moose Development/Moose/Dcs/DCScoalition.lua @@ -12,3 +12,5 @@ --- @function [parent=#coalition] getCountryCoalition -- @param #number countryId -- @return #number coalitionId + +coalition = coalition -- #coalition diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index e8e4f1d32..e521f63df 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -220,6 +220,14 @@ function COMMANDCENTER:GetShortText() end +--- Gets the coalition of the command center. +-- @param #COMMANDCENTER self +-- @return DCScoalition#coalition +function COMMANDCENTER:GetCoalition() + + return self.CommandCenterCoalition +end + --- Gets the POSITIONABLE of the HQ command center. -- @param #COMMANDCENTER self diff --git a/Moose Development/Moose/Tasking/Mission.lua b/Moose Development/Moose/Tasking/Mission.lua index 06a7e7d9b..441b463aa 100644 --- a/Moose Development/Moose/Tasking/Mission.lua +++ b/Moose Development/Moose/Tasking/Mission.lua @@ -29,7 +29,7 @@ MISSION = { -- @param #string MissionName is the name of the mission. This name will be used to reference the status of each mission by the players. -- @param #string MissionPriority is a string indicating the "priority" of the Mission. f.e. "Primary", "Secondary" or "First", "Second". It is free format and up to the Mission designer to choose. There are no rules behind this field. -- @param #string MissionBriefing is a string indicating the mission briefing to be shown when a player joins a @{CLIENT}. --- @param Dcs.DCSCoalitionWrapper.Object#coalition MissionCoalition is a string indicating the coalition or party to which this mission belongs to. It is free format and can be chosen freely by the mission designer. Note that this field is not to be confused with the coalition concept of the ME. Examples of a Mission Coalition could be "NATO", "CCCP", "Intruders", "Terrorists"... +-- @param #string MissionCoalition is a string indicating the coalition or party to which this mission belongs to. It is free format and can be chosen freely by the mission designer. Note that this field is not to be confused with the coalition concept of the ME. Examples of a Mission Coalition could be "NATO", "CCCP", "Intruders", "Terrorists"... -- @return #MISSION self function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefing, MissionCoalition ) @@ -265,6 +265,8 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi end + + --- FSM function for a MISSION -- @param #MISSION self -- @param #string From diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index 5795b947b..df9c36dec 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -177,6 +177,7 @@ do -- TASK_CARGO_DISPATCHER Mission = nil, Tasks = {}, CSAR = {}, + CSARSpawned = 0, } @@ -210,26 +211,6 @@ do -- TASK_CARGO_DISPATCHER -- For CSAR missions, we process the event when a pilot ejects. self:HandleEvent( EVENTS.Ejection ) - - -- Create the CSAR Pilot SPAWN object. - -- Let us create the Template for the replacement Pilot :-) - local Template = { - ["visible"] = false, - ["taskSelected"] = true, - ["hidden"] = false, - ["units"] = - { - [1] = - { - ["type"] = "Soldier M4", - ["skill"] = "Excellent", - ["playerCanDrive"] = false, - }, -- end of [1] - }, -- end of ["units"] - ["task"] = "Ground Nothing", - } - - self.PilotSpawn = SPAWN:NewFromTemplate( Template, "CSAR Pilot" ) return self end @@ -242,19 +223,50 @@ do -- TASK_CARGO_DISPATCHER self:E( { EventData = EventData } ) + self.CSARSpawned = self.CSARSpawned + 1 + local PlaneUnit = EventData.IniUnit local CSARName = EventData.IniUnitName - local PointVec2Spawn = EventData.IniUnit:GetPointVec2() + local CargoPointVec2 = EventData.IniUnit:GetPointVec2() + local CargoCoalition = EventData.IniUnit:GetCoalition() + local CargoCountry = EventData.IniUnit:GetCountry() + + -- Only add a CSAR task if the coalition of the mission is equal to the coalition of the ejected unit. - self.PilotSpawn:InitHeading( EventData.IniUnit:GetHeading() ) -- This will ensure that the new pilot will point towards the same heading as the plane. - self.PilotSpawn:InitCategory( Group.Category.GROUND ) - self.PilotSpawn:InitCountry( EventData.IniUnit:GetCountry() ) - self.PilotSpawn:InitCoalition( EventData.IniUnit:GetCoalition() ) - - self.CSAR[#self.CSAR+1] = {} - self.CSAR[#self.CSAR].PilotGroup = self.PilotSpawn:SpawnFromPointVec2( PointVec2Spawn ) - self.CSAR[#self.CSAR].Task = nil + if CargoCoalition == self.Mission:GetCommandCenter():GetCoalition() then + + -- Create the CSAR Pilot SPAWN object. + -- Let us create the Template for the replacement Pilot :-) + local Template = { + ["visible"] = false, + ["hidden"] = false, + ["task"] = "Ground Nothing", + ["name"] = string.format( "CSAR Pilot#%03d", self.CSARSpawned ), + ["x"] = CargoPointVec2:GetLat(), + ["y"] = CargoPointVec2:GetLon(), + ["units"] = + { + [1] = + { + ["type"] = ( CargoCoalition == coalition.side.BLUE ) and "Soldier M4" or "Infantry AK", + ["name"] = string.format( "CSAR Pilot#%03d-01", self.CSARSpawned ), + ["skill"] = "Excellent", + ["playerCanDrive"] = false, + ["x"] = CargoPointVec2:GetLat(), + ["y"] = CargoPointVec2:GetLon(), + ["heading"] = EventData.IniUnit:GetHeading(), + }, -- end of [1] + }, -- end of ["units"] + } + + local CargoGroup = GROUP:NewTemplate( Template, CargoCoalition, Group.Category.GROUND, CargoCountry ) + + self.CSAR[#self.CSAR+1] = {} + self.CSAR[#self.CSAR].PilotGroup = CargoGroup + self.CSAR[#self.CSAR].Task = nil + + end return self end @@ -294,7 +306,7 @@ do -- TASK_CARGO_DISPATCHER --- Define the deploy zones for the CSAR tasks. -- @param #TASK_CARGO_DISPATCHER self - -- @param DeployZones A list of the deploy zones. + -- @param CSARDeployZones A list of the deploy zones. -- @return #TASK_CARGO_DISPATCHER function TASK_CARGO_DISPATCHER:SetCSARDeployZones( CSARDeployZones ) @@ -361,12 +373,12 @@ do -- TASK_CARGO_DISPATCHER if CSARData.Task then else -- New CSAR Task - self:F( { PilotGroup = CSARData.PilotGroup } ) local SetCargo = self:EvaluateCSAR( CSARData.PilotGroup ) local CSARTask = TASK_CARGO_CSAR:New( Mission, self.SetGroup, string.format( "CSAR.%03d", CSARID ), SetCargo ) CSARTask:SetDeployZones( self.CSARDeployZones or {} ) Mission:AddTask( CSARTask ) TaskReport:Add( CSARTask:GetName() ) + CSARData.Task = CSARTask end end diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 66d9068a3..26062ea09 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -121,7 +121,7 @@ GROUPTEMPLATE.Takeoff = { -- @return #GROUP self function GROUP:NewTemplate( GroupTemplate, CoalitionSide, CategoryID, CountryID ) local GroupName = GroupTemplate.name - _DATABASE:_RegisterGroupTemplate( GroupTemplate, CategoryID, CountryID, CoalitionSide, GroupName ) + _DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, CategoryID, CountryID, GroupName ) self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) self:F2( GroupName ) self.GroupName = GroupName From 1f578d4ab5d169cf74dce2390986e52885a0f61c Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Tue, 3 Apr 2018 07:45:26 +0200 Subject: [PATCH 025/420] Working CSAR ... huray --- Moose Development/Moose/Cargo/Cargo.lua | 11 + Moose Development/Moose/Core/Spawn.lua | 249 +++++++++-- .../Moose/Dcs/DCSCoalitionObject.lua | 2 +- Moose Development/Moose/Dcs/DCScoalition.lua | 2 + .../Moose/Tasking/CommandCenter.lua | 8 + Moose Development/Moose/Tasking/Mission.lua | 4 +- .../Moose/Tasking/Task_CARGO.lua | 4 +- .../Moose/Tasking/Task_Cargo_CSAR.lua | 187 ++++++++ .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 402 ++++++++++++++++++ .../Moose/Tasking/Task_Cargo_Transport.lua | 187 ++++++++ .../Moose/Tasking/Task_Manager.lua | 150 +++++++ Moose Development/Moose/Wrapper/Group.lua | 2 +- Moose Setup/Moose.files | 4 + 13 files changed, 1178 insertions(+), 34 deletions(-) create mode 100644 Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua create mode 100644 Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua create mode 100644 Moose Development/Moose/Tasking/Task_Cargo_Transport.lua create mode 100644 Moose Development/Moose/Tasking/Task_Manager.lua diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index e6b3bde6e..07ee37a6d 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -277,6 +277,17 @@ do -- CARGO return self end + + --- Find a CARGO in the _DATABASE. + -- @param #CARGO self + -- @param #string CargoName The Cargo Name. + -- @return #CARGO self + function CARGO:FindByName( CargoName ) + + local CargoFound = _DATABASE:FindCargo( CargoName ) + return CargoFound + end + --- Destroy the cargo. -- @param #CARGO self function CARGO:Destroy() diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 5342a6e92..1e4640a62 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -288,20 +288,20 @@ function SPAWN:New( SpawnTemplatePrefix ) self.SpawnIndex = 0 self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart. self.AliveUnits = 0 -- Contains the counter how many units are currently alive - self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not. + self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not. self.SpawnTemplate = self._GetTemplate( self, SpawnTemplatePrefix ) -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!! - self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning. - self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts. - self.SpawnInitLimit = false -- By default, no InitLimit - self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time. - self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned. - self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false. - self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned. - self.AIOnOff = true -- The AI is on by default when spawning a group. + self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning. + self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts. + self.SpawnInitLimit = false -- By default, no InitLimit + self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time. + self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned. + self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false. + self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned. + self.AIOnOff = true -- The AI is on by default when spawning a group. self.SpawnUnControlled = false - self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. - self.DelayOnOff = false -- No intial delay when spawning the first group. - self.Grouping = nil -- No grouping + self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. + self.DelayOnOff = false -- No intial delay when spawning the first group. + self.Grouping = nil -- No grouping self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. else @@ -334,19 +334,19 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix ) self.SpawnIndex = 0 self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart. self.AliveUnits = 0 -- Contains the counter how many units are currently alive - self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not. + self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not. self.SpawnTemplate = self._GetTemplate( self, SpawnTemplatePrefix ) -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!! - self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning. - self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts. - self.SpawnInitLimit = false -- By default, no InitLimit - self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time. - self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned. - self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false. - self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned. - self.AIOnOff = true -- The AI is on by default when spawning a group. + self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning. + self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts. + self.SpawnInitLimit = false -- By default, no InitLimit + self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time. + self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned. + self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false. + self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned. + self.AIOnOff = true -- The AI is on by default when spawning a group. self.SpawnUnControlled = false - self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. - self.DelayOnOff = false -- No intial delay when spawning the first group. + self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. + self.DelayOnOff = false -- No intial delay when spawning the first group. self.Grouping = nil self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. @@ -361,6 +361,55 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix ) end +--- Creates a new SPAWN instance to create new groups based on the provided template. +-- @param #SPAWN self +-- @param #table SpawnTemplate is the Template of the Group. This must be a valid Group Template structure! +-- @param #string SpawnTemplatePrefix is the name of the Group that will be given at each spawn. +-- @param #string SpawnAliasPrefix (optional) is the name that will be given to the Group at runtime. +-- @return #SPAWN +-- @usage +-- -- Create a new SPAWN object based on a Group Template defined from scratch. +-- Spawn_BE_KA50 = SPAWN:NewWithAlias( 'BE KA-50@RAMP-Ground Defense', 'Helicopter Attacking a City' ) +-- @usage +-- -- Create a new CSAR_Spawn object based on a normal Group Template to spawn a soldier. +-- local CSAR_Spawn = SPAWN:NewWithFromTemplate( Template, "CSAR", "Pilot" ) +function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix ) + local self = BASE:Inherit( self, BASE:New() ) + self:F( { SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPrefix } ) + + if SpawnTemplate then + self.SpawnTemplate = SpawnTemplate -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!! + self.SpawnTemplatePrefix = SpawnTemplatePrefix + self.SpawnAliasPrefix = SpawnAliasPrefix + self.SpawnIndex = 0 + self.SpawnCount = 0 -- The internal counter of the amount of spawning the has happened since SpawnStart. + self.AliveUnits = 0 -- Contains the counter how many units are currently alive + self.SpawnIsScheduled = false -- Reflects if the spawning for this SpawnTemplatePrefix is going to be scheduled or not. + self.Repeat = false -- Don't repeat the group from Take-Off till Landing and back Take-Off by ReSpawning. + self.UnControlled = false -- When working in UnControlled mode, all planes are Spawned in UnControlled mode before the scheduler starts. + self.SpawnInitLimit = false -- By default, no InitLimit. + self.SpawnMaxUnitsAlive = 0 -- The maximum amount of groups that can be alive of SpawnTemplatePrefix at the same time. + self.SpawnMaxGroups = 0 -- The maximum amount of groups that can be spawned. + self.SpawnRandomize = false -- Sets the randomization flag of new Spawned units to false. + self.SpawnVisible = false -- Flag that indicates if all the Groups of the SpawnGroup need to be visible when Spawned. + self.AIOnOff = true -- The AI is on by default when spawning a group. + self.SpawnUnControlled = false + self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. + self.DelayOnOff = false -- No intial delay when spawning the first group. + self.Grouping = nil + + self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. + else + error( "There is no template provided for SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" ) + end + + self:SetEventPriority( 5 ) + self.SpawnHookScheduler = SCHEDULER:New( nil ) + + return self +end + + --- Limits the Maximum amount of Units that can be alive at the same time, and the maximum amount of groups that can be spawned. -- Note that this method is exceptionally important to balance the performance of the mission. Depending on the machine etc, a mission can only process a maximum amount of units. -- If the time interval must be short, but there should not be more Units or Groups alive than a maximum amount of units, then this method should be used... @@ -404,6 +453,57 @@ function SPAWN:InitKeepUnitNames() return self end +--- Defines the Heading for the new spawned units. +-- The heading can be given as one fixed degree, or can be randomized between minimum and maximum degrees. +-- @param #SPAWN self +-- @param #number HeadingMin The minimum or fixed heading in degrees. +-- @param #number HeadingMax (optional) The maximum heading in degrees. This there is no maximum heading, then the heading will be fixed for all units using minimum heading. +-- @return #SPAWN self +-- @usage +-- +-- Spawn = SPAWN:New( ... ) +-- +-- -- Spawn the units pointing to 100 degrees. +-- Spawn:InitHeading( 100 ) +-- +-- -- Spawn the units pointing between 100 and 150 degrees. +-- Spawn:InitHeading( 100, 150 ) +-- +function SPAWN:InitHeading( HeadingMin, HeadingMax ) + self:F( ) + + self.SpawnInitHeadingMin = HeadingMin + self.SpawnInitHeadingMax = HeadingMax + + return self +end + + +function SPAWN:InitCoalition( Coalition ) + self:F( ) + + self.SpawnInitCoalition = Coalition + + return self +end + + +function SPAWN:InitCountry( Country ) + self:F( ) + + self.SpawnInitCountry = Country + + return self +end + + +function SPAWN:InitCategory( Category ) + self:F( ) + + self.SpawnInitCategory = Category + + return self +end --- Randomizes the defined route of the SpawnTemplatePrefix group in the ME. This is very useful to define extra variation of the behaviour of groups. -- @param #SPAWN self @@ -941,6 +1041,18 @@ function SPAWN:SpawnWithIndex( SpawnIndex ) end end + -- If Heading is given, point all the units towards the given Heading. + if self.SpawnInitHeadingMin then + for UnitID = 1, #SpawnTemplate.units do + SpawnTemplate.units[UnitID].heading = self.SpawnInitHeadingMax and math.random( self.SpawnInitHeadingMin, self.SpawnInitHeadingMax ) or self.SpawnInitHeadingMin + end + end + + SpawnTemplate.CategoryID = self.SpawnInitCategory or SpawnTemplate.CategoryID + SpawnTemplate.CountryID = self.SpawnInitCountry or SpawnTemplate.CountryID + SpawnTemplate.CoalitionID = self.SpawnInitCoalition or SpawnTemplate.CoalitionID + + if SpawnTemplate.CategoryID == Group.Category.HELICOPTER or SpawnTemplate.CategoryID == Group.Category.AIRPLANE then if SpawnTemplate.route.points[1].type == "TakeOffParking" then SpawnTemplate.uncontrolled = self.SpawnUnControlled @@ -1247,14 +1359,20 @@ function SPAWN:SpawnFromVec3( Vec3, SpawnIndex ) self:T( { "Current point of ", self.SpawnTemplatePrefix, Vec3 } ) - local TemplateHeight = SpawnTemplate.route.points[1].alt + local TemplateHeight = SpawnTemplate.route and SpawnTemplate.route.points[1].alt or nil + + SpawnTemplate.route = SpawnTemplate.route or {} + SpawnTemplate.route.points = SpawnTemplate.route.points or {} + SpawnTemplate.route.points[1] = SpawnTemplate.route.points[1] or {} + SpawnTemplate.route.points[1].x = SpawnTemplate.route.points[1].x or 0 + SpawnTemplate.route.points[1].y = SpawnTemplate.route.points[1].y or 0 -- Translate the position of the Group Template to the Vec3. for UnitID = 1, #SpawnTemplate.units do - self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y ) + --self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y ) local UnitTemplate = SpawnTemplate.units[UnitID] - local SX = UnitTemplate.x - local SY = UnitTemplate.y + local SX = UnitTemplate.x or 0 + local SY = UnitTemplate.y or 0 local BX = SpawnTemplate.route.points[1].x local BY = SpawnTemplate.route.points[1].y local TX = Vec3.x + ( SX - BX ) @@ -1266,7 +1384,6 @@ function SPAWN:SpawnFromVec3( Vec3, SpawnIndex ) end self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y ) end - SpawnTemplate.route.points[1].x = Vec3.x SpawnTemplate.route.points[1].y = Vec3.z if SpawnTemplate.CategoryID ~= Group.Category.SHIP then @@ -1283,6 +1400,47 @@ function SPAWN:SpawnFromVec3( Vec3, SpawnIndex ) return nil end + +--- Will spawn a group from a Coordinate in 3D space. +-- This method is mostly advisable to be used if you want to simulate spawning units in the air, like helicopters or airplanes. +-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. +-- You can use the returned group to further define the route to be followed. +-- @param #SPAWN self +-- @param Core.Point#Coordinate Coordinate The Coordinate coordinates where to spawn the group. +-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. +-- @return Wrapper.Group#GROUP that was spawned. +-- @return #nil Nothing was spawned. +function SPAWN:SpawnFromCoordinate( Coordinate, SpawnIndex ) + self:F( { self.SpawnTemplatePrefix, SpawnIndex } ) + + return self:SpawnFromVec3( Coordinate:GetVec3(), SpawnIndex ) +end + + + +--- Will spawn a group from a PointVec3 in 3D space. +-- This method is mostly advisable to be used if you want to simulate spawning units in the air, like helicopters or airplanes. +-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. +-- You can use the returned group to further define the route to be followed. +-- @param #SPAWN self +-- @param Core.Point#POINT_VEC3 PointVec3 The PointVec3 coordinates where to spawn the group. +-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. +-- @return Wrapper.Group#GROUP that was spawned. +-- @return #nil Nothing was spawned. +-- @usage +-- +-- local SpawnPointVec3 = ZONE:New( ZoneName ):GetPointVec3( 2000 ) -- Get the center of the ZONE object at 2000 meters from the ground. +-- +-- -- Spawn at the zone center position at 2000 meters from the ground! +-- SpawnAirplanes:SpawnFromPointVec3( SpawnPointVec3 ) +-- +function SPAWN:SpawnFromPointVec3( PointVec3, SpawnIndex ) + self:F( { self.SpawnTemplatePrefix, SpawnIndex } ) + + return self:SpawnFromVec3( PointVec3:GetVec3(), SpawnIndex ) +end + + --- Will spawn a group from a Vec2 in 3D space. -- This method is mostly advisable to be used if you want to simulate spawning groups on the ground from air units, like vehicles. -- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. @@ -1317,6 +1475,35 @@ function SPAWN:SpawnFromVec2( Vec2, MinHeight, MaxHeight, SpawnIndex ) end +--- Will spawn a group from a POINT_VEC2 in 3D space. +-- This method is mostly advisable to be used if you want to simulate spawning groups on the ground from air units, like vehicles. +-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. +-- You can use the returned group to further define the route to be followed. +-- @param #SPAWN self +-- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 coordinates where to spawn the group. +-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone. +-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone. +-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. +-- @return Wrapper.Group#GROUP that was spawned. +-- @return #nil Nothing was spawned. +-- @usage +-- +-- local SpawnPointVec2 = ZONE:New( ZoneName ):GetPointVec2() +-- +-- -- Spawn at the zone center position at the height specified in the ME of the group template! +-- SpawnAirplanes:SpawnFromPointVec2( SpawnPointVec2 ) +-- +-- -- Spawn from the static position at the height randomized between 2000 and 4000 meters. +-- SpawnAirplanes:SpawnFromPointVec2( SpawnPointVec2, 2000, 4000 ) +-- +function SPAWN:SpawnFromPointVec2( PointVec2, MinHeight, MaxHeight, SpawnIndex ) + self:F( { self.SpawnTemplatePrefix, self.SpawnIndex } ) + + return self:SpawnFromVec2( PointVec2:GetVec2(), MinHeight, MaxHeight, SpawnIndex ) +end + + + --- Will spawn a group from a hosting unit. This method is mostly advisable to be used if you want to simulate spawning from air units, like helicopters, which are dropping infantry into a defined Landing Zone. -- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. -- You can use the returned group to further define the route to be followed. @@ -1715,7 +1902,11 @@ end function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) --R2.2 self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix } ) - local SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix ) + if not self.SpawnTemplate then + self.SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix ) + end + + local SpawnTemplate = self.SpawnTemplate SpawnTemplate.name = self:SpawnGroupName( SpawnIndex ) SpawnTemplate.groupId = nil diff --git a/Moose Development/Moose/Dcs/DCSCoalitionObject.lua b/Moose Development/Moose/Dcs/DCSCoalitionObject.lua index be7dc106b..119c9a807 100644 --- a/Moose Development/Moose/Dcs/DCSCoalitionObject.lua +++ b/Moose Development/Moose/Dcs/DCSCoalitionObject.lua @@ -4,7 +4,7 @@ --- @type CoalitionObject -- @extends Dcs.DCSWrapper.Object#Object -coalition = {} --#coalition + --- Returns coalition of the object. -- @function [parent=#CoalitionObject] getCoalition diff --git a/Moose Development/Moose/Dcs/DCScoalition.lua b/Moose Development/Moose/Dcs/DCScoalition.lua index 4ec5f6f10..e51508a3e 100644 --- a/Moose Development/Moose/Dcs/DCScoalition.lua +++ b/Moose Development/Moose/Dcs/DCScoalition.lua @@ -12,3 +12,5 @@ --- @function [parent=#coalition] getCountryCoalition -- @param #number countryId -- @return #number coalitionId + +coalition = coalition -- #coalition diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index e8e4f1d32..e521f63df 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -220,6 +220,14 @@ function COMMANDCENTER:GetShortText() end +--- Gets the coalition of the command center. +-- @param #COMMANDCENTER self +-- @return DCScoalition#coalition +function COMMANDCENTER:GetCoalition() + + return self.CommandCenterCoalition +end + --- Gets the POSITIONABLE of the HQ command center. -- @param #COMMANDCENTER self diff --git a/Moose Development/Moose/Tasking/Mission.lua b/Moose Development/Moose/Tasking/Mission.lua index 06a7e7d9b..441b463aa 100644 --- a/Moose Development/Moose/Tasking/Mission.lua +++ b/Moose Development/Moose/Tasking/Mission.lua @@ -29,7 +29,7 @@ MISSION = { -- @param #string MissionName is the name of the mission. This name will be used to reference the status of each mission by the players. -- @param #string MissionPriority is a string indicating the "priority" of the Mission. f.e. "Primary", "Secondary" or "First", "Second". It is free format and up to the Mission designer to choose. There are no rules behind this field. -- @param #string MissionBriefing is a string indicating the mission briefing to be shown when a player joins a @{CLIENT}. --- @param Dcs.DCSCoalitionWrapper.Object#coalition MissionCoalition is a string indicating the coalition or party to which this mission belongs to. It is free format and can be chosen freely by the mission designer. Note that this field is not to be confused with the coalition concept of the ME. Examples of a Mission Coalition could be "NATO", "CCCP", "Intruders", "Terrorists"... +-- @param #string MissionCoalition is a string indicating the coalition or party to which this mission belongs to. It is free format and can be chosen freely by the mission designer. Note that this field is not to be confused with the coalition concept of the ME. Examples of a Mission Coalition could be "NATO", "CCCP", "Intruders", "Terrorists"... -- @return #MISSION self function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefing, MissionCoalition ) @@ -265,6 +265,8 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi end + + --- FSM function for a MISSION -- @param #MISSION self -- @param #string From diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 7d11cae8d..d59473047 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -726,12 +726,12 @@ do -- TASK_CARGO end --- @param #TASK_CARGO self - -- @param @list DeployZones + -- @param #list DeployZones -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK_CARGO function TASK_CARGO:SetDeployZones( DeployZones, TaskUnit ) - for DeployZoneID, DeployZone in pairs( DeployZones ) do + for DeployZoneID, DeployZone in pairs( DeployZones or {} ) do self.DeployZones[DeployZone:GetName()] = DeployZone end diff --git a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua new file mode 100644 index 000000000..b2e2aebdb --- /dev/null +++ b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua @@ -0,0 +1,187 @@ +--- **Tasking** -- Models tasks for players to execute CSAR @{Cargo} downed pilots. +-- +-- ![Banner Image](..\Presentations\TASK_CARGO\Dia1.JPG) +-- +-- === + + +do -- TASK_CARGO_CSAR + + --- The TASK_CARGO_CSAR class + -- @type TASK_CARGO_CSAR + -- @extends Tasking.Task_Cargo#TASK_CARGO + TASK_CARGO_CSAR = { + ClassName = "TASK_CARGO_CSAR", + } + + --- Instantiates a new TASK_CARGO_CSAR. + -- @param #TASK_CARGO_CSAR self + -- @param Tasking.Mission#MISSION Mission + -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param #string TaskName The name of the Task. + -- @param Core.Set#SET_CARGO SetCargo The scope of the cargo to be transported. + -- @param #string TaskBriefing The Cargo Task briefing. + -- @return #TASK_CARGO_CSAR self + function TASK_CARGO_CSAR:New( Mission, SetGroup, TaskName, SetCargo, TaskBriefing ) + local self = BASE:Inherit( self, TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, "CSAR", TaskBriefing ) ) -- #TASK_CARGO_CSAR + self:F() + + Mission:AddTask( self ) + + + -- Events + + self:AddTransition( "*", "CargoPickedUp", "*" ) + self:AddTransition( "*", "CargoDeployed", "*" ) + + self:F( { CargoDeployed = self.CargoDeployed ~= nil and "true" or "false" } ) + + --- OnBefore Transition Handler for Event CargoPickedUp. + -- @function [parent=#TASK_CARGO_CSAR] OnBeforeCargoPickedUp + -- @param #TASK_CARGO_CSAR self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event CargoPickedUp. + -- @function [parent=#TASK_CARGO_CSAR] OnAfterCargoPickedUp + -- @param #TASK_CARGO_CSAR self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + + --- Synchronous Event Trigger for Event CargoPickedUp. + -- @function [parent=#TASK_CARGO_CSAR] CargoPickedUp + -- @param #TASK_CARGO_CSAR self + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + + --- Asynchronous Event Trigger for Event CargoPickedUp. + -- @function [parent=#TASK_CARGO_CSAR] __CargoPickedUp + -- @param #TASK_CARGO_CSAR self + -- @param #number Delay The delay in seconds. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + + --- OnBefore Transition Handler for Event CargoDeployed. + -- @function [parent=#TASK_CARGO_CSAR] OnBeforeCargoDeployed + -- @param #TASK_CARGO_CSAR self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event CargoDeployed. + -- @function [parent=#TASK_CARGO_CSAR] OnAfterCargoDeployed + -- @param #TASK_CARGO_CSAR self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. + + --- Synchronous Event Trigger for Event CargoDeployed. + -- @function [parent=#TASK_CARGO_CSAR] CargoDeployed + -- @param #TASK_CARGO_CSAR self + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. + + --- Asynchronous Event Trigger for Event CargoDeployed. + -- @function [parent=#TASK_CARGO_CSAR] __CargoDeployed + -- @param #TASK_CARGO_CSAR self + -- @param #number Delay The delay in seconds. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. + + local Fsm = self:GetUnitProcess() + + local CargoReport = REPORT:New( "Rescue a downed pilot from the following position:") + + SetCargo:ForEachCargo( + --- @param Core.Cargo#CARGO Cargo + function( Cargo ) + local CargoType = Cargo:GetType() + local CargoName = Cargo:GetName() + local CargoCoordinate = Cargo:GetCoordinate() + CargoReport:Add( string.format( '- "%s" (%s) at %s', CargoName, CargoType, CargoCoordinate:ToStringMGRS() ) ) + end + ) + + self:SetBriefing( + TaskBriefing or + CargoReport:Text() + ) + + + return self + end + + function TASK_CARGO_CSAR:ReportOrder( ReportGroup ) + + return 0 + end + + + --- + -- @param #TASK_CARGO_CSAR self + -- @return #boolean + function TASK_CARGO_CSAR:IsAllCargoTransported() + + local CargoSet = self:GetCargoSet() + local Set = CargoSet:GetSet() + + local DeployZones = self:GetDeployZones() + + local CargoDeployed = true + + -- Loop the CargoSet (so evaluate each Cargo in the SET_CARGO ). + for CargoID, CargoData in pairs( Set ) do + local Cargo = CargoData -- Core.Cargo#CARGO + + self:F( { Cargo = Cargo:GetName(), CargoDeployed = Cargo:IsDeployed() } ) + + if Cargo:IsDeployed() then + +-- -- Loop the DeployZones set for the TASK_CARGO_CSAR. +-- for DeployZoneID, DeployZone in pairs( DeployZones ) do +-- +-- -- If all cargo is in one of the deploy zones, then all is good. +-- self:T( { Cargo.CargoObject } ) +-- if Cargo:IsInZone( DeployZone ) == false then +-- CargoDeployed = false +-- end +-- end + else + CargoDeployed = false + end + end + + self:F( { CargoDeployed = CargoDeployed } ) + + return CargoDeployed + end + + --- @param #TASK_CARGO_CSAR self + function TASK_CARGO_CSAR:onafterGoal( TaskUnit, From, Event, To ) + local CargoSet = self.CargoSet + + if self:IsAllCargoTransported() then + self:Success() + end + + self:__Goal( -10 ) + end + +end + diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua new file mode 100644 index 000000000..df9c36dec --- /dev/null +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -0,0 +1,402 @@ +--- **Tasking** - Creates and manages player TASK_CARGO tasks. +-- +-- === +-- +-- ### Author: **FlightControl** +-- +-- ### Contributions: +-- +-- === +-- +-- @module Task_Cargo_Dispatcher + +do -- TASK_CARGO_DISPATCHER + + --- TASK_CARGO_DISPATCHER class. + -- @type TASK_CARGO_DISPATCHER + -- @extends Tasking.Task_Manager#TASK_MANAGER + -- @field TASK_CARGO_DISPATCHER.CSAR CSAR + + --- @type TASK_CARGO_DISPATCHER.CSAR + -- @field Wrapper.Unit#UNIT PilotUnit + -- @field Tasking.Task#TASK Task + + + --- # TASK_CARGO_DISPATCHER class, extends @{Task_Manager#TASK_MANAGER} + -- + -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia1.JPG) + -- + -- The @{#TASK_CARGO_DISPATCHER} class implements the dynamic dispatching of cargo tasks. + -- + -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia3.JPG) + -- + -- The EWR will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched. + -- Find a summary below describing for which situation a task type is created: + -- + -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia9.JPG) + -- + -- * **CSAR Task**: Is created when a fiendly pilot has ejected from a plane, and needs to be rescued (sometimes behind enemy lines). + -- + -- ## 1. TASK\_A2A\_DISPATCHER constructor: + -- + -- The @{#TASK_CARGO_DISPATCHER.New}() method creates a new TASK\_A2A\_DISPATCHER instance. + -- + -- ### 1.1. Define or set the **Mission**: + -- + -- Tasking is executed to accomplish missions. Therefore, a MISSION object needs to be given as the first parameter. + -- + -- local HQ = GROUP:FindByName( "HQ", "Bravo" ) + -- local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) + -- local Mission = MISSION:New( CommandCenter, "A2A Mission", "High", "Watch the air enemy units being detected.", coalition.side.RED ) + -- + -- Missions are governed by COMMANDCENTERS, so, ensure you have a COMMANDCENTER object installed and setup within your mission. + -- Create the MISSION object, and hook it under the command center. + -- + -- ### 1.2. Build a set of the groups seated by human players: + -- + -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia6.JPG) + -- + -- A set or collection of the groups wherein human players can be seated, these can be clients or units that can be joined as a slot or jumping into. + -- + -- local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Defender" ):FilterStart() + -- + -- The set is built using the SET_GROUP class. Apply any filter criteria to identify the correct groups for your mission. + -- Only these slots or units will be able to execute the mission and will receive tasks for this mission, once available. + -- + -- ### 1.3. Define the **EWR network**: + -- + -- As part of the TASK\_A2A\_DISPATCHER constructor, an EWR network must be given as the third parameter. + -- An EWR network, or, Early Warning Radar network, is used to early detect potential airborne targets and to understand the position of patrolling targets of the enemy. + -- + -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia5.JPG) + -- + -- Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units. + -- These radars have different ranges and 55G6 EWR and 1L13 EWR radars are Eastern Bloc units (eg Russia, Ukraine, Georgia) while the Hawk and Patriot radars are Western (eg US). + -- Additionally, ANY other radar capable unit can be part of the EWR network! Also AWACS airborne units, planes, helicopters can help to detect targets, as long as they have radar. + -- The position of these units is very important as they need to provide enough coverage + -- to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them. + -- + -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia7.JPG) + -- + -- Additionally in a hot war situation where the border is no longer respected the placement of radars has a big effect on how fast the war escalates. + -- For example if they are a long way forward and can detect enemy planes on the ground and taking off + -- they will start to vector CAP and GCI flights to attack them straight away which will immediately draw a response from the other coalition. + -- Having the radars further back will mean a slower escalation because fewer targets will be detected and + -- therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map. + -- It all depends on what the desired effect is. + -- + -- EWR networks are **dynamically constructed**, that is, they form part of the @{Functional#DETECTION_BASE} object that is given as the input parameter of the TASK\_A2A\_DISPATCHER class. + -- By defining in a **smart way the names or name prefixes of the groups** with EWR capable units, these groups will be **automatically added or deleted** from the EWR network, + -- increasing or decreasing the radar coverage of the Early Warning System. + -- + -- See the following example to setup an EWR network containing EWR stations and AWACS. + -- + -- local EWRSet = SET_GROUP:New():FilterPrefixes( "EWR" ):FilterCoalitions("red"):FilterStart() + -- + -- local EWRDetection = DETECTION_AREAS:New( EWRSet, 6000 ) + -- EWRDetection:SetFriendliesRange( 10000 ) + -- EWRDetection:SetRefreshTimeInterval(30) + -- + -- -- Setup the A2A dispatcher, and initialize it. + -- A2ADispatcher = TASK_CARGO_DISPATCHER:New( Mission, AttackGroups, EWRDetection ) + -- + -- The above example creates a SET_GROUP instance, and stores this in the variable (object) **EWRSet**. + -- **EWRSet** is then being configured to filter all active groups with a group name starting with **EWR** to be included in the Set. + -- **EWRSet** is then being ordered to start the dynamic filtering. Note that any destroy or new spawn of a group with the above names will be removed or added to the Set. + -- Then a new **EWRDetection** object is created from the class DETECTION_AREAS. A grouping radius of 6000 is choosen, which is 6km. + -- The **EWRDetection** object is then passed to the @{#TASK_CARGO_DISPATCHER.New}() method to indicate the EWR network configuration and setup the A2A tasking and detection mechanism. + -- + -- ### 2. Define the detected **target grouping radius**: + -- + -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia8.JPG) + -- + -- The target grouping radius is a property of the Detection object, that was passed to the AI\_A2A\_DISPATCHER object, but can be changed. + -- The grouping radius should not be too small, but also depends on the types of planes and the era of the simulation. + -- Fast planes like in the 80s, need a larger radius than WWII planes. + -- Typically I suggest to use 30000 for new generation planes and 10000 for older era aircraft. + -- + -- Note that detected targets are constantly re-grouped, that is, when certain detected aircraft are moving further than the group radius, then these aircraft will become a separate + -- group being detected. This may result in additional GCI being started by the dispatcher! So don't make this value too small! + -- + -- ## 3. Set the **Engage radius**: + -- + -- Define the radius to engage any target by airborne friendlies, which are executing cap or returning from an intercept mission. + -- + -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia11.JPG) + -- + -- So, if there is a target area detected and reported, + -- then any friendlies that are airborne near this target area, + -- will be commanded to (re-)engage that target when available (if no other tasks were commanded). + -- For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target, + -- will be considered to receive the command to engage that target area. + -- You need to evaluate the value of this parameter carefully. + -- If too small, more intercept missions may be triggered upon detected target areas. + -- If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far. + -- + -- ## 4. Set **Scoring** and **Messages**: + -- + -- The TASK\_A2A\_DISPATCHER is a state machine. It triggers the event Assign when a new player joins a @{Task} dispatched by the TASK\_A2A\_DISPATCHER. + -- An _event handler_ can be defined to catch the **Assign** event, and add **additional processing** to set _scoring_ and to _define messages_, + -- when the player reaches certain achievements in the task. + -- + -- The prototype to handle the **Assign** event needs to be developed as follows: + -- + -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( ... ) + -- + -- --- @param #TaskDispatcher self + -- -- @param #string From Contains the name of the state from where the Event was triggered. + -- -- @param #string Event Contains the name of the event that was triggered. In this case Assign. + -- -- @param #string To Contains the name of the state that will be transitioned to. + -- -- @param Tasking.Task_A2A#TASK_A2A Task The Task object, which is any derived object from TASK_A2A. + -- -- @param Wrapper.Unit#UNIT TaskUnit The Unit or Client that contains the Player. + -- -- @param #string PlayerName The name of the Player that joined the TaskUnit. + -- function TaskDispatcher:OnAfterAssign( From, Event, To, Task, TaskUnit, PlayerName ) + -- Task:SetScoreOnProgress( PlayerName, 20, TaskUnit ) + -- Task:SetScoreOnSuccess( PlayerName, 200, TaskUnit ) + -- Task:SetScoreOnFail( PlayerName, -100, TaskUnit ) + -- end + -- + -- The **OnAfterAssign** method (function) is added to the TaskDispatcher object. + -- This method will be called when a new player joins a unit in the set of groups in scope of the dispatcher. + -- So, this method will be called only **ONCE** when a player joins a unit in scope of the task. + -- + -- The TASK class implements various methods to additional **set scoring** for player achievements: + -- + -- * @{Tasking.Task#TASK.SetScoreOnProgress}() will add additional scores when a player achieves **Progress** while executing the task. + -- Examples of **task progress** can be destroying units, arriving at zones etc. + -- + -- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional scores when the task goes into **Success** state. + -- This means the **task has been successfully completed**. + -- + -- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional (negative) scores when the task goes into **Failed** state. + -- This means the **task has not been successfully completed**, and the scores must be given with a negative value! + -- + -- @field #TASK_CARGO_DISPATCHER + TASK_CARGO_DISPATCHER = { + ClassName = "TASK_CARGO_DISPATCHER", + Mission = nil, + Tasks = {}, + CSAR = {}, + CSARSpawned = 0, + } + + + --- TASK_CARGO_DISPATCHER constructor. + -- @param #TASK_CARGO_DISPATCHER self + -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. + -- @param Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission. + -- @return #TASK_CARGO_DISPATCHER self + function TASK_CARGO_DISPATCHER:New( Mission, SetGroup ) + + -- Inherits from DETECTION_MANAGER + local self = BASE:Inherit( self, TASK_MANAGER:New( SetGroup ) ) -- #TASK_CARGO_DISPATCHER + + self.Mission = Mission + + self:AddTransition( "Started", "Assign", "Started" ) + + --- OnAfter Transition Handler for Event Assign. + -- @function [parent=#TASK_CARGO_DISPATCHER] OnAfterAssign + -- @param #TASK_CARGO_DISPATCHER self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Tasking.Task_A2A#TASK_A2A Task + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param #string PlayerName + + self:SetCSARRadius() + self:__StartTasks( 5 ) + + -- For CSAR missions, we process the event when a pilot ejects. + + self:HandleEvent( EVENTS.Ejection ) + + return self + end + + + --- Handle the event when a pilot ejects. + -- @param #TASK_CARGO_DISPATCHER self + -- @param Core.Event#EVENTDATA EventData + function TASK_CARGO_DISPATCHER:OnEventEjection( EventData ) + + self:E( { EventData = EventData } ) + + self.CSARSpawned = self.CSARSpawned + 1 + + local PlaneUnit = EventData.IniUnit + local CSARName = EventData.IniUnitName + + local CargoPointVec2 = EventData.IniUnit:GetPointVec2() + local CargoCoalition = EventData.IniUnit:GetCoalition() + local CargoCountry = EventData.IniUnit:GetCountry() + + -- Only add a CSAR task if the coalition of the mission is equal to the coalition of the ejected unit. + + if CargoCoalition == self.Mission:GetCommandCenter():GetCoalition() then + + -- Create the CSAR Pilot SPAWN object. + -- Let us create the Template for the replacement Pilot :-) + local Template = { + ["visible"] = false, + ["hidden"] = false, + ["task"] = "Ground Nothing", + ["name"] = string.format( "CSAR Pilot#%03d", self.CSARSpawned ), + ["x"] = CargoPointVec2:GetLat(), + ["y"] = CargoPointVec2:GetLon(), + ["units"] = + { + [1] = + { + ["type"] = ( CargoCoalition == coalition.side.BLUE ) and "Soldier M4" or "Infantry AK", + ["name"] = string.format( "CSAR Pilot#%03d-01", self.CSARSpawned ), + ["skill"] = "Excellent", + ["playerCanDrive"] = false, + ["x"] = CargoPointVec2:GetLat(), + ["y"] = CargoPointVec2:GetLon(), + ["heading"] = EventData.IniUnit:GetHeading(), + }, -- end of [1] + }, -- end of ["units"] + } + + local CargoGroup = GROUP:NewTemplate( Template, CargoCoalition, Group.Category.GROUND, CargoCountry ) + + self.CSAR[#self.CSAR+1] = {} + self.CSAR[#self.CSAR].PilotGroup = CargoGroup + self.CSAR[#self.CSAR].Task = nil + + end + + return self + end + + + --- Define the radius to when a CSAR task will be generated for any downed pilot within range of the nearest CSAR airbase. + -- @param #TASK_CARGO_DISPATCHER self + -- @param #number CSARRadius (Optional, Default = 50000) The radius in meters to decide whether a CSAR needs to be created. + -- @return #TASK_CARGO_DISPATCHER + -- @usage + -- + -- -- Set 20km as the radius to CSAR any downed pilot within range of the nearest CSAR airbase. + -- TaskA2ADispatcher:SetEngageRadius( 20000 ) + -- + -- -- Set 50km as the radius to to CSAR any downed pilot within range of the nearest CSAR airbase. + -- TaskA2ADispatcher:SetEngageRadius() -- 50000 is the default value. + -- + function TASK_CARGO_DISPATCHER:SetCSARRadius( CSARRadius ) + + self.CSARRadius = CSARRadius or 50000 + + return self + end + + + --- Define one deploy zone for the CSAR tasks. + -- @param #TASK_CARGO_DISPATCHER self + -- @param DeployZone A deploy zone. + -- @return #TASK_CARGO_DISPATCHER + function TASK_CARGO_DISPATCHER:SetCSARDeployZone( CSARDeployZone ) + + self.CSARDeployZones = { CSARDeployZone } + + return self + end + + + --- Define the deploy zones for the CSAR tasks. + -- @param #TASK_CARGO_DISPATCHER self + -- @param CSARDeployZones A list of the deploy zones. + -- @return #TASK_CARGO_DISPATCHER + function TASK_CARGO_DISPATCHER:SetCSARDeployZones( CSARDeployZones ) + + self.CSARDeployZones = CSARDeployZones + + return self + end + + + --- Evaluates of a CSAR task needs to be started. + -- @param #TASK_CARGO_DISPATCHER self + -- @return Set#SET_CARGO The SetCargo to be rescued. + -- @return #nil If there is no CSAR task required. + function TASK_CARGO_DISPATCHER:EvaluateCSAR( CSARUnit ) + + local CSARCargo = CARGO_GROUP:New( CSARUnit, "Pilot", CSARUnit:GetName(), 80, 1500, 10 ) + + local SetCargo = SET_CARGO:New() + SetCargo:AddCargosByName( CSARUnit:GetName() ) + + SetCargo:Flush(self) + + return SetCargo + + end + + + + --- Assigns tasks to the @{Set#SET_GROUP}. + -- @param #TASK_CARGO_DISPATCHER self + -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. + function TASK_CARGO_DISPATCHER:ManageTasks() + self:F() + + local AreaMsg = {} + local TaskMsg = {} + local ChangeMsg = {} + + local Mission = self.Mission + + if Mission:IsIDLE() or Mission:IsENGAGED() then + + local TaskReport = REPORT:New() + + -- Checking the task queue for the dispatcher, and removing any obsolete task! + for TaskIndex, TaskData in pairs( self.Tasks ) do + local Task = TaskData -- Tasking.Task#TASK + if Task:IsStatePlanned() then + -- Here we need to check if the pilot is still existing. +-- local DetectedItem = Detection:GetDetectedItemByIndex( TaskIndex ) +-- if not DetectedItem then +-- local TaskText = Task:GetName() +-- for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do +-- Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2A task %s for %s removed.", TaskText, Mission:GetShortText() ), TaskGroup ) +-- end +-- Task = self:RemoveTask( TaskIndex ) +-- end + end + end + + -- Now that all obsolete tasks are removed, loop through the CSAR pilots. + for CSARID, CSARData in pairs( self.CSAR ) do + + if CSARData.Task then + else + -- New CSAR Task + local SetCargo = self:EvaluateCSAR( CSARData.PilotGroup ) + local CSARTask = TASK_CARGO_CSAR:New( Mission, self.SetGroup, string.format( "CSAR.%03d", CSARID ), SetCargo ) + CSARTask:SetDeployZones( self.CSARDeployZones or {} ) + Mission:AddTask( CSARTask ) + TaskReport:Add( CSARTask:GetName() ) + CSARData.Task = CSARTask + end + end + + + -- TODO set menus using the HQ coordinator + Mission:GetCommandCenter():SetMenu() + + local TaskText = TaskReport:Text(", ") + + for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do + if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" then + Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetShortText(), TaskText ), TaskGroup ) + end + end + + end + + return true + end + +end diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua new file mode 100644 index 000000000..0deff06e4 --- /dev/null +++ b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua @@ -0,0 +1,187 @@ +--- **Tasking** -- The TASK_CARGO models tasks for players to transport @{Cargo}. +-- +-- ![Banner Image](..\Presentations\TASK_CARGO\Dia1.JPG) +-- +-- === + + +do -- TASK_CARGO_TRANSPORT + + --- The TASK_CARGO_TRANSPORT class + -- @type TASK_CARGO_TRANSPORT + -- @extends Tasking.Task_Cargo#TASK_CARGO + TASK_CARGO_TRANSPORT = { + ClassName = "TASK_CARGO_TRANSPORT", + } + + --- Instantiates a new TASK_CARGO_TRANSPORT. + -- @param #TASK_CARGO_TRANSPORT self + -- @param Tasking.Mission#MISSION Mission + -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param #string TaskName The name of the Task. + -- @param Core.Set#SET_CARGO SetCargo The scope of the cargo to be transported. + -- @param #string TaskBriefing The Cargo Task briefing. + -- @return #TASK_CARGO_TRANSPORT self + function TASK_CARGO_TRANSPORT:New( Mission, SetGroup, TaskName, SetCargo, TaskBriefing ) + local self = BASE:Inherit( self, TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, "Transport", TaskBriefing ) ) -- #TASK_CARGO_TRANSPORT + self:F() + + Mission:AddTask( self ) + + + -- Events + + self:AddTransition( "*", "CargoPickedUp", "*" ) + self:AddTransition( "*", "CargoDeployed", "*" ) + + self:F( { CargoDeployed = self.CargoDeployed ~= nil and "true" or "false" } ) + + --- OnBefore Transition Handler for Event CargoPickedUp. + -- @function [parent=#TASK_CARGO_TRANSPORT] OnBeforeCargoPickedUp + -- @param #TASK_CARGO_TRANSPORT self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event CargoPickedUp. + -- @function [parent=#TASK_CARGO_TRANSPORT] OnAfterCargoPickedUp + -- @param #TASK_CARGO_TRANSPORT self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + + --- Synchronous Event Trigger for Event CargoPickedUp. + -- @function [parent=#TASK_CARGO_TRANSPORT] CargoPickedUp + -- @param #TASK_CARGO_TRANSPORT self + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + + --- Asynchronous Event Trigger for Event CargoPickedUp. + -- @function [parent=#TASK_CARGO_TRANSPORT] __CargoPickedUp + -- @param #TASK_CARGO_TRANSPORT self + -- @param #number Delay The delay in seconds. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + + --- OnBefore Transition Handler for Event CargoDeployed. + -- @function [parent=#TASK_CARGO_TRANSPORT] OnBeforeCargoDeployed + -- @param #TASK_CARGO_TRANSPORT self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. + -- @return #boolean Return false to cancel Transition. + + --- OnAfter Transition Handler for Event CargoDeployed. + -- @function [parent=#TASK_CARGO_TRANSPORT] OnAfterCargoDeployed + -- @param #TASK_CARGO_TRANSPORT self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. + + --- Synchronous Event Trigger for Event CargoDeployed. + -- @function [parent=#TASK_CARGO_TRANSPORT] CargoDeployed + -- @param #TASK_CARGO_TRANSPORT self + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. + + --- Asynchronous Event Trigger for Event CargoDeployed. + -- @function [parent=#TASK_CARGO_TRANSPORT] __CargoDeployed + -- @param #TASK_CARGO_TRANSPORT self + -- @param #number Delay The delay in seconds. + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. + + local Fsm = self:GetUnitProcess() + + local CargoReport = REPORT:New( "Transport Cargo. The following cargo needs to be transported including initial positions:") + + SetCargo:ForEachCargo( + --- @param Core.Cargo#CARGO Cargo + function( Cargo ) + local CargoType = Cargo:GetType() + local CargoName = Cargo:GetName() + local CargoCoordinate = Cargo:GetCoordinate() + CargoReport:Add( string.format( '- "%s" (%s) at %s', CargoName, CargoType, CargoCoordinate:ToStringMGRS() ) ) + end + ) + + self:SetBriefing( + TaskBriefing or + CargoReport:Text() + ) + + + return self + end + + function TASK_CARGO_TRANSPORT:ReportOrder( ReportGroup ) + + return 0 + end + + + --- + -- @param #TASK_CARGO_TRANSPORT self + -- @return #boolean + function TASK_CARGO_TRANSPORT:IsAllCargoTransported() + + local CargoSet = self:GetCargoSet() + local Set = CargoSet:GetSet() + + local DeployZones = self:GetDeployZones() + + local CargoDeployed = true + + -- Loop the CargoSet (so evaluate each Cargo in the SET_CARGO ). + for CargoID, CargoData in pairs( Set ) do + local Cargo = CargoData -- Core.Cargo#CARGO + + self:F( { Cargo = Cargo:GetName(), CargoDeployed = Cargo:IsDeployed() } ) + + if Cargo:IsDeployed() then + +-- -- Loop the DeployZones set for the TASK_CARGO_TRANSPORT. +-- for DeployZoneID, DeployZone in pairs( DeployZones ) do +-- +-- -- If all cargo is in one of the deploy zones, then all is good. +-- self:T( { Cargo.CargoObject } ) +-- if Cargo:IsInZone( DeployZone ) == false then +-- CargoDeployed = false +-- end +-- end + else + CargoDeployed = false + end + end + + self:F( { CargoDeployed = CargoDeployed } ) + + return CargoDeployed + end + + --- @param #TASK_CARGO_TRANSPORT self + function TASK_CARGO_TRANSPORT:onafterGoal( TaskUnit, From, Event, To ) + local CargoSet = self.CargoSet + + if self:IsAllCargoTransported() then + self:Success() + end + + self:__Goal( -10 ) + end + +end + diff --git a/Moose Development/Moose/Tasking/Task_Manager.lua b/Moose Development/Moose/Tasking/Task_Manager.lua new file mode 100644 index 000000000..340fd8233 --- /dev/null +++ b/Moose Development/Moose/Tasking/Task_Manager.lua @@ -0,0 +1,150 @@ +--- This module contains the TASK_MANAGER class and derived classes. +-- +-- === +-- +-- 1) @{Task_Manager#TASK_MANAGER} class, extends @{Fsm#FSM} +-- === +-- The @{Task_Manager#TASK_MANAGER} class defines the core functions to report tasks to groups. +-- Reportings can be done in several manners, and it is up to the derived classes if TASK_MANAGER to model the reporting behaviour. +-- +-- 1.1) TASK_MANAGER constructor: +-- ----------------------------------- +-- * @{Task_Manager#TASK_MANAGER.New}(): Create a new TASK_MANAGER instance. +-- +-- 1.2) TASK_MANAGER reporting: +-- --------------------------------- +-- Derived TASK_MANAGER classes will manage tasks using the method @{Task_Manager#TASK_MANAGER.ManageTasks}(). This method implements polymorphic behaviour. +-- +-- The time interval in seconds of the task management can be changed using the methods @{Task_Manager#TASK_MANAGER.SetRefreshTimeInterval}(). +-- To control how long a reporting message is displayed, use @{Task_Manager#TASK_MANAGER.SetReportDisplayTime}(). +-- Derived classes need to implement the method @{Task_Manager#TASK_MANAGER.GetReportDisplayTime}() to use the correct display time for displayed messages during a report. +-- +-- Task management can be started and stopped using the methods @{Task_Manager#TASK_MANAGER.StartTasks}() and @{Task_Manager#TASK_MANAGER.StopTasks}() respectively. +-- If an ad-hoc report is requested, use the method @{Task_Manager#TASK_MANAGER#ManageTasks}(). +-- +-- The default task management interval is every 60 seconds. +-- +-- === +-- +-- ### Contributions: Mechanist, Prof_Hilactic, FlightControl - Concept & Testing +-- ### Author: FlightControl - Framework Design & Programming +-- +-- @module Task_Manager + +do -- TASK_MANAGER + + --- TASK_MANAGER class. + -- @type TASK_MANAGER + -- @field Set#SET_GROUP SetGroup The set of group objects containing players for which tasks are managed. + -- @extends Core.Fsm#FSM + TASK_MANAGER = { + ClassName = "TASK_MANAGER", + SetGroup = nil, + } + + --- TASK\_MANAGER constructor. + -- @param #TASK_MANAGER self + -- @param Set#SET_GROUP SetGroup The set of group objects containing players for which tasks are managed. + -- @return #TASK_MANAGER self + function TASK_MANAGER:New( SetGroup ) + + -- Inherits from BASE + local self = BASE:Inherit( self, FSM:New() ) -- #TASK_MANAGER + + self.SetGroup = SetGroup + + self:SetStartState( "Stopped" ) + self:AddTransition( "Stopped", "StartTasks", "Started" ) + + --- StartTasks Handler OnBefore for TASK_MANAGER + -- @function [parent=#TASK_MANAGER] OnBeforeStartTasks + -- @param #TASK_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- StartTasks Handler OnAfter for TASK_MANAGER + -- @function [parent=#TASK_MANAGER] OnAfterStartTasks + -- @param #TASK_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- StartTasks Trigger for TASK_MANAGER + -- @function [parent=#TASK_MANAGER] StartTasks + -- @param #TASK_MANAGER self + + --- StartTasks Asynchronous Trigger for TASK_MANAGER + -- @function [parent=#TASK_MANAGER] __StartTasks + -- @param #TASK_MANAGER self + -- @param #number Delay + + + + self:AddTransition( "Started", "StopTasks", "Stopped" ) + + --- StopTasks Handler OnBefore for TASK_MANAGER + -- @function [parent=#TASK_MANAGER] OnBeforeStopTasks + -- @param #TASK_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- StopTasks Handler OnAfter for TASK_MANAGER + -- @function [parent=#TASK_MANAGER] OnAfterStopTasks + -- @param #TASK_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- StopTasks Trigger for TASK_MANAGER + -- @function [parent=#TASK_MANAGER] StopTasks + -- @param #TASK_MANAGER self + + --- StopTasks Asynchronous Trigger for TASK_MANAGER + -- @function [parent=#TASK_MANAGER] __StopTasks + -- @param #TASK_MANAGER self + -- @param #number Delay + + + self:AddTransition( "Started", "Manage", "Started" ) + + self:SetRefreshTimeInterval( 30 ) + + return self + end + + function TASK_MANAGER:onafterStartTasks( From, Event, To ) + self:Manage() + end + + function TASK_MANAGER:onafterManage( From, Event, To ) + + self:__Manage( -self._RefreshTimeInterval ) + + self:ManageTasks() + end + + --- Set the refresh time interval in seconds when a new task management action needs to be done. + -- @param #TASK_MANAGER self + -- @param #number RefreshTimeInterval The refresh time interval in seconds when a new task management action needs to be done. + -- @return #TASK_MANAGER self + function TASK_MANAGER:SetRefreshTimeInterval( RefreshTimeInterval ) + self:F2() + + self._RefreshTimeInterval = RefreshTimeInterval + end + + + --- Manages the tasks for the @{Set#SET_GROUP}. + -- @param #TASK_MANAGER self + -- @return #TASK_MANAGER self + function TASK_MANAGER:ManageTasks() + self:E() + + end + +end + diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 66d9068a3..26062ea09 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -121,7 +121,7 @@ GROUPTEMPLATE.Takeoff = { -- @return #GROUP self function GROUP:NewTemplate( GroupTemplate, CoalitionSide, CategoryID, CountryID ) local GroupName = GroupTemplate.name - _DATABASE:_RegisterGroupTemplate( GroupTemplate, CategoryID, CountryID, CoalitionSide, GroupName ) + _DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, CategoryID, CountryID, GroupName ) self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) self:F2( GroupName ) self.GroupName = GroupName diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index ebc56985c..d1328835d 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -76,12 +76,16 @@ Tasking/CommandCenter.lua Tasking/Mission.lua Tasking/Task.lua Tasking/TaskInfo.lua +Tasking/Task_Manager.lua Tasking/DetectionManager.lua Tasking/Task_A2G_Dispatcher.lua Tasking/Task_A2G.lua Tasking/Task_A2A_Dispatcher.lua Tasking/Task_A2A.lua Tasking/Task_Cargo.lua +Tasking/Task_Cargo_Transport.lua +Tasking/Task_Cargo_CSAR.lua +Tasking/Task_Cargo_Dispatcher.lua Tasking/TaskZoneCapture.lua Moose.lua From e094c8133a80ea26204983a16f9001202df14b5c Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Tue, 3 Apr 2018 16:49:34 +0200 Subject: [PATCH 026/420] Added PseudoATC and Suppression --- .../Moose/Functional/PseudoATC.lua | 766 +++++++ .../Moose/Functional/Suppression.lua | 1816 +++++++++++++++++ 2 files changed, 2582 insertions(+) create mode 100644 Moose Development/Moose/Functional/PseudoATC.lua create mode 100644 Moose Development/Moose/Functional/Suppression.lua diff --git a/Moose Development/Moose/Functional/PseudoATC.lua b/Moose Development/Moose/Functional/PseudoATC.lua new file mode 100644 index 000000000..9e93073d6 --- /dev/null +++ b/Moose Development/Moose/Functional/PseudoATC.lua @@ -0,0 +1,766 @@ +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- **Functional** - Pseudo ATC. +-- +-- ![Banner Image](..\Presentations\PSEUDOATC\PSEUDOATC_Main.jpg) +-- +-- ==== +-- +-- The pseudo ATC enhances the standard DCS ATC functions. +-- +-- In particular, a menu entry "Pseudo ATC" is created in the special F10 menu. +-- +-- ## Features +-- +-- * Report QFE or QNH pressures at nearby airbases. +-- * Report wind direction and strength at airbases. +-- * Report temperature at airbases +-- * Report absolute bearing and range to nearest airports. +-- * Report current altitude AGL of own aircraft. +-- * Upon request, ATC reports altitude until touchdown. +-- * Pressure temperature, wind data and BR for mission waypoints. +-- * Works with static and dynamic weather. +-- * All maps supported (Caucasus, NTTR, Normandy, and all future maps). +-- * Multiplayer ready (?) (I suppose yes, but I don't have a server to test or debug. Jumping from client to client works.) +-- +-- Pressure units: hPa (european aircraft), mmHg (russian aircraft), inHg (american aircraft). +-- +-- ==== +-- +-- # Demo Missions +-- +-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- +-- ==== +-- +-- # YouTube Channel +-- +-- ### [MOOSE YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl1jirWIo4t4YxqN-HxjqRkL) +-- +-- === +-- +-- ### Author: **[funkyfranky](https://forums.eagle.ru/member.php?u=115026)** +-- +-- ### Contributions: **Sven van de Velde ([FlightControl](https://forums.eagle.ru/member.php?u=89536))** +-- +-- ==== +-- @module PeusoATC + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- PSEUDOATC class +-- @type PSEUDOATC +-- @field #string ClassName Name of the Class. +-- @field #boolean Debug If true, print debug info to dcs.log file. +-- @field #table player Table comprising the player info. +-- @field #number mdur Duration in seconds how low messages to the player are displayed. +-- @field #boolean eventsmoose If true, events are handled by MOOSE. If false, events are handled directly by DCS eventhandler. +-- @extends Core.Base#BASE + +---# PSEUDOATC class, extends @{Base#BASE} +-- The PSEUDOATC class adds some rudimentary ATC functionality via the radio menu. +-- +-- ## Scripting: +-- +-- Scripting is almost trivial. Just add the following line to your script: +-- +-- PSEUDOATC:Start() +-- +-- +-- @field #PSEUDOATC +PSEUDOATC={ + ClassName = "PSEUDOATC", + Debug=true, + player={}, + maxairport=9, + mdur=30, + mrefresh=120, +} + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- PSEUDOATC unit conversions. +-- @list unit +PSEUDOATC.unit={ + hPa2inHg=0.0295299830714, + hPa2mmHg=0.7500615613030, + meter2feet=3.28084, + km2nm=0.539957, +} + +--- Some ID to identify who we are in output of the DCS.log file. +-- @field #string id +PSEUDOATC.id="PseudoATC | " + +--- PSEUDOATC version. +-- @field #list +PSEUDOATC.version={ + version = "0.5.0", + print = true, +} + + +--- PSEUDOATC contructor. Starts the PseudoATC. +-- @param #PSEUDOATC self +-- @return #PSEUDOATC Returns a PSEUDOATC object. +function PSEUDOATC:Start() + + -- Inherit BASE. + local self=BASE:Inherit(self, BASE:New()) -- #PSEUDOATC + + -- Debug info + env.info(PSEUDOATC.id..string.format("Creating PseudoATC object. PseudoATC version %s", PSEUDOATC.version.version)) + + -- Handle events. + if self.eventsmoose then + self:HandleEvent(EVENTS.Birth, self._OnBirth) + self:HandleEvent(EVENTS.PlayerLeaveUnit, self._PlayerLeft) + --self:HandleEvent(EVENTS.PilotDead, self._PlayerLeft) + self:HandleEvent(EVENTS.Land, self._PlayerLanded) + --self:HandleEvent(EVENTS.Takeoff, self._PlayerTakeoff) + else + -- Events are handled directly by DCS. + world.addEventHandler(self) + end + + -- Return object. + return self +end + +----------------------------------------------------------------------------------------------------------------------------------------- +-- Event Handling + +--- Event handler for suppressed groups. +--@param #PSEUDOATC self +--@param #table Event Event data table. Holds event.id, event.initiator and event.target etc. +function PSEUDOATC:onEvent(Event) + if Event == nil or Event.initiator == nil or Unit.getByName(Event.initiator:getName()) == nil then + return true + end + + local DCSiniunit = Event.initiator + local DCSplace = Event.place + local DCSsubplace = Event.subplace + + local EventData={} + local _playerunit=nil + local _playername=nil + + if Event.initiator then + EventData.IniUnitName = Event.initiator:getName() + EventData.IniDCSGroup = Event.initiator:getGroup() + EventData.IniGroupName = Event.initiator:getGroup():getName() + -- Get player unit and name. This returns nil,nil if the event was not fired by a player unit. And these are the only events we are interested in. + _playerunit, _playername = self:_GetPlayerUnitAndName(EventData.IniUnitName) + end + + if Event.place then + EventData.Place=Event.place + EventData.PlaceName=Event.place:getName() + end + if Event.subplace then + EventData.SubPlace=Event.subplace + EventData.SubPlaceName=Event.subplace:getName() + end + + -- Event info. + if self.Debug then + env.info(PSEUDOATC.id..string.format("EVENT: Event in onEvent with ID = %s", tostring(Event.id))) + env.info(PSEUDOATC.id..string.format("EVENT: Ini unit = %s" , tostring(EventData.IniUnitName))) + env.info(PSEUDOATC.id..string.format("EVENT: Ini group = %s" , tostring(EventData.IniGroupName))) + env.info(PSEUDOATC.id..string.format("EVENT: Ini player = %s" , tostring(_playername))) + env.info(PSEUDOATC.id..string.format("EVENT: Place = %s" , tostring(EventData.PlaceName))) + env.info(PSEUDOATC.id..string.format("EVENT: SubPlace = %s" , tostring(EventData.SubPlaceName))) + end + + -- Event birth. + if Event.id == world.event.S_EVENT_BIRTH and _playername then + self:_OnBirth(EventData) + end + + -- Event land. + if Event.id == world.event.S_EVENT_LAND and _playername and EventData.Place then + self:_PlayerLanded(EventData) + end + + -- Event player left unit + if Event.id == world.event.S_EVENT_PLAYER_LEAVE_UNIT and _playername then + self:_PlayerLeft(EventData) + end + +end + +--- Function called my MOOSE event handler when a player enters a unit. +-- @param #PSEUDOATC self +-- @param Core.Event#EVENTDATA EventData +function PSEUDOATC:_OnBirth(EventData) + env.info(PSEUDOATC.id.."PlayerEntered event caught my MOOSE.") + + local _unitName=EventData.IniUnitName + local _unit, _playername=self:_GetPlayerUnitAndName(_unitName) + + if _unit and _playername then + self:PlayerEntered(_unit) + end + +end + +--- Function called by MOOSE event handler when a player leaves a unit or dies. +-- @param #PSEUDOATC self +-- @param Core.Event#EVENTDATA EventData +function PSEUDOATC:_PlayerLeft(EventData) + + local _unitName=EventData.IniUnitName + local _unit, _playername=self:_GetPlayerUnitAndName(_unitName) + + if _unit and _playername then + self:PlayerLeft(_unit) + end +end + +--- Function called by MOOSE event handler when a player landed. +-- @param #PSEUDOATC self +-- @param Core.Event#EVENTDATA EventData +function PSEUDOATC:_PlayerLanded(EventData) + + local _unitName=EventData.IniUnitName + local _unit, _playername=self:_GetPlayerUnitAndName(_unitName) + local _base=EventData.Place + local _baseName=EventData.PlaceName + + if _unit and _playername and _base then + self:PlayerLanded(_unit, _baseName) + end +end + +----------------------------------------------------------------------------------------------------------------------------------------- +-- Menu Functions + +--- Function called when a player enters a unit. +-- @param #PSEUDOATC self +-- @param Wrapper.Unit#UNIT unit Unit the player entered. +function PSEUDOATC:PlayerEntered(unit) + self:F2({unit=unit}) + + local group=unit:GetGroup() --Wrapper.Group#GROUP + local GID=group:GetID() + local GroupName=group:GetName() + local PlayerName=unit:GetPlayerName() + local UnitName=unit:GetName() + local CallSign=unit:GetCallsign() + + -- Init player table. + self.player[GID]={} + self.player[GID].group=group + self.player[GID].unit=unit + self.player[GID].groupname=GroupName + self.player[GID].unitname=UnitName + self.player[GID].playername=PlayerName + self.player[GID].callsign=CallSign + self.player[GID].waypoints=group:GetTaskRoute() + + -- Info message. + local text=string.format("Player %s entered unit %s of group %s. ID = %d", PlayerName, UnitName, GroupName, GID) + if self.Debug then + MESSAGE:New(text, 30):ToGroup(group) + env.info(PSEUDOATC.id..text) + end + + -- Create main F10 menu, i.e. "F10/Pseudo ATC" + self.player[GID].menu_main=missionCommands.addSubMenuForGroup(GID, "Pseudo ATC") + + -- Create list of nearby airports. + self:LocalAirports(GID) + + -- Create submenu My Positon. + self:MenuAircraft(GID) + + -- Create submenu airports. + self:MenuAirports(GID) + + -- Start scheduler to refresh the F10 menues. + self.player[GID].scheduler, self.player[GID].schedulerid=SCHEDULER:New(nil, self.MenuRefresh, {self, GID}, self.mrefresh, self.mrefresh) + + self:T2(self.player[GID]) +end + +--- Function called when a player has landed. +-- @param #PSEUDOATC self +-- @param Wrapper.Unit#UNIT unit Unit of player which has landed. +-- @param #string place Name of the place the player landed at. +function PSEUDOATC:PlayerLanded(unit, place) + self:F2({unit=unit, place=place}) + + -- Gather some information. + local group=unit:GetGroup() + local id=group:GetID() + local PlayerName=self.player[id].playername + local UnitName=self.player[id].playername + local GroupName=self.player[id].groupname + local CallSign=self.player[id].callsign + + -- Debug message. + if self.Debug then + local text=string.format("Player %s (%s) from group %s with ID %d landed at %s", PlayerName, UnitName, GroupName, place) + MESSAGE:New(text,30):ToAll() + env.info(PSEUDOATC.id..text) + end + + -- Stop altitude reporting timer if its activated. + self:AltidudeStopTimer(id) + + -- Welcome message. + if place then + local text=string.format("Touchdown! Welcome to %s. Have a nice day!", place) + MESSAGE:New(text, self.mdur):ToGroup(group) + end + +end + +--- Function called when a player leaves a unit or dies. +-- @param #PSEUDOATC self +-- @param Wrapper.Unit#UNIT unit Player unit which was left. +function PSEUDOATC:PlayerLeft(unit) + self:F2({unit=unit}) + + -- Get id. + local group=unit:GetGroup() + local id=group:GetID() + + -- Debug message. + if self.Debug then + local text=string.format("Player %s (%s) callsign %s of group %s just left.", self.player[id].playername, self.player[id].unitname, self.player[id].callsign, self.player[id].groupname) + MESSAGE:New(text,30):ToAll() + env.info(PSEUDOATC.id..text) + end + + -- Stop scheduler for menu updates + if self.player[id].schedulerid then + self.player[id].scheduler:Stop(self.player[id].schedulerid) + self.player[id].scheduler=nil + self.player[id].schedulerid=nil + end + + -- Remove main menu + missionCommands.removeItem(self.player[id].menu_main) + + -- Remove player array. + self.player[id]=nil + +end + +----------------------------------------------------------------------------------------------------------------------------------------- +-- Menu Functions + +--- Refreshes all player menues. +-- @param #PSEUDOATC self. +-- @param #number id Group id of player unit. +function PSEUDOATC:MenuRefresh(id) + self:F(id) + + if self.Debug then + local text=string.format("Refreshing menues for player %s in group %s.", self.player[id].playername, self.player[id].groupname) + env.info(PSEUDOATC.id..text) + MESSAGE:New(text,30):ToAll() + end + + -- Clear menu. + self:MenuClear(id) + + -- Create list of nearby airports. + self:LocalAirports(id) + + -- Create submenu My Positon. + self:MenuAircraft(id) + + -- Create submenu airports. + self:MenuAirports(id) +end + + +--- Clear player menues. +-- @param #PSEUDOATC self. +-- @param #number id Group id of player unit. +function PSEUDOATC:MenuClear(id) + self:F(id) + + if self.Debug then + local text=string.format("Clearing menues for player %s in group %s.", self.player[id].playername, self.player[id].groupname) + env.info(PSEUDOATC.id..text) + MESSAGE:New(text,30):ToAll() + end + + BASE:E(self.player[id].menu_airports) + + if self.player[id].menu_airports then + for name,item in pairs(self.player[id].menu_airports) do + + if self.Debug then + env.info(PSEUDOATC.id..string.format("Deleting menu item %s for ID %d", name, id)) + BASE:E(item) + end + + missionCommands.removeItemForGroup(id, self.player[id].menu_airports[name]) + --missionCommands.removeItemForGroup(id, item) + end + + else + if self.Debug then + local text=string.format("no airports to clear menues") + env.info(PSEUDOATC.id..text) + end + end + + if self.player[id].menu_aircraft then + missionCommands.removeItemForGroup(id, self.player[id].menu_aircraft.main) + end + + self.player[id].menu_airports=nil + self.player[id].menu_aircraft=nil +end + +--- Create "F10/Pseudo ATC" menu items "Airport Data". +-- @param #PSEUDOATC self +-- @param #number id Group id of player unit for which menues are created. +function PSEUDOATC:MenuAirports(id) + self:F(id) + + -- Table for menu entries. + self.player[id].menu_airports={} + + local i=0 + for _,airport in pairs(self.player[id].airports) do + + i=i+1 + if i>self.maxairport then + break -- Max X<10 airports due to 10 menu items restriction. + end + + local name=airport.name + local d=airport.distance + local pos=AIRBASE:FindByName(name):GetCoordinate() + + --F10menu_ATC_airports[ID][name] = missionCommands.addSubMenuForGroup(ID, name, F10menu_ATC) + local submenu=missionCommands.addSubMenuForGroup(id, name, self.player[id].menu_main) + self.player[id].menu_airports[name]=submenu + + -- Create menu reporting commands + missionCommands.addCommandForGroup(id, "Request QFE", submenu, self.ReportPressure, self, id, "QFE", pos, name) + missionCommands.addCommandForGroup(id, "Request QNH", submenu, self.ReportPressure, self, id, "QNH", pos, name) + missionCommands.addCommandForGroup(id, "Request Wind", submenu, self.ReportWind, self, id, pos, name) + missionCommands.addCommandForGroup(id, "Request Temperature", submenu, self.ReportTemperature, self, id, pos, name) + missionCommands.addCommandForGroup(id, "Request BR", submenu, self.ReportBR, self, id, pos, name) + + if self.Debug then + env.info(string.format(PSEUDOATC.id.."Creating airport menu item %s for ID %d", name, id)) + end + end +end + +--- Create F10/Pseudo ATC menu item "My Plane". +-- @param #PSEUDOATC self +-- @param #number id Group id of player unit for which menues are created. +function PSEUDOATC:MenuAircraft(id) + self:F(id) + + -- Table for menu entries. + self.player[id].menu_aircraft={} + + local unit=self.player[id].unit --Wrapper.Unit#UNIT + local callsign=self.player[id].callsign + local name=string.format("My Aircraft (%s)", callsign) + + -- Debug info. + if self.Debug then + env.info(PSEUDOATC.id..string.format("Creating menu item %s for ID %d", name,id)) + end + + -- F10/PseudoATC/My Aircraft (callsign) + self.player[id].menu_aircraft.main = missionCommands.addSubMenuForGroup(id, name, self.player[id].menu_main) + + -- F10/PseudoATC/My Aircraft (callsign)/Waypoints + if #self.player[id].waypoints>0 then + + --F10menu_ATC_waypoints[ID]={} + self.player[id].menu_aircraft_waypoints={} + self.player[id].menu_aircraft_waypoints.main=missionCommands.addSubMenuForGroup(id, "Waypoints", self.player[id].menu_aircraft.main) + + local j=0 + for i, wp in pairs(self.player[id].waypoints) do + -- Increase counter + j=j+1 + + if j>10 then + break -- max ten menu entries + end + + local pos=COORDINATE:New(wp.x,wp.alt,wp.z) + + local fname=string.format("Waypoint %d for %s", i-1, callsign) + local pname=string.format("Waypoint %d", i-1) + + -- "F10/PseudoATC/My Aircraft (callsign)/Waypoints/Waypoint X" + local submenu=missionCommands.addSubMenuForGroup(id, pname, self.player[id].menu_aircraft_waypoints.main) + self.player[id].menu_aircraft_waypoints.pname=submenu + + -- Menu commands for each waypoint "F10/PseudoATC/My Aircraft (callsign)/Waypoints/Waypoint X/" + missionCommands.addCommandForGroup(id, "Request QFE", submenu, self.ReportPressure, self, id, "QFE", pos, pname) + missionCommands.addCommandForGroup(id, "Request QNH", submenu, self.ReportPressure, self, id, "QNH", pos, pname) + missionCommands.addCommandForGroup(id, "Request Wind", submenu, self.ReportWind, self, id, pos, pname) + missionCommands.addCommandForGroup(id, "Request Temperature", submenu, self.ReportTemperature, self, id, pos, pname) + missionCommands.addCommandForGroup(id, "Request BR", submenu, self.ReportBR, self, id, pos, pname) + end + end + missionCommands.addCommandForGroup(id, "Request current altitude AGL", self.player[id].menu_aircraft.main, self.ReportHeight, self, id) + missionCommands.addCommandForGroup(id, "Report altitude until touchdown", self.player[id].menu_aircraft.main, self.AltidudeStartTimer, self, id) + missionCommands.addCommandForGroup(id, "Quit reporting altitude", self.player[id].menu_aircraft.main, self.AltidudeStopTimer, self, id) +end + +----------------------------------------------------------------------------------------------------------------------------------------- +-- Reporting Functions + +--- Report pressure. +-- @param #PSEUDOATC self +-- @param #number id Group id to which the report is delivered. +-- @param #string Qcode Can be "QNH" for pressure at sea level or "QFE" for pressure at field elevation. Default is QFE or more precisely pressure at position. +-- @param Core.Point#COORDINATE position Coordinates at which the pressure is measured. +-- @param #string location Name of the location at which the pressure is measured. +function PSEUDOATC:ReportPressure(id, Qcode, position, location) + self:F({id=id, Qcode=Qcode, position=position, location=location}) + + -- Get pressure in hPa. + local P + if Qcode=="QNH" then + P=position:GetPressure(0) -- Get pressure at sea level. + else + P=position:GetPressure() -- Get pressure at (land) height of position. + end + + -- Unit conversion. + local P_inHg=P * PSEUDOATC.unit.hPa2inHg + local P_mmHg=P * PSEUDOATC.unit.hPa2mmHg + + -- Message text. + local text=string.format("%s at %s: P = %.1f hPa = %.2f inHg = %.1f mmHg.", Qcode, location, P, P_inHg, P_mmHg) + + -- Send message. + MESSAGE:New(text, self.mdur):ToGroup(self.player[id].group) +end + +--- Report temperature. +-- @param #PSEUDOATC self +-- @param #number id Group id to the report is delivered. +-- @param Core.Point#COORDINATE position Coordinates at which the pressure is measured. +-- @param #string location Name of the location at which the pressure is measured. +function PSEUDOATC:ReportTemperature(id, position, location) + self:F({id=id, position=position, location=location}) + + --- convert celsius to fahrenheit + local function celsius2fahrenheit(degC) + return degC*1.8+32 + end + + -- Get temperature at position in degrees Celsius. + local T=position:GetTemperature() + + -- Formatted temperature in Celsius and Fahrenheit. + local Tc=string.format('%d°C', T) + local Tf=string.format('%d°F', celsius2fahrenheit(T)) + + -- Message text. + local text=string.format("Temperature at %s is %s = %s", location, Tc, Tf) + + -- Send message to player group. + MESSAGE:New(text, self.mdur):ToGroup(self.player[id].group) +end + +--- Report wind direction and strength. +-- @param #PSEUDOATC self +-- @param #number id Group id to the report is delivered. +-- @param Core.Point#COORDINATE position Coordinates at which the pressure is measured. +-- @param #string location Name of the location at which the pressure is measured. +function PSEUDOATC:ReportWind(id, position, location) + self:F({id=id, position=position, location=location}) + + -- Get wind direction and speed. + local Dir,Vel=position:GetWind() + + -- Get Beaufort wind scale. + local Bn,Bd=UTILS.BeaufortScale(Vel) + + -- Formatted wind direction. + local Ds = string.format('%03d°', Dir) + + -- Message text. + local text=string.format("%s: Wind from %s at %.1f m/s (%s).", location, Ds, Vel, Bd) + + -- Send message to player group. + MESSAGE:New(text, self.mdur):ToGroup(self.player[id].group) +end + +--- Report absolute bearing and range form player unit to airport. +-- @param #PSEUDOATC self +-- @param #number id Group id to the report is delivered. +-- @param Core.Point#COORDINATE position Coordinates at which the pressure is measured. +-- @param #string location Name of the location at which the pressure is measured. +function PSEUDOATC:ReportBR(id, position, location) + self:F({id=id, position=position, location=location}) + + -- Current coordinates. + local unit=self.player[id].unit --Wrapper.Unit#UNIT + local coord=unit:GetCoordinate() + + -- Direction vector from current position (coord) to target (position). + local vec3=coord:GetDirectionVec3(position) + local angle=coord:GetAngleDegrees(vec3) + local range=coord:Get2DDistance(position) + + -- Bearing string. + local Bs=string.format('%03d°', angle) + + -- Message text. + local text=string.format("%s: Bearing %s, Range %.1f km = %.1f NM.", location, Bs, range/1000, range/1000 * PSEUDOATC.unit.km2nm) + + -- Send message to player group. + MESSAGE:New(text, self.mdur):ToGroup(self.player[id].group) +end + +--- Report altitude above ground level of player unit. +-- @param #PSEUDOATC self +-- @param #number id Group id to the report is delivered. +-- @param #number dt (Optional) Duration the message is displayed. +-- @return #number Altuitude above ground. +function PSEUDOATC:ReportHeight(id, dt) + self:F({id=id, dt=dt}) + + local dt = dt or self.mdur + + -- Return height [m] above ground level. + local function get_AGL(p) + local vec2={x=p.x,y=p.z} + local ground=land.getHeight(vec2) + local agl=p.y-ground + return agl + end + + -- Get height AGL. + local unit=self.player[id].unit --Wrapper.Unit#UNIT + local position=unit:GetCoordinate() + local height=get_AGL(position) + local callsign=unit:GetCallsign() + + -- Message text. + local text=string.format("%s: Your altitude is %d m = %d ft AGL.", callsign, height, height*PSEUDOATC.unit.meter2feet) + + -- Send message to player group. + MESSAGE:New(text, dt):ToGroup(self.player[id].group) + + -- Return height + return height +end + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Start DCS scheduler function. +-- @param #PSEUDOATC self. +-- @param #number id Group id of player unit. +function PSEUDOATC:AltidudeStartTimer(id) + self:F(id) + + -- Debug info. + if self.Debug then + env.info(PSEUDOATC.id..string.format("Starting altitude report timer for player ID %d.", id)) + end + + -- Start timer. + --self.player[id].altimer=timer.scheduleFunction(self.ReportAltTouchdown, self, id, Tnow+2) + self.player[id].altimer, self.player[id].altimerid=SCHEDULER:New(nil, self.ReportHeight, {self, id, 0.1}, 1, 5) +end + +--- Stop/destroy DCS scheduler function for reporting altitude. +-- @param #PSEUDOATC self. +-- @param #number id Group id of player unit. +function PSEUDOATC:AltidudeStopTimer(id) + + -- Debug info. + if self.Debug then + env.info(PSEUDOATC.id..string.format("Stopping altitude report timer for player ID %d.", id)) + end + + -- Stop timer. + --timer.removeFunction(self.player[id].alttimer) + if self.player[id].altimerid then + self.player[id].altimer:Stop(self.player[id].altimerid) + end + + self.player[id].altimer=nil + self.player[id].altimerid=nil +end + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Misc + +--- Create list of nearby airports sorted by distance to player unit. +-- @param #PSEUDOATC self +-- @param #number id Group id of player unit. +function PSEUDOATC:LocalAirports(id) + self:F(id) + + -- Airports table. + self.player[id].airports=nil + self.player[id].airports={} + + -- Current player position. + local pos=self.player[id].unit:GetCoordinate() + + -- Loop over coalitions. + for i=0,2 do + + -- Get all airbases of coalition. + local airports=coalition.getAirbases(i) + + -- Loop over airbases + for _,airbase in pairs(airports) do + + local name=airbase:getName() + local q=AIRBASE:FindByName(name):GetCoordinate() + local d=q:Get2DDistance(pos) + + -- Add to table. + table.insert(self.player[id].airports, {distance=d, name=name}) + + end + end + + --- compare distance (for sorting airports) + local function compare(a,b) + return a.distance < b.distance + end + + -- Sort airports table w.r.t. distance to player. + table.sort(self.player[id].airports, compare) + +end + +--- Returns the unit of a player and the player name. If the unit does not belong to a player, nil is returned. +-- @param #PSEUDOATC self +-- @param #string _unitName Name of the player unit. +-- @return Wrapper.Unit#UNIT Unit of player. +-- @return #string Name of the player. +-- @return nil If player does not exist. +function PSEUDOATC:_GetPlayerUnitAndName(_unitName) + self:F(_unitName) + + if _unitName ~= nil then + local DCSunit=Unit.getByName(_unitName) + local playername=DCSunit:getPlayerName() + local unit=UNIT:Find(DCSunit) + + if DCSunit and unit and playername then + return unit, playername + end + end + + return nil,nil +end + + + diff --git a/Moose Development/Moose/Functional/Suppression.lua b/Moose Development/Moose/Functional/Suppression.lua new file mode 100644 index 000000000..f3a4c4954 --- /dev/null +++ b/Moose Development/Moose/Functional/Suppression.lua @@ -0,0 +1,1816 @@ +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- **Functional** - Suppress fire of ground units when they get hit. +-- +-- ![Banner Image](..\Presentations\SUPPRESSION\Suppression_Main.png) +-- +-- ==== +-- +-- When ground units get hit by (suppressive) enemy fire, they will not be able to shoot back for a certain amount of time. +-- +-- The implementation is based on an idea and script by MBot. See the [DCS forum threat](https://forums.eagle.ru/showthread.php?t=107635) for details. +-- +-- In addition to suppressing the fire, conditions can be specified which let the group retreat to a defined zone, move away from the attacker +-- or hide at a nearby scenery object. +-- +-- ==== +-- +-- # Demo Missions +-- +-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- +-- ==== +-- +-- # YouTube Channel +-- +-- ### [MOOSE YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl1jirWIo4t4YxqN-HxjqRkL) +-- +-- === +-- +-- ### Author: **[funkyfranky](https://forums.eagle.ru/member.php?u=115026)** +-- +-- ### Contributions: **Sven van de Velde ([FlightControl](https://forums.eagle.ru/member.php?u=89536))** +-- +-- ==== +-- @module Suppression + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- SUPPRESSION class +-- @type SUPPRESSION +-- @field #string ClassName Name of the class. +-- @field #boolean Debug Write Debug messages to DCS log file and send Debug messages to all players. +-- @field #boolean flare Flare units when they get hit or die. +-- @field #boolean smoke Smoke places to which the group retreats, falls back or hides. +-- @field #list DCSdesc Table containing all DCS descriptors of the group. +-- @field #string Type Type of the group. +-- @field #number SpeedMax Maximum speed of group in km/h. +-- @field #boolean IsInfantry True if group has attribute Infantry. +-- @field Core.Controllable#CONTROLLABLE Controllable Controllable of the FSM. Must be a ground group. +-- @field #number Tsuppress_ave Average time in seconds a group gets suppressed. Actual value is sampled randomly from a Gaussian distribution. +-- @field #number Tsuppress_min Minimum time in seconds the group gets suppressed. +-- @field #number Tsuppress_max Maximum time in seconds the group gets suppressed. +-- @field #number TsuppressionOver Time at which the suppression will be over. +-- @field #number IniGroupStrength Number of units in a group at start. +-- @field #number Nhit Number of times the group was hit. +-- @field #string Formation Formation which will be used when falling back, taking cover or retreating. Default "Vee". +-- @field #number Speed Speed the unit will use when falling back, taking cover or retreating. Default 999. +-- @field #boolean MenuON If true creates a entry in the F10 menu. +-- @field #boolean FallbackON If true, group can fall back, i.e. move away from the attacking unit. +-- @field #number FallbackWait Time in seconds the unit will wait at the fall back point before it resumes its mission. +-- @field #number FallbackDist Distance in meters the unit will fall back. +-- @field #number FallbackHeading Heading in degrees to which the group should fall back. Default is directly away from the attacking unit. +-- @field #boolean TakecoverON If true, group can hide at a nearby scenery object. +-- @field #number TakecoverWait Time in seconds the group will hide before it will resume its mission. +-- @field #number TakecoverRange Range in which the group will search for scenery objects to hide at. +-- @field Core.Point#COORDINATE hideout Coordinate/place where the group will try to take cover. +-- @field #number PminFlee Minimum probability in percent that a group will flee (fall back or take cover) at each hit event. Default is 10 %. +-- @field #number PmaxFlee Maximum probability in percent that a group will flee (fall back or take cover) at each hit event. Default is 90 %. +-- @field Core.Zone#ZONE RetreatZone Zone to which a group retreats. +-- @field #number RetreatDamage Damage in percent at which the group will be ordered to retreat. +-- @field #number RetreatWait Time in seconds the group will wait in the retreat zone before it resumes its mission. Default two hours. +-- @field #string CurrentAlarmState Alam state the group is currently in. +-- @field #string CurrentROE ROE the group currently has. +-- @field #string DefaultAlarmState Alarm state the group will go to when it is changed back from another state. Default is "Auto". +-- @field #string DefaultROE ROE the group will get once suppression is over. Default is "Free". +-- @field #boolean eventmoose If true, events are handled by MOOSE. If false, events are handled directly by DCS eventhandler. Default true. +-- @extends Core.Fsm#FSM_CONTROLLABLE +-- + +---# SUPPRESSION class, extends @{Core.Fsm#FSM_CONTROLLABLE} +-- Mimic suppressive enemy fire and let groups flee or retreat. +-- +-- ## Suppression Process +-- +-- ![Process](..\Presentations\SUPPRESSION\Suppression_Process.png) +-- +-- The suppression process can be described as follows. +-- +-- ### CombatReady +-- +-- A group starts in the state **CombatReady**. In this state the group is ready to fight. The ROE is set to either "Weapon Free" or "Return Fire". +-- The alarm state is set to either "Auto" or "Red". +-- +-- ### Event Hit +-- The most important event in this scenario is the **Hit** event. This is an event of the FSM and triggered by the DCS event hit. +-- +-- ### Suppressed +-- After the **Hit** event the group changes its state to **Suppressed**. Technically, the ROE of the group is changed to "Weapon Hold". +-- The suppression of the group will last a certain amount of time. It is randomized an will vary each time the group is hit. +-- The expected suppression time is set to 15 seconds by default. But the actual value is sampled from a Gaussian distribution. +-- +-- ![Process](..\Presentations\SUPPRESSION\Suppression_Gaussian.png) +-- +-- The graph shows the distribution of suppression times if a group would be hit 100,000 times. As can be seen, on most hits the group gets +-- suppressed for around 15 seconds. Other values are also possible but they become less likely the further away from the "expected" suppression time they are. +-- Minimal and maximal suppression times can also be specified. By default these are set to 5 and 25 seconds, respectively. This can also be seen in the graph +-- because the tails of the Gaussian distribution are cut off at these values. +-- +-- ### Event Recovered +-- After the suppression time is over, the event **Recovered** is initiated and the group becomes **CombatReady** again. +-- The ROE of the group will be set to "Weapon Free". +-- +-- Of course, it can also happen that a group is hit again while it is still suppressed. In that case a new random suppression time is calculated. +-- If the new suppression time is longer than the remaining suppression of the previous hit, then the group recovers when the suppression time of the last +-- hit has passed. +-- If the new suppression time is shorter than the remaining suppression, the group will recover after the longer time of the first suppression has passed. +-- +-- For example: +-- +-- * A group gets hit the first time and is suppressed for - let's say - 15 seconds. +-- * After 10 seconds, i.e. when 5 seconds of the old suppression are left, the group gets hit a again. +-- * A new suppression time is calculated which can be smaller or larger than the remaining 5 seconds. +-- * If the new suppression time is smaller, e.g. three seconds, than five seconds, the group will recover after the 5 remaining seconds of the first suppression have passed. +-- * If the new suppression time is longer than last suppression time, e.g. 10 seconds, then the group will recover after the 10 seconds of the new hit have passed. +-- +-- Generally speaking, the suppression times are not just added on top of each other. Because this could easily lead to the situation that a group +-- never becomes CombatReady again before it gets destroyed. +-- +-- The mission designer can capture the event **Recovered** by the function @{#SUPPRESSION.OnAfterRecovered}(). +-- +-- ## Flee Events and States +-- Apart from being suppressed the groups can also flee from the enemy under certain conditions. +-- +-- ### Event Retreat +-- The first option is a retreat. This can be enabled by setting a retreat zone, i.e. a trigger zone defined in the mission editor. +-- +-- If the group takes a certain amount of damage, the event **Retreat** will be called and the group will start to move to the retreat zone. +-- The group will be in the state **Retreating**, which means that its ROE is set to "Weapon Hold" and the alarm state is set to "Green". +-- Setting the alarm state to green is necessary to enable the group to move under fire. +-- +-- When the group has reached the retreat zone, the event **Retreated** is triggered and the state will change to **Retreated** (note that both the event and +-- the state of the same name in this case). ROE and alarm state are +-- set to "Return Fire" and "Auto", respectively. The group will stay in the retreat zone and not actively participate in the combat any more. +-- +-- If no option retreat zone has been specified, the option retreat is not available. +-- +-- The mission designer can capture the events **Retreat** and **Retreated** by the functions @{#SUPPRESSION.OnAfterRetreat}() and @{#SUPPRESSION.OnAfterRetreated}(). +-- +-- ### Fallback +-- +-- If a group is attacked by another ground group, it has the option to fall back, i.e. move away from the enemy. The probability of the event **FallBack** to +-- happen depends on the damage of the group that was hit. The more a group gets damaged, the more likely **FallBack** event becomes. +-- +-- If the group enters the state **FallingBack** it will move 100 meters in the opposite direction of the attacking unit. ROE and alarmstate are set to "Weapon Hold" +-- and "Green", respectively. +-- +-- At the fallback point the group will wait for 60 seconds before it resumes its normal mission. +-- +-- The mission designer can capture the event **FallBack** by the function @{#SUPPRESSION.OnAfterFallBack}(). +-- +-- ### TakeCover +-- +-- If a group is hit by either another ground or air unit, it has the option to "take cover" or "hide". This means that the group will move to a random +-- scenery object in it vicinity. +-- +-- Analogously to the fall back case, the probability of a **TakeCover** event to occur, depends on the damage of the group. The more a group is damaged, the more +-- likely it becomes that a group takes cover. +-- +-- When a **TakeCover** event occurs an area with a radius of 300 meters around the hit group is searched for an arbitrary scenery object. +-- If at least one scenery object is found, the group will move there. One it has reached its "hideout", it will wait there for two minutes before it resumes its +-- normal mission. +-- +-- If more than one scenery object is found, the group will move to a random one. +-- If no scenery object is near the group the **TakeCover** event is rejected and the group will not move. +-- +-- The mission designer can capture the event **TakeCover** by the function @{#SUPPRESSION.OnAfterTakeCover}(). +-- +-- ### Choice of FallBack or TakeCover if both are enabled? +-- +-- If both **FallBack** and **TakeCover** events are enabled by the functions @{#SUPPRESSION.Fallback}() and @{#SUPPRESSION.Takecover}() the algorithm does the following: +-- +-- * If the attacking unit is a ground unit, then the **FallBack** event is executed. +-- * Otherwise, i.e. if the attacker is *not* a ground unit, then the **TakeCover** event is triggered. +-- +-- ### FightBack +-- +-- When a group leaves the states **TakingCover** or **FallingBack** the event **FightBack** is triggered. This changes the ROE and the alarm state back to their default values. +-- +-- The mission designer can capture the event **FightBack** by the function @{#SUPPRESSION.OnAfterFightBack}() +-- +-- # Examples +-- +-- ## Simple Suppression +-- This example shows the basic steps to use suppressive fire for a group. +-- +-- ![Process](..\Presentations\SUPPRESSION\Suppression_Example_01.png) +-- +-- ## Suppression and Rescure +-- This example shows how the event **Retreat** can be captured. Here, a transport is started which picks up the wounded troups and drives them to a safe zone. +-- +-- ![Process](..\Presentations\SUPPRESSION\Suppression_Rescue.png) +-- +-- # Customization and Fine Tuning +-- The following user functions can be used to change the default values +-- +-- * @{#SUPPRESSION.SetSuppressionTime}() can be used to set the time a goup gets suppressed. +-- * @{#SUPPRESSION.SetRetreatZone}() sets the retreat zone and enables the possiblity for the group to retreat. +-- * @{#SUPPRESSION.SetFallbackDistance}() sets a value how far the unit moves away from the attacker after the fallback event. +-- * @{#SUPPRESSION.SetFallbackWait}() sets the time after which the group resumes its mission after a FallBack event. +-- * @{#SUPPRESSION.SetTakecoverWait}() sets the time after which the group resumes its mission after a TakeCover event. +-- * @{#SUPPRESSION.SetTakecoverRange}() sets the radius in which hideouts are searched. +-- * @{#SUPPRESSION.SetTakecoverPlace}() explicitly sets the place where the group will run at a TakeCover event. +-- * @{#SUPPRESSION.SetMinimumFleeProbability}() sets the minimum probability that a group flees (FallBack or TakeCover) after a hit. Note taht the probability increases with damage. +-- * @{#SUPPRESSION.SetMaximumFleeProbability}() sets the maximum probability that a group flees (FallBack or TakeCover) after a hit. Default is 90%. +-- * @{#SUPPRESSION.SetRetreatDamage}() sets the damage a group/unit can take before it is ordered to retreat. +-- * @{#SUPPRESSION.SetRetreatWait}() sets the time a group waits in the retreat zone after a retreat. +-- * @{#SUPPRESSION.SetDefaultAlarmState}() sets the alarm state a group gets after it becomes CombatReady again. +-- * @{#SUPPRESSION.SetDefaultROE}() set the rules of engagement a group gets after it becomes CombatReady again. +-- * @{#SUPPRESSION.FlareOn}() is mainly for debugging. A flare is fired when a unit is hit, gets suppressed, recovers, dies. +-- * @{#SUPPRESSION.SmokeOn}() is mainly for debugging. Puts smoke on retreat zone, hideouts etc. +-- * @{#SUPPRESSION.MenuON}() is mainly for debugging. Activates a radio menu item where certain functions like retreat etc. can be triggered manually. +-- +-- +-- @field #SUPPRESSION +SUPPRESSION={ + ClassName = "SUPPRESSION", + Debug = false, + flare = false, + smoke = false, + DCSdesc = nil, + Type = nil, + IsInfantry=nil, + SpeedMax = nil, + Tsuppress_ave = 15, + Tsuppress_min = 5, + Tsuppress_max = 25, + TsuppressOver = nil, + IniGroupStrength = nil, + Nhit = 0, + Formation = "Off road", + Speed = 4, + MenuON = false, + FallbackON = false, + FallbackWait = 60, + FallbackDist = 100, + FallbackHeading = nil, + TakecoverON = false, + TakecoverWait = 120, + TakecoverRange = 300, + hideout = nil, + PminFlee = 10, + PmaxFlee = 90, + RetreatZone = nil, + RetreatDamage = nil, + RetreatWait = 7200, + CurrentAlarmState = "unknown", + CurrentROE = "unknown", + DefaultAlarmState = "Auto", + DefaultROE = "Weapon Free", + eventmoose = true, +} + +--- Enumerator of possible rules of engagement. +-- @field #list ROE +SUPPRESSION.ROE={ + Hold="Weapon Hold", + Free="Weapon Free", + Return="Return Fire", +} + +--- Enumerator of possible alarm states. +-- @field #list AlarmState +SUPPRESSION.AlarmState={ + Auto="Auto", + Green="Green", + Red="Red", +} + +--- Main F10 menu for suppresion, i.e. F10/Suppression. +-- @field #string MenuF10 +SUPPRESSION.MenuF10=nil + +--- Some ID to identify who we are in output of the DCS.log file. +-- @field #string id +SUPPRESSION.id="SFX | " + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--TODO: Figure out who was shooting and move away from him. +--TODO: Move behind a scenery building if there is one nearby. +--TODO: Retreat to a given zone or point. + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Creates a new AI_suppression object. +-- @param #SUPPRESSION self +-- @param Wrapper.Group#GROUP group The GROUP object for which suppression should be applied. +-- @return #SUPPRESSION SUPPRESSION object. +-- @return nil If group does not exist or is not a ground group. +function SUPPRESSION:New(group) + BASE:F2(group) + + -- Inherits from FSM_CONTROLLABLE + local self=BASE:Inherit(self, FSM_CONTROLLABLE:New()) -- #SUPPRESSION + + -- Check that group is present. + if group then + self:T(SUPPRESSION.id.."Suppressive fire for group "..group:GetName()) + else + self:E(SUPPRESSION.id.."Suppressive fire: Requested group does not exist! (Has to be a MOOSE group.)") + return nil + end + + -- Check that we actually have a GROUND group. + if group:IsGround()==false then + self:E(SUPPRESSION.id.."SUPPRESSION fire group "..group:GetName().." has to be a GROUND group!") + return nil + end + + -- Set the controllable for the FSM. + self:SetControllable(group) + + -- Get DCS descriptors of group. + local DCSgroup=Group.getByName(group:GetName()) + local DCSunit=DCSgroup:getUnit(1) + self.DCSdesc=DCSunit:getDesc() + + -- Get max speed the group can do and convert to km/h. + self.SpeedMax=self.DCSdesc.speedMaxOffRoad*3.6 + --self.SpeedMaxOffRoad=DCSdesc.speedMaxOffRoad + + -- Set speed to maximum. + self.Speed=self.SpeedMax + + -- Is this infantry or not. + self.IsInfantry=DCSunit:hasAttribute("Infantry") + + -- Type of group. + self.Type=group:GetTypeName() + + -- Initial group strength. + self.IniGroupStrength=#group:GetUnits() + + -- Set ROE and Alarm State. + self:SetDefaultROE("Free") + self:SetDefaultAlarmState("Auto") + + -- Transitions + self:AddTransition("*", "Start", "CombatReady") + self:AddTransition("CombatReady", "Hit", "Suppressed") + self:AddTransition("Suppressed", "Hit", "Suppressed") + self:AddTransition("Suppressed", "Recovered", "CombatReady") + self:AddTransition("Suppressed", "TakeCover", "TakingCover") + self:AddTransition("Suppressed", "FallBack", "FallingBack") + self:AddTransition("*", "Retreat", "Retreating") + self:AddTransition("TakingCover", "FightBack", "CombatReady") + self:AddTransition("FallingBack", "FightBack", "CombatReady") + self:AddTransition("Retreating", "Retreated", "Retreated") + self:AddTransition("*", "Dead", "*") + + + --- User function for OnBefore "Hit" event. + -- @function [parent=#SUPPRESSION] OnBeforeHit + -- @param #SUPPRESSION self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Unit#UNIT Unit Unit that was hit. + -- @param Wrapper.Unit#UNIT AttackUnit Unit that attacked. + -- @return #boolean + + --- User function for OnAfer "Hit" event. + -- @function [parent=#SUPPRESSION] OnAfterHit + -- @param #SUPPRESSION self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Unit#UNIT Unit Unit that was hit. + -- @param Wrapper.Unit#UNIT AttackUnit Unit that attacked. + + + --- User function for OnBefore "Recovered" event. + -- @function [parent=#SUPPRESSION] OnBeforeRecovered + -- @param #SUPPRESSION self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @return #boolean + + --- User function for OnAfter "Recovered" event. + -- @function [parent=#SUPPRESSION] OnAfterRecovered + -- @param #SUPPRESSION self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + + --- User function for OnBefore "TakeCover" event. + -- @function [parent=#SUPPRESSION] OnBeforeTakeCover + -- @param #SUPPRESSION self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Core.Point#COORDINATE Hideout Place where the group will hide. + -- @return #boolean + + --- User function for OnAfter "TakeCover" event. + -- @function [parent=#SUPPRESSION] OnAfterTakeCover + -- @param #SUPPRESSION self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Core.Point#COORDINATE Hideout Place where the group will hide. + + + --- User function for OnBefore "FallBack" event. + -- @function [parent=#SUPPRESSION] OnBeforeFallBack + -- @param #SUPPRESSION self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Unit#UNIT AttackUnit Attacking unit. We will move away from this. + -- @return #boolean + + --- User function for OnAfter "FallBack" event. + -- @function [parent=#SUPPRESSION] OnAfterFallBack + -- @param #SUPPRESSION self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Unit#UNIT AttackUnit Attacking unit. We will move away from this. + + + --- User function for OnBefore "Retreat" event. + -- @function [parent=#SUPPRESSION] OnBeforeRetreat + -- @param #SUPPRESSION self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @return #boolean + + --- User function for OnAfter "Retreat" event. + -- @function [parent=#SUPPRESSION] OnAfterRetreat + -- @param #SUPPRESSION self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + + --- User function for OnBefore "Retreated" event. + -- @function [parent=#SUPPRESSION] OnBeforeRetreated + -- @param #SUPPRESSION self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @return #boolean + + --- User function for OnAfter "Retreated" event. + -- @function [parent=#SUPPRESSION] OnAfterRetreated + -- @param #SUPPRESSION self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + + --- User function for OnBefore "FlightBack" event. + -- @function [parent=#SUPPRESSION] OnBeforeFightBack + -- @param #SUPPRESSION self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @return #boolean + + --- User function for OnAfter "FlightBack" event. + -- @function [parent=#SUPPRESSION] OnAfterFightBack + -- @param #SUPPRESSION self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Set average, minimum and maximum time a unit is suppressed each time it gets hit. +-- @param #SUPPRESSION self +-- @param #number Tave Average time [seconds] a group will be suppressed. Default is 15 seconds. +-- @param #number Tmin (Optional) Minimum time [seconds] a group will be suppressed. Default is 5 seconds. +-- @param #number Tmax (Optional) Maximum time a group will be suppressed. Default is 25 seconds. +function SUPPRESSION:SetSuppressionTime(Tave, Tmin, Tmax) + + -- Minimum suppression time is input or default but at least 1 second. + self.Tsuppress_min=Tmin or self.Tsuppress_min + self.Tsuppress_min=math.max(self.Tsuppress_min, 1) + + -- Maximum suppression time is input or dault but at least Tmin. + self.Tsuppress_max=Tmax or self.Tsuppress_max + self.Tsuppress_max=math.max(self.Tsuppress_max, self.Tsuppress_min) + + -- Expected suppression time is input or default but at leat Tmin and at most Tmax. + self.Tsuppress_ave=Tave or self.Tsuppress_ave + self.Tsuppress_ave=math.max(self.Tsuppress_min) + self.Tsuppress_ave=math.min(self.Tsuppress_max) + + self:T(SUPPRESSION.id..string.format("Set ave suppression time to %d seconds.", self.Tsuppress_ave)) + self:T(SUPPRESSION.id..string.format("Set min suppression time to %d seconds.", self.Tsuppress_min)) + self:T(SUPPRESSION.id..string.format("Set max suppression time to %d seconds.", self.Tsuppress_max)) +end + +--- Set the zone to which a group retreats after being damaged too much. +-- @param #SUPPRESSION self +-- @param Core.Zone#ZONE zone MOOSE zone object. +function SUPPRESSION:SetRetreatZone(zone) + self.RetreatZone=zone +end + +--- Turn Debug mode on. Enables messages and more output to DCS log file. +-- @param #SUPPRESSION self +function SUPPRESSION:DebugOn() + self.Debug=true +end + +--- Flare units when they are hit, die or recover from suppression. +-- @param #SUPPRESSION self +function SUPPRESSION:FlareOn() + self.flare=true +end + +--- Smoke positions where units fall back to, hide or retreat. +-- @param #SUPPRESSION self +function SUPPRESSION:SmokeOn() + self.smoke=true +end + +--- Set the formation a group uses for fall back, hide or retreat. +-- @param #SUPPRESSION self +-- @param #string formation Formation of the group. Default "Vee". +function SUPPRESSION:SetFormation(formation) + self.Formation=formation or "Vee" +end + +--- Set speed a group moves at for fall back, hide or retreat. +-- @param #SUPPRESSION self +-- @param #number speed Speed in km/h of group. Default max speed the group can do. +function SUPPRESSION:SetSpeed(speed) + self.Speed=speed or self.SpeedMax + self.Speed=math.min(self.Speed, self.SpeedMax) +end + +--- Enable fall back if a group is hit. +-- @param #SUPPRESSION self +-- @param #boolean switch Enable=true or disable=false fall back of group. +function SUPPRESSION:Fallback(switch) + if switch==nil then + switch=true + end + self.FallbackON=switch +end + +--- Set distance a group will fall back when it gets hit. +-- @param #SUPPRESSION self +-- @param #number distance Distance in meters. +function SUPPRESSION:SetFallbackDistance(distance) + self.FallbackDist=distance +end + +--- Set time a group waits at its fall back position before it resumes its normal mission. +-- @param #SUPPRESSION self +-- @param #number time Time in seconds. +function SUPPRESSION:SetFallbackWait(time) + self.FallbackWait=time +end + +--- Enable take cover option if a unit is hit. +-- @param #SUPPRESSION self +-- @param #boolean switch Enable=true or disable=false fall back of group. +function SUPPRESSION:Takecover(switch) + if switch==nil then + switch=true + end + self.TakecoverON=switch +end + +--- Set time a group waits at its hideout position before it resumes its normal mission. +-- @param #SUPPRESSION self +-- @param #number time Time in seconds. +function SUPPRESSION:SetTakecoverWait(time) + self.TakecoverWait=time +end + +--- Set distance a group searches for hideout places. +-- @param #SUPPRESSION self +-- @param #number range Search range in meters. +function SUPPRESSION:SetTakecoverRange(range) + self.TakecoverRange=range +end + +--- Set hideout place explicitly. +-- @param #SUPPRESSION self +-- @param Core.Point#COORDINATE Hideout Place where the group will hide after the TakeCover event. +function SUPPRESSION:SetTakecoverPlace(Hideout) + self.hideout=Hideout +end + +--- Set minimum probability that a group flees (falls back or takes cover) after a hit event. Default is 10%. +-- @param #SUPPRESSION self +-- @param #number probability Probability in percent. +function SUPPRESSION:SetMinimumFleeProbability(probability) + self.PminFlee=probability or 10 +end + +--- Set maximum probability that a group flees (falls back or takes cover) after a hit event. Default is 90%. +-- @param #SUPPRESSION self +-- @param #number probability Probability in percent. +function SUPPRESSION:SetMaximumFleeProbability(probability) + self.PmaxFlee=probability or 90 +end + +--- Set damage threshold before a group is ordered to retreat if a retreat zone was defined. +-- If the group consists of only a singe unit, this referrs to the life of the unit. +-- If the group consists of more than one unit, this referrs to the group strength relative to its initial strength. +-- @param #SUPPRESSION self +-- @param #number damage Damage in percent. If group gets damaged above this value, the group will retreat. Default 50 %. +function SUPPRESSION:SetRetreatDamage(damage) + self.RetreatDamage=damage or 50 +end + +--- Set time a group waits in the retreat zone before it resumes its mission. Default is two hours. +-- @param #SUPPRESSION self +-- @param #number time Time in seconds. Default 7200 seconds = 2 hours. +function SUPPRESSION:SetRetreatWait(time) + self.RetreatWait=time or 7200 +end + +--- Set alarm state a group will get after it returns from a fall back or take cover. +-- @param #SUPPRESSION self +-- @param #string alarmstate Alarm state. Possible "Auto", "Green", "Red". Default is "Auto". +function SUPPRESSION:SetDefaultAlarmState(alarmstate) + if alarmstate:lower()=="auto" then + self.DefaultAlarmState=SUPPRESSION.AlarmState.Auto + elseif alarmstate:lower()=="green" then + self.DefaultAlarmState=SUPPRESSION.AlarmState.Green + elseif alarmstate:lower()=="red" then + self.DefaultAlarmState=SUPPRESSION.AlarmState.Red + else + self.DefaultAlarmState=SUPPRESSION.AlarmState.Auto + end +end + +--- Set Rules of Engagement (ROE) a group will get when it recovers from suppression. +-- @param #SUPPRESSION self +-- @param #string roe ROE after suppression. Possible "Free", "Hold" or "Return". Default "Free". +function SUPPRESSION:SetDefaultROE(roe) + if roe:lower()=="free" then + self.DefaultROE=SUPPRESSION.ROE.Free + elseif roe:lower()=="hold" then + self.DefaultROE=SUPPRESSION.ROE.Hold + elseif roe:lower()=="return" then + self.DefaultROE=SUPPRESSION.ROE.Return + else + self.DefaultROE=SUPPRESSION.ROE.Free + end +end + +--- Create an F10 menu entry for the suppressed group. The menu is mainly for Debugging purposes. +-- @param #SUPPRESSION self +-- @param #boolean switch Enable=true or disable=false menu group. Default is true. +function SUPPRESSION:MenuOn(switch) + if switch==nil then + switch=true + end + self.MenuON=switch +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Create F10 main menu, i.e. F10/Suppression. The menu is mainly for Debugging purposes. +-- @param #SUPPRESSION self +function SUPPRESSION:_CreateMenuGroup() + local SubMenuName=self.Controllable:GetName() + local MenuGroup=MENU_MISSION:New(SubMenuName, SUPPRESSION.MenuF10) + MENU_MISSION_COMMAND:New("Fallback!", MenuGroup, self.OrderFallBack, self) + MENU_MISSION_COMMAND:New("Take Cover!", MenuGroup, self.OrderTakeCover, self) + MENU_MISSION_COMMAND:New("Retreat!", MenuGroup, self.OrderRetreat, self) + MENU_MISSION_COMMAND:New("Report Status", MenuGroup, self.Status, self, true) +end + +--- Order group to fall back between 100 and 150 meters in a random direction. +-- @param #SUPPRESSION self +function SUPPRESSION:OrderFallBack() + local group=self.Controllable --Wrapper.Controllable#CONTROLLABLE + local vicinity=group:GetCoordinate():GetRandomVec2InRadius(150, 100) + local coord=COORDINATE:NewFromVec2(vicinity) + self:FallBack(self.Controllable) +end + +--- Order group to take cover at a nearby scenery object. +-- @param #SUPPRESSION self +function SUPPRESSION:OrderTakeCover() + -- Search place to hide or take specified one. + local Hideout=self.hideout + if self.hideout==nil then + Hideout=self:_SearchHideout() + end + -- Trigger TakeCover event. + self:TakeCover(Hideout) +end + +--- Order group to retreat to a pre-defined zone. +-- @param #SUPPRESSION self +function SUPPRESSION:OrderRetreat() + self:Retreat() +end + +--- Status of group. Current ROE, alarm state, life. +-- @param #SUPPRESSION self +-- @param #boolean message Send message to all players. +function SUPPRESSION:Status(message) + + local name=self.Controllable:GetName() + local nunits=#self.Controllable:GetUnits() + local roe=self.CurrentROE + local state=self.CurrentAlarmState + local life_min, life_max, life_ave, life_ave0, groupstrength=self:_GetLife() + + local text=string.format("Status of group %s\n", name) + text=text..string.format("Number of units: %d of %d\n", nunits, self.IniGroupStrength) + text=text..string.format("Current state: %s\n", self:GetState()) + text=text..string.format("ROE: %s\n", roe) + text=text..string.format("Alarm state: %s\n", state) + text=text..string.format("Hits taken: %d\n", self.Nhit) + text=text..string.format("Life min: %3.0f\n", life_min) + text=text..string.format("Life max: %3.0f\n", life_max) + text=text..string.format("Life ave: %3.0f\n", life_ave) + text=text..string.format("Life ave0: %3.0f\n", life_ave0) + text=text..string.format("Group strength: %3.0f", groupstrength) + + MESSAGE:New(text, 10):ToAllIf(message or self.Debug) + self:T(SUPPRESSION.id.."\n"..text) +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- After "Start" event. Initialized ROE and alarm state. Starts the event handler. +-- @param #SUPPRESSION self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function SUPPRESSION:onafterStart(Controllable, From, Event, To) + self:_EventFromTo("onafterStart", Event, From, To) + + local text=string.format("Started SUPPRESSION for group %s.", Controllable:GetName()) + MESSAGE:New(text, 10):ToAllIf(self.Debug) + + local rzone="not defined" + if self.RetreatZone then + rzone=self.RetreatZone:GetName() + end + + -- Set retreat damage value if it was not set by user input. + if self.RetreatDamage==nil then + if self.RetreatZone then + if self.IniGroupStrength==1 then + self.RetreatDamage=60.0 -- 40% of life is left. + elseif self.IniGroupStrength==2 then + self.RetreatDamage=50.0 -- 50% of group left, i.e. 1 of 2. We already order a retreat, because if for a group 2 two a zone is defined it would not be used at all. + else + self.RetreatDamage=66.5 -- 34% of the group is left, e.g. 1 of 3,4 or 5, 2 of 6,7 or 8, 3 of 9,10 or 11, 4/12, 4/13, 4/14, 5/15, ... + end + else + self.RetreatDamage=100 -- If no retreat then this should be set to 100%. + end + end + + -- Create main F10 menu if it is not there yet. + if self.MenuON then + if not SUPPRESSION.MenuF10 then + SUPPRESSION.MenuF10 = MENU_MISSION:New("Suppression") + end + self:_CreateMenuGroup() + end + + -- Set the current ROE and alam state. + self:_SetAlarmState(self.DefaultAlarmState) + self:_SetROE(self.DefaultROE) + + local text=string.format("\n******************************************************\n") + text=text..string.format("Suppressed group = %s\n", Controllable:GetName()) + text=text..string.format("Type = %s\n", self.Type) + text=text..string.format("IsInfantry = %s\n", tostring(self.IsInfantry)) + text=text..string.format("Group strength = %d\n", self.IniGroupStrength) + text=text..string.format("Average time = %5.1f seconds\n", self.Tsuppress_ave) + text=text..string.format("Minimum time = %5.1f seconds\n", self.Tsuppress_min) + text=text..string.format("Maximum time = %5.1f seconds\n", self.Tsuppress_max) + text=text..string.format("Default ROE = %s\n", self.DefaultROE) + text=text..string.format("Default AlarmState = %s\n", self.DefaultAlarmState) + text=text..string.format("Fall back ON = %s\n", tostring(self.FallbackON)) + text=text..string.format("Fall back distance = %5.1f m\n", self.FallbackDist) + text=text..string.format("Fall back wait = %5.1f seconds\n", self.FallbackWait) + text=text..string.format("Fall back heading = %s degrees\n", tostring(self.FallbackHeading)) + text=text..string.format("Take cover ON = %s\n", tostring(self.TakecoverON)) + text=text..string.format("Take cover search = %5.1f m\n", self.TakecoverRange) + text=text..string.format("Take cover wait = %5.1f seconds\n", self.TakecoverWait) + text=text..string.format("Min flee probability = %5.1f\n", self.PminFlee) + text=text..string.format("Max flee probability = %5.1f\n", self.PmaxFlee) + text=text..string.format("Retreat zone = %s\n", rzone) + text=text..string.format("Retreat damage = %5.1f %%\n", self.RetreatDamage) + text=text..string.format("Retreat wait = %5.1f seconds\n", self.RetreatWait) + text=text..string.format("Speed = %5.1f km/h\n", self.Speed) + text=text..string.format("Speed max = %5.1f km/h\n", self.SpeedMax) + text=text..string.format("Formation = %s\n", self.Formation) + text=text..string.format("******************************************************\n") + self:T(SUPPRESSION.id..text) + + -- Add event handler. + if self.eventmoose then + self:HandleEvent(EVENTS.Hit, self._OnEventHit) + self:HandleEvent(EVENTS.Dead, self._OnEventDead) + else + world.addEventHandler(self) + end + +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Before "Hit" event. (Of course, this is not really before the group got hit.) +-- @param #SUPPRESSION self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Wrapper.Unit#UNIT Unit Unit that was hit. +-- @param Wrapper.Unit#UNIT AttackUnit Unit that attacked. +-- @return boolean +function SUPPRESSION:onbeforeHit(Controllable, From, Event, To, Unit, AttackUnit) + self:_EventFromTo("onbeforeHit", Event, From, To) + + --local Tnow=timer.getTime() + --env.info(SUPPRESSION.id..string.format("Last hit = %s %s", tostring(self.LastHit), tostring(Tnow))) + + return true +end + +--- After "Hit" event. +-- @param #SUPPRESSION self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Wrapper.Unit#UNIT Unit Unit that was hit. +-- @param Wrapper.Unit#UNIT AttackUnit Unit that attacked. +function SUPPRESSION:onafterHit(Controllable, From, Event, To, Unit, AttackUnit) + self:_EventFromTo("onafterHit", Event, From, To) + + -- Suppress unit. + if From=="CombatReady" or From=="Suppressed" then + self:_Suppress() + end + + -- Get life of group in %. + local life_min, life_max, life_ave, life_ave0, groupstrength=self:_GetLife() + + -- Damage in %. If group consists only of one unit, we take its life value. + local Damage=100-life_ave0 + + -- Condition for retreat. + local RetreatCondition = Damage >= self.RetreatDamage-0.01 and self.RetreatZone + + -- Probability that a unit flees. The probability increases linearly with the damage of the group/unit. + -- If Damage=0 ==> P=Pmin + -- if Damage=RetreatDamage ==> P=Pmax + -- If no retreat zone has been specified, RetreatDamage is 100. + local Pflee=(self.PmaxFlee-self.PminFlee)/self.RetreatDamage * math.min(Damage, self.RetreatDamage) + self.PminFlee + + -- Evaluate flee condition. + local P=math.random(0,100) + local FleeCondition = P < Pflee + + local text + text=string.format("\nGroup %s: Life min=%5.1f, max=%5.1f, ave=%5.1f, ave0=%5.1f group=%5.1f\n", Controllable:GetName(), life_min, life_max, life_ave, life_ave0, groupstrength) + text=string.format("Group %s: Damage = %8.4f (%8.4f retreat threshold).\n", Controllable:GetName(), Damage, self.RetreatDamage) + text=string.format("Group %s: P_Flee = %5.1f %5.1f=P_rand (P_Flee > Prand ==> Flee)\n", Controllable:GetName(), Pflee, P) + self:T(SUPPRESSION.id..text) + + -- Group is obviously destroyed. + if Damage >= 99.9 then + return + end + + if RetreatCondition then + + -- Trigger Retreat event. + self:Retreat() + + elseif FleeCondition then + + if self.FallbackON and AttackUnit:IsGround() then + + -- Trigger FallBack event. + self:FallBack(AttackUnit) + + elseif self.TakecoverON then + + -- Search place to hide or take specified one. + local Hideout=self.hideout + if self.hideout==nil then + Hideout=self:_SearchHideout() + end + + -- Trigger TakeCover event. + self:TakeCover(Hideout) + end + end + + -- Give info on current status. + if self.Debug then + self:Status() + end + +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Before "Recovered" event. Check if suppression time is over. +-- @param #SUPPRESSION self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @return #boolean +function SUPPRESSION:onbeforeRecovered(Controllable, From, Event, To) + self:_EventFromTo("onbeforeRecovered", Event, From, To) + + -- Current time. + local Tnow=timer.getTime() + + -- Debug info + self:T(SUPPRESSION.id..string.format("onbeforeRecovered: Time now: %d - Time over: %d", Tnow, self.TsuppressionOver)) + + -- Recovery is only possible if enough time since the last hit has passed. + if Tnow >= self.TsuppressionOver then + return true + else + return false + end + +end + +--- After "Recovered" event. Group has recovered and its ROE is set back to the "normal" unsuppressed state. Optionally the group is flared green. +-- @param #SUPPRESSION self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function SUPPRESSION:onafterRecovered(Controllable, From, Event, To) + self:_EventFromTo("onafterRecovered", Event, From, To) + + if Controllable and Controllable:IsAlive() then + + -- Debug message. + local text=string.format("Group %s has recovered!", Controllable:GetName()) + MESSAGE:New(text, 10):ToAllIf(self.Debug) + self:T(SUPPRESSION.id..text) + + -- Set ROE back to default. + self:_SetROE() + + -- Flare unit green. + if self.flare or self.Debug then + Controllable:FlareGreen() + end + + end +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- After "FightBack" event. ROE and Alarm state are set back to default. +-- @param #SUPPRESSION self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function SUPPRESSION:onafterFightBack(Controllable, From, Event, To) + self:_EventFromTo("onafterFightBack", Event, From, To) + + -- Set ROE and alarm state back to default. + self:_SetROE() + self:_SetAlarmState() +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Before "FallBack" event. We check that group is not already falling back. +-- @param #SUPPRESSION self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Wrapper.Unit#UNIT AttackUnit Attacking unit. We will move away from this. +-- @return #boolean +function SUPPRESSION:onbeforeFallBack(Controllable, From, Event, To, AttackUnit) + self:_EventFromTo("onbeforeFallBack", Event, From, To) + + --TODO: Add retreat? Only allowd transition is Suppressed-->Fallback. So in principle no need. + if From == "FallingBack" then + return false + else + return true + end +end + +--- After "FallBack" event. We get the heading away from the attacker and route the group a certain distance in that direction. +-- @param #SUPPRESSION self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Wrapper.Unit#UNIT AttackUnit Attacking unit. We will move away from this. +function SUPPRESSION:onafterFallBack(Controllable, From, Event, To, AttackUnit) + self:_EventFromTo("onafterFallback", Event, From, To) + + -- Debug info + self:T(SUPPRESSION.id..string.format("Group %s is falling back after %d hits.", Controllable:GetName(), self.Nhit)) + + -- Coordinate of the attacker and attacked unit. + local ACoord=AttackUnit:GetCoordinate() + local DCoord=Controllable:GetCoordinate() + + -- Heading from attacker to attacked unit. + local heading=self:_Heading(ACoord, DCoord) + + -- Overwrite heading with user specified heading. + if self.FallbackHeading then + heading=self.FallbackHeading + end + + -- Create a coordinate ~ 100 m in opposite direction of the attacking unit. + local Coord=DCoord:Translate(self.FallbackDist, heading) + + -- Place marker + local MarkerID=Coord:MarkToAll("Fall back position for group "..Controllable:GetName()) + + -- Smoke the coordinate. + if self.smoke or self.Debug then + Coord:SmokeBlue() + end + + -- Set ROE to weapon hold. + self:_SetROE(SUPPRESSION.ROE.Hold) + + -- Set alarm state to GREEN and let the unit run away. + self:_SetAlarmState(SUPPRESSION.AlarmState.Green) + + -- Make the group run away. + self:_Run(Coord, self.Speed, self.Formation, self.FallbackWait) + +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Before "TakeCover" event. Search an area around the group for possible scenery objects where the group can hide. +-- @param #SUPPRESSION self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Core.Point#COORDINATE Hideout Place where the group will hide. +-- @return #boolean +function SUPPRESSION:onbeforeTakeCover(Controllable, From, Event, To, Hideout) + self:_EventFromTo("onbeforeTakeCover", Event, From, To) + + --TODO: Need to test this! + if From=="TakingCover" then + return false + end + + -- Block transition if no hideout place is given. + if Hideout ~= nil then + return true + else + return false + end + +end + +--- After "TakeCover" event. Group will run to a nearby scenery object and "hide" there for a certain time. +-- @param #SUPPRESSION self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Core.Point#COORDINATE Hideout Place where the group will hide. +function SUPPRESSION:onafterTakeCover(Controllable, From, Event, To, Hideout) + self:_EventFromTo("onafterTakeCover", Event, From, To) + + if self.Debug then + local MarkerID=Hideout:MarkToAll(string.format("Hideout for group %s", Controllable:GetName())) + end + + -- Smoke place of hideout. + if self.smoke or self.Debug then + Hideout:SmokeBlue() + end + + -- Set ROE to weapon hold. + self:_SetROE(SUPPRESSION.ROE.Hold) + + -- Set the ALARM STATE to GREEN. Then the unit will move even if it is under fire. + self:_SetAlarmState(SUPPRESSION.AlarmState.Green) + + -- Make the group run away. + self:_Run(Hideout, self.Speed, self.Formation, self.TakecoverWait) + +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Before "Retreat" event. We check that the group is not already retreating. +-- @param #SUPPRESSION self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @return #boolean True if transition is allowed, False if transition is forbidden. +function SUPPRESSION:onbeforeRetreat(Controllable, From, Event, To) + self:_EventFromTo("onbeforeRetreat", Event, From, To) + + if From=="Retreating" then + local text=string.format("Group %s is already retreating.") + self:T2(SUPPRESSION.id..text) + return false + else + return true + end + +end + +--- After "Retreat" event. Find a random point in the retreat zone and route the group there. +-- @param #SUPPRESSION self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function SUPPRESSION:onafterRetreat(Controllable, From, Event, To) + self:_EventFromTo("onafterRetreat", Event, From, To) + + -- Route the group to a zone. + local text=string.format("Group %s is retreating! Alarm state green.", Controllable:GetName()) + MESSAGE:New(text, 10):ToAllIf(self.Debug) + self:T(SUPPRESSION.id..text) + + -- Get a random point in the retreat zone. + local ZoneCoord=self.RetreatZone:GetRandomCoordinate() -- Core.Point#COORDINATE + local ZoneVec2=ZoneCoord:GetVec2() + + -- Debug smoke zone and point. + if self.smoke or self.Debug then + ZoneCoord:SmokeBlue() + end + if self.Debug then + self.RetreatZone:SmokeZone(SMOKECOLOR.Red, 12) + end + + -- Set ROE to weapon hold. + self:_SetROE(SUPPRESSION.ROE.Hold) + + -- Set the ALARM STATE to GREEN. Then the unit will move even if it is under fire. + self:_SetAlarmState(SUPPRESSION.AlarmState.Green) + + -- Make unit run to retreat zone and wait there for ~two hours. + self:_Run(ZoneCoord, self.Speed, self.Formation, self.RetreatWait) + +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Before "Retreateded" event. Check that the group is really in the retreat zone. +-- @param #SUPPRESSION self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function SUPPRESSION:onbeforeRetreated(Controllable, From, Event, To) + self:_EventFromTo("onbeforeRetreated", Event, From, To) + + -- Check that the group is inside the zone. + local inzone=self.RetreatZone:IsVec3InZone(Controllable:GetVec3()) + + return inzone +end + +--- After "Retreateded" event. Group has reached the retreat zone. Set ROE to return fire and alarm state to auto. +-- @param #SUPPRESSION self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function SUPPRESSION:onafterRetreated(Controllable, From, Event, To) + self:_EventFromTo("onafterRetreated", Event, From, To) + + -- Set ROE to weapon return fire. + self:_SetROE(SUPPRESSION.ROE.Return) + + -- Set the ALARM STATE to GREEN. Then the unit will move even if it is under fire. + self:_SetAlarmState(SUPPRESSION.AlarmState.Auto) + + -- TODO: Add hold task? Move from _Run() +end + + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- After "Dead" event, when a unit has died. When all units of a group are dead, FSM is stopped and eventhandler removed. +-- @param #SUPPRESSION self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function SUPPRESSION:onafterDead(Controllable, From, Event, To) + self:_EventFromTo("onafterDead", Event, From, To) + + -- Number of units left in the group. + local nunits=#self.Controllable:GetUnits() + + local text=string.format("Group %s: One of our units just died! %d units left.", self.Controllable:GetName(), nunits) + MESSAGE:New(text, 10):ToAllIf(self.Debug) + self:T(SUPPRESSION.id..text) + + -- Go to stop state. + if nunits==0 then + self:T(SUPPRESSION.id..string.format("Stopping SUPPRESSION for group %s.", Controllable:GetName())) + self:Stop() + if self.mooseevents then + self:UnHandleEvent(EVENTS.Dead) + self:UnHandleEvent(EVENTS.Hit) + else + world.removeEventHandler(self) + end + end + +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Event Handler +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Event handler for suppressed groups. +--@param #SUPPRESSION self +function SUPPRESSION:onEvent(Event) + --self:E(event) + + if Event == nil or Event.initiator == nil or Unit.getByName(Event.initiator:getName()) == nil then + return true + end + + local EventData={} + if Event.initiator then + EventData.IniDCSUnit = Event.initiator + EventData.IniUnitName = Event.initiator:getName() + EventData.IniDCSGroup = Event.initiator:getGroup() + EventData.IniGroupName = Event.initiator:getGroup():getName() + EventData.IniGroup = GROUP:FindByName(EventData.IniGroupName) + EventData.IniUnit = UNIT:FindByName(EventData.IniUnitName) + end + + if Event.target then + EventData.TgtDCSUnit = Event.target + EventData.TgtUnitName = Event.target:getName() + EventData.TgtDCSGroup = Event.target:getGroup() + EventData.TgtGroupName = Event.target:getGroup():getName() + EventData.TgtGroup = GROUP:FindByName(EventData.TgtGroupName) + EventData.TgtUnit = UNIT:FindByName(EventData.TgtUnitName) + end + + + -- Event HIT + if Event.id == world.event.S_EVENT_HIT then + self:_OnEventHit(EventData) + end + + -- Event HIT + if Event.id == world.event.S_EVENT_DEAD then + self:_OnEventDead(EventData) + end + +end + +--- Event handler for Dead event of suppressed groups. +-- @param #SUPPRESSION self +-- @param Core.Event#EVENTDATA EventData +function SUPPRESSION:_OnEventHit(EventData) + + local GroupNameSelf=self.Controllable:GetName() + local GroupNameTgt=EventData.TgtGroupName + local TgtUnit=EventData.TgtUnit + local tgt=EventData.TgtDCSUnit + local IniUnit=EventData.IniUnit + + -- Check that correct group was hit. + if GroupNameTgt == GroupNameSelf then + + self:T2(SUPPRESSION.id..string.format("Hit event at t = %5.1f", timer.getTime())) + + -- Flare unit that was hit. + if self.flare or self.Debug then + TgtUnit:FlareRed() + end + + -- Increase Hit counter. + self.Nhit=self.Nhit+1 + + -- Info on hit times. + self:T(SUPPRESSION.id..string.format("Group %s has just been hit %d times.", self.Controllable:GetName(), self.Nhit)) + + --self:Status() + local life=tgt:getLife()/(tgt:getLife0()+1)*100 + self:T2(SUPPRESSION.id..string.format("Target unit life = %5.1f", life)) + + -- FSM Hit event. + self:__Hit(3, TgtUnit, IniUnit) + end + +end + +--- Event handler for Dead event of suppressed groups. +-- @param #SUPPRESSION self +-- @param Core.Event#EVENTDATA EventData +function SUPPRESSION:_OnEventDead(EventData) + + local GroupNameSelf=self.Controllable:GetName() + local GroupNameIni=EventData.IniGroupName + + -- Check for correct group. + if GroupNameIni== GroupNameSelf then + + -- Dead Unit. + local IniUnit=EventData.IniUnit --Wrapper.Unit#UNIT + local IniUnitName=EventData.IniUnitName + + if EventData.IniUnit then + self:T2(SUPPRESSION.id..string.format("Group %s: Dead MOOSE unit DOES exist! Unit name %s.", GroupNameIni, IniUnitName)) + else + self:T2(SUPPRESSION.id..string.format("Group %s: Dead MOOSE unit DOES NOT not exist! Unit name %s.", GroupNameIni, IniUnitName)) + end + + if EventData.IniDCSUnit then + self:T2(SUPPRESSION.id..string.format("Group %s: Dead DCS unit DOES exist! Unit name %s.", GroupNameIni, IniUnitName)) + else + self:T2(SUPPRESSION.id..string.format("Group %s: Dead DCS unit DOES NOT exist! Unit name %s.", GroupNameIni, IniUnitName)) + end + + -- Flare unit that died. + if IniUnit and (self.flare or self.Debug) then + IniUnit:FlareWhite() + self:T(SUPPRESSION.id..string.format("Flare Dead MOOSE unit.")) + end + + -- Flare unit that died. + if EventData.IniDCSUnit and (self.flare or self.Debug) then + local p=EventData.IniDCSUnit:getPosition().p + trigger.action.signalFlare(p, trigger.flareColor.Yellow , 0) + self:T(SUPPRESSION.id..string.format("Flare Dead DCS unit.")) + end + + -- Get status. + self:Status() + + -- FSM Dead event. + self:__Dead(0.1) + + end + +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Suppress fire of a unit by setting its ROE to "Weapon Hold". +-- @param #SUPPRESSION self +function SUPPRESSION:_Suppress() + + -- Current time. + local Tnow=timer.getTime() + + -- Controllable + local Controllable=self.Controllable --Wrapper.Controllable#CONTROLLABLE + + -- Group will hold their weapons. + self:_SetROE(SUPPRESSION.ROE.Hold) + + -- Get randomized time the unit is suppressed. + local sigma=(self.Tsuppress_max-self.Tsuppress_min)/4 + local Tsuppress=self:_Random_Gaussian(self.Tsuppress_ave,sigma,self.Tsuppress_min, self.Tsuppress_max) + + -- Time at which the suppression is over. + local renew=true + if self.TsuppressionOver ~= nil then + if Tsuppress+Tnow > self.TsuppressionOver then + self.TsuppressionOver=Tnow+Tsuppress + else + renew=false + end + else + self.TsuppressionOver=Tnow+Tsuppress + end + + -- Recovery event will be called in Tsuppress seconds. + if renew then + self:__Recovered(self.TsuppressionOver-Tnow) + end + + -- Debug message. + local text=string.format("Group %s is suppressed for %d seconds. Suppression ends at %d:%02d.", Controllable:GetName(), Tsuppress, self.TsuppressionOver/60, self.TsuppressionOver%60) + MESSAGE:New(text, 10):ToAllIf(self.Debug) + self:T(SUPPRESSION.id..text) + +end + + +--- Make group run/drive to a certain point. We put in several intermediate waypoints because sometimes the group stops before it arrived at the desired point. +--@param #SUPPRESSION self +--@param Core.Point#COORDINATE fin Coordinate where we want to go. +--@param #number speed Speed of group. Default is 999. +--@param #string formation Formation of group. Default is "Vee". +--@param #number wait Time the group will wait/hold at final waypoint. Default is 30 seconds. +function SUPPRESSION:_Run(fin, speed, formation, wait) + + speed=speed or 999 + formation=formation or "Vee" + wait=wait or 30 + + local group=self.Controllable -- Wrapper.Controllable#CONTROLLABLE + + -- Clear all tasks. + group:ClearTasks() + + -- Current coordinates of group. + local ini=group:GetCoordinate() + + -- Distance between current and final point. + local dist=ini:Get2DDistance(fin) + + -- Heading from ini to fin. + local heading=self:_Heading(ini, fin) + + -- Number of waypoints. + local nx + if dist <= 50 then + nx=2 + elseif dist <= 100 then + nx=3 + elseif dist <= 500 then + nx=4 + else + nx=5 + end + + -- Number of intermediate waypoints. + local dx=dist/(nx-1) + + -- Waypoint and task arrays. + local wp={} + local tasks={} + + -- First waypoint is the current position of the group. + wp[1]=ini:WaypointGround(speed, formation) + local MarkerID=ini:MarkToAll(string.format("Waypoing %d of group %s (initial)", #wp, self.Controllable:GetName())) + tasks[1]=group:TaskFunction("SUPPRESSION._Passing_Waypoint", self, 1, false) + + self:T2(SUPPRESSION.id..string.format("Number of waypoints %d", nx)) + for i=1,nx-2 do + + local x=dx*i + local coord=ini:Translate(x, heading) + + wp[#wp+1]=coord:WaypointGround(speed, formation) + tasks[#tasks+1]=group:TaskFunction("SUPPRESSION._Passing_Waypoint", self, #wp, false) + + self:T2(SUPPRESSION.id..string.format("%d x = %4.1f", i, x)) + if self.Debug then + local MarkerID=coord:MarkToAll(string.format("Waypoing %d of group %s", #wp, self.Controllable:GetName())) + end + + end + self:T2(SUPPRESSION.id..string.format("Total distance: %4.1f", dist)) + + -- Final waypoint. + wp[#wp+1]=fin:WaypointGround(speed, formation) + if self.Debug then + local MarkerID=fin:MarkToAll(string.format("Waypoing %d of group %s (final)", #wp, self.Controllable:GetName())) + end + + -- Task to hold. + local ConditionWait=group:TaskCondition(nil, nil, nil, nil, wait, nil) + local TaskHold = group:TaskHold() + + -- Task combo to make group hold at final waypoint. + local TaskComboFin = {} + TaskComboFin[#TaskComboFin+1] = group:TaskFunction("SUPPRESSION._Passing_Waypoint", self, #wp, true) + TaskComboFin[#TaskComboFin+1] = group:TaskControlled(TaskHold, ConditionWait) + + -- Add final task. + tasks[#tasks+1]=group:TaskCombo(TaskComboFin) + + -- Original waypoints of the group. + local Waypoints = group:GetTemplateRoutePoints() + + -- New points are added to the default route. + for i,p in ipairs(wp) do + table.insert(Waypoints, i, wp[i]) + end + + -- Set task for all waypoints. + for i,wp in ipairs(Waypoints) do + group:SetTaskWaypoint(Waypoints[i], tasks[i]) + end + + -- Submit task and route group along waypoints. + group:Route(Waypoints) + +end + +--- Function called when group is passing a waypoint. At the last waypoint we set the group back to CombatReady. +--@param Wrapper.Group#GROUP group Group which is passing a waypoint. +--@param #SUPPRESSION Fsm The suppression object. +--@param #number i Waypoint number that has been reached. +--@param #boolean final True if it is the final waypoint. Start Fightback. +function SUPPRESSION._Passing_Waypoint(group, Fsm, i, final) + + -- Debug message. + local text=string.format("Group %s passing waypoint %d (final=%s)", group:GetName(), i, tostring(final)) + MESSAGE:New(text,10):ToAllIf(Fsm.Debug) + if Fsm.Debug then + env.info(SUPPRESSION.id..text) + end + + if final then + if Fsm:is("Retreating") then + -- Retreated-->Retreated. + Fsm:Retreated() + else + -- FightBack-->Combatready: Change alarm state back to default. + Fsm:FightBack() + end + end +end + + +--- Search a place to hide. This is any scenery object in the vicinity. +--@param #SUPPRESSION self +--@return Core.Point#COORDINATE Coordinate of the hideout place. +--@return nil If no scenery object is within search radius. +function SUPPRESSION:_SearchHideout() + -- We search objects in a zone with radius ~300 m around the group. + local Zone = ZONE_GROUP:New("Zone_Hiding", self.Controllable, self.TakecoverRange) + local gpos = self.Controllable:GetCoordinate() + + -- Scan for Scenery objects to run/drive to. + Zone:Scan(Object.Category.SCENERY) + + -- Array with all possible hideouts, i.e. scenery objects in the vicinity of the group. + local hideouts={} + + for SceneryTypeName, SceneryData in pairs(Zone:GetScannedScenery()) do + for SceneryName, SceneryObject in pairs(SceneryData) do + + local SceneryObject = SceneryObject -- Wrapper.Scenery#SCENERY + + -- Position of the scenery object. + local spos=SceneryObject:GetCoordinate() + + -- Distance from group to hideout. + local distance= spos:Get2DDistance(gpos) + + if self.Debug then + -- Place markers on every possible scenery object. + local MarkerID=SceneryObject:GetCoordinate():MarkToAll(string.format("%s scenery object %s", self.Controllable:GetName(),SceneryObject:GetTypeName())) + local text=string.format("%s scenery: %s, Coord %s", self.Controllable:GetName(), SceneryObject:GetTypeName(), SceneryObject:GetCoordinate():ToStringLLDMS()) + self:T2(SUPPRESSION.id..text) + end + + -- Add to table. + table.insert(hideouts, {object=SceneryObject, distance=distance}) + end + end + + -- Get random hideout place. + local Hideout=nil + if #hideouts>0 then + + -- Debug info. + self:T(SUPPRESSION.id.."Number of hideouts "..#hideouts) + + -- Sort results table wrt number of hits. + local _sort = function(a,b) return a.distance < b.distance end + table.sort(hideouts,_sort) + + -- Pick a random location. + --Hideout=hideouts[math.random(#hideouts)].object + + -- Pick closest location. + Hideout=hideouts[1].object:GetCoordinate() + + else + self:E(SUPPRESSION.id.."No hideouts found!") + end + + return Hideout + +end + +--- Get (relative) life in percent of a group. Function returns the value of the units with the smallest and largest life. Also the average value of all groups is returned. +-- @param #SUPPRESSION self +-- @return #number Smallest life value of all units. +-- @return #number Largest life value of all units. +-- @return #number Average life value of all alife groups +-- @return #number Average life value of all groups including already dead ones. +-- @return #number Relative group strength. +function SUPPRESSION:_GetLife() + + local group=self.Controllable --Wrapper.Group#GROUP + + if group and group:IsAlive() then + + local units=group:GetUnits() + + local life_min=nil + local life_max=nil + local life_ave=0 + local life_ave0=0 + local n=0 + + local groupstrength=#units/self.IniGroupStrength*100 + + self.T2(SUPPRESSION.id..string.format("Group %s _GetLife nunits = %d", self.Controllable:GetName(), #units)) + + for _,unit in pairs(units) do + + local unit=unit -- Wrapper.Unit#UNIT + if unit and unit:IsAlive() then + n=n+1 + local life=unit:GetLife()/(unit:GetLife0()+1)*100 + if life_min==nil or life < life_min then + life_min=life + end + if life_max== nil or life > life_max then + life_max=life + end + life_ave=life_ave+life + if self.Debug then + local text=string.format("n=%02d: Life = %3.1f, Life0 = %3.1f, min=%3.1f, max=%3.1f, ave=%3.1f, group=%3.1f", n, unit:GetLife(), unit:GetLife0(), life_min, life_max, life_ave/n,groupstrength) + self:T2(SUPPRESSION.id..text) + end + end + + end + + -- If the counter did not increase (can happen!) return 0 + if n==0 then + return 0,0,0,0,0 + end + + -- Average life relative to initial group strength including the dead ones. + life_ave0=life_ave/self.IniGroupStrength + + -- Average life of all alive units. + life_ave=life_ave/n + + return life_min, life_max, life_ave, life_ave0, groupstrength + else + return 0, 0, 0, 0, 0 + end +end + + +--- Heading from point a to point b in degrees. +--@param #SUPPRESSION self +--@param Core.Point#COORDINATE a Coordinate. +--@param Core.Point#COORDINATE b Coordinate. +--@return #number angle Angle from a to b in degrees. +function SUPPRESSION:_Heading(a, b, distance) + local dx = b.x-a.x + local dy = b.z-a.z + local angle = math.deg(math.atan2(dy,dx)) + if angle < 0 then + angle = 360 + angle + end + return angle +end + +--- Generate Gaussian pseudo-random numbers. +-- @param #SUPPRESSION self +-- @param #number x0 Expectation value of distribution. +-- @param #number sigma (Optional) Standard deviation. Default 10. +-- @param #number xmin (Optional) Lower cut-off value. +-- @param #number xmax (Optional) Upper cut-off value. +-- @return #number Gaussian random number. +function SUPPRESSION:_Random_Gaussian(x0, sigma, xmin, xmax) + + -- Standard deviation. Default 5 if not given. + sigma=sigma or 5 + + local r + local gotit=false + local i=0 + while not gotit do + + -- Uniform numbers in [0,1). We need two. + local x1=math.random() + local x2=math.random() + + -- Transform to Gaussian exp(-(x-x0)²/(2*sigma²). + r = math.sqrt(-2*sigma*sigma * math.log(x1)) * math.cos(2*math.pi * x2) + x0 + + i=i+1 + if (r>=xmin and r<=xmax) or i>100 then + gotit=true + end + end + + return r + +end + +--- Sets the ROE for the group and updates the current ROE variable. +-- @param #SUPPRESSION self +-- @param #string roe ROE the group will get. Possible "Free", "Hold", "Return". Default is self.DefaultROE. +function SUPPRESSION:_SetROE(roe) + local group=self.Controllable --Wrapper.Controllable#CONTROLLABLE + + -- If no argument is given, we take the default ROE. + roe=roe or self.DefaultROE + + -- Update the current ROE. + self.CurrentROE=roe + + -- Set the ROE. + if roe==SUPPRESSION.ROE.Free then + group:OptionROEOpenFire() + elseif roe==SUPPRESSION.ROE.Hold then + group:OptionROEHoldFire() + elseif roe==SUPPRESSION.ROE.Return then + group:OptionROEReturnFire() + else + self:E(SUPPRESSION.id.."Unknown ROE requested: "..tostring(roe)) + group:OptionROEOpenFire() + self.CurrentROE=SUPPRESSION.ROE.Free + end + + local text=string.format("Group %s now has ROE %s.", self.Controllable:GetName(), self.CurrentROE) + self:T(SUPPRESSION.id..text) +end + +--- Sets the alarm state of the group and updates the current alarm state variable. +-- @param #SUPPRESSION self +-- @param #string state Alarm state the group will get. Possible "Auto", "Green", "Red". Default is self.DefaultAlarmState. +function SUPPRESSION:_SetAlarmState(state) + local group=self.Controllable --Wrapper.Controllable#CONTROLLABLE + + -- Input or back to default alarm state. + state=state or self.DefaultAlarmState + + -- Update the current alam state of the group. + self.CurrentAlarmState=state + + -- Set the alarm state. + if state==SUPPRESSION.AlarmState.Auto then + group:OptionAlarmStateAuto() + elseif state==SUPPRESSION.AlarmState.Green then + group:OptionAlarmStateGreen() + elseif state==SUPPRESSION.AlarmState.Red then + group:OptionAlarmStateRed() + else + self:E(SUPPRESSION.id.."Unknown alarm state requested: "..tostring(state)) + group:OptionAlarmStateAuto() + self.CurrentAlarmState=SUPPRESSION.AlarmState.Auto + end + + local text=string.format("Group %s now has Alarm State %s.", self.Controllable:GetName(), self.CurrentAlarmState) + self:T(SUPPRESSION.id..text) +end + +--- Print event-from-to string to DCS log file. +-- @param #SUPPRESSION self +-- @param #string BA Before/after info. +-- @param #string Event Event. +-- @param #string From From state. +-- @param #string To To state. +function SUPPRESSION:_EventFromTo(BA, Event, From, To) + local text=string.format("\n%s: %s EVENT %s: %s --> %s", BA, self.Controllable:GetName(), Event, From, To) + self:T(SUPPRESSION.id..text) +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + From 0d995d183284e4944f09c54fb44b3be6b739e584 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Wed, 4 Apr 2018 14:38:49 +0200 Subject: [PATCH 027/420] Progress --- .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 305 ++++++++++++++---- 1 file changed, 244 insertions(+), 61 deletions(-) diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index df9c36dec..ca96b2d87 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -178,6 +178,9 @@ do -- TASK_CARGO_DISPATCHER Tasks = {}, CSAR = {}, CSARSpawned = 0, + + Transport = {}, + TransportCount = 0, } @@ -214,62 +217,163 @@ do -- TASK_CARGO_DISPATCHER return self end - + + + --- Handle the event when a pilot ejects. -- @param #TASK_CARGO_DISPATCHER self -- @param Core.Event#EVENTDATA EventData function TASK_CARGO_DISPATCHER:OnEventEjection( EventData ) + self:F( { EventData = EventData } ) + + if self.CSARTasks == true then - self:E( { EventData = EventData } ) - - self.CSARSpawned = self.CSARSpawned + 1 - - local PlaneUnit = EventData.IniUnit - local CSARName = EventData.IniUnitName - - local CargoPointVec2 = EventData.IniUnit:GetPointVec2() - local CargoCoalition = EventData.IniUnit:GetCoalition() - local CargoCountry = EventData.IniUnit:GetCountry() - - -- Only add a CSAR task if the coalition of the mission is equal to the coalition of the ejected unit. - - if CargoCoalition == self.Mission:GetCommandCenter():GetCoalition() then - - -- Create the CSAR Pilot SPAWN object. - -- Let us create the Template for the replacement Pilot :-) - local Template = { - ["visible"] = false, - ["hidden"] = false, - ["task"] = "Ground Nothing", - ["name"] = string.format( "CSAR Pilot#%03d", self.CSARSpawned ), - ["x"] = CargoPointVec2:GetLat(), - ["y"] = CargoPointVec2:GetLon(), - ["units"] = - { - [1] = - { - ["type"] = ( CargoCoalition == coalition.side.BLUE ) and "Soldier M4" or "Infantry AK", - ["name"] = string.format( "CSAR Pilot#%03d-01", self.CSARSpawned ), - ["skill"] = "Excellent", - ["playerCanDrive"] = false, - ["x"] = CargoPointVec2:GetLat(), - ["y"] = CargoPointVec2:GetLon(), - ["heading"] = EventData.IniUnit:GetHeading(), - }, -- end of [1] - }, -- end of ["units"] - } - - local CargoGroup = GROUP:NewTemplate( Template, CargoCoalition, Group.Category.GROUND, CargoCountry ) - - self.CSAR[#self.CSAR+1] = {} - self.CSAR[#self.CSAR].PilotGroup = CargoGroup - self.CSAR[#self.CSAR].Task = nil - + local CSARCoordinate = EventData.IniUnit:GetCoordinate() + local CSARCoalition = EventData.IniUnit:GetCoalition() + local CSARCountry = EventData.IniUnit:GetCountry() + local CSARHeading = EventData.IniUnit:GetHeading() + + -- Only add a CSAR task if the coalition of the mission is equal to the coalition of the ejected unit. + if CSARCoalition == self.Mission:GetCommandCenter():GetCoalition() then + local CSARTaskName = self:AddCSARTask( self.CSARTaskName, CSARCoordinate, CSARHeading, CSARCountry, self.CSARBriefing ) + self:SetCSARDeployZones( CSARTaskName, self.CSARDeployZones ) + end end return self end + + + --- Define one default deploy zone for all the cargo tasks. + -- @param #TASK_CARGO_DISPATCHER self + -- @param DefaultDeployZone A default deploy zone. + -- @return #TASK_CARGO_DISPATCHER + function TASK_CARGO_DISPATCHER:SetDefaultDeployZone( DefaultDeployZone ) + + self.DefaultDeployZones = { DefaultDeployZone } + + return self + end + + + --- Define the deploy zones for all the cargo tasks. + -- @param #TASK_CARGO_DISPATCHER self + -- @param DefaultDeployZones A list of the deploy zones. + -- @return #TASK_CARGO_DISPATCHER + -- + function TASK_CARGO_DISPATCHER:SetDefaultDeployZones( DefaultDeployZones ) + + self.DefaultDeployZones = DefaultDeployZones + + return self + end + + + --- Start the generation of CSAR tasks to retrieve a downed pilots. + -- You need to specify a task briefing, a task name, default deployment zone(s). + -- This method can only be used once! + -- @param #TASK_CARGO_DISPATCHER self + -- @param #string CSARTaskName The CSAR task name. + -- @param #string CSARDeployZones The zones to where the CSAR deployment should be directed. + -- @param #string CSARBriefing The briefing of the CSAR tasks. + -- @return #TASK_CARGO_DISPATCHER + function TASK_CARGO_DISPATCHER:StartCSARTasks( CSARTaskName, CSARDeployZones, CSARBriefing) + + if not self.CSARTasks then + self.CSARTasks = true + self.CSARTaskName = CSARTaskName + self.CSARDeployZones = CSARDeployZones + self.CSARBriefing = CSARBriefing + else + error( "TASK_CARGO_DISPATCHER: The generation of CSAR tasks has already started." ) + end + + return self + end + + + --- Stop the generation of CSAR tasks to retrieve a downed pilots. + -- @param #TASK_CARGO_DISPATCHER self + -- @return #TASK_CARGO_DISPATCHER + function TASK_CARGO_DISPATCHER:StopCSARTasks() + + if self.CSARTasks then + self.CSARTasks = nil + self.CSARTaskName = nil + self.CSARDeployZones = nil + self.CSARBriefing = nil + else + error( "TASK_CARGO_DISPATCHER: The generation of CSAR tasks was not yet started." ) + end + + return self + end + + + --- Add a CSAR task to retrieve a downed pilot. + -- You need to specify a coordinate from where the pilot will be spawned to be rescued. + -- @param #TASK_CARGO_DISPATCHER self + -- @param #string CSARTaskPrefix (optional) The prefix of the CSAR task. + -- @param Core.Point#COORDINATE CSARCoordinate The coordinate where a downed pilot will be spawned. + -- @param #number CSARHeading The heading of the pilot in degrees. + -- @param DCSCountry#Country CSARCountry The country ID of the pilot that will be spawned. + -- @param #string CSARBriefing The briefing of the CSAR task. + -- @return #string The CSAR Task Name as a string. The Task Name is the main key and is shown in the task list of the Mission Tasking menu. + -- @usage + -- + -- -- Add a CSAR task to rescue a downed pilot from within a coordinate. + -- local Coordinate = PlaneUnit:GetPointVec2() + -- TaskA2ADispatcher:AddCSARTask( Coordinate ) + -- + -- -- Add a CSAR task to rescue a downed pilot from within a coordinate of country RUSSIA, which is pointing to the west (270°). + -- local Coordinate = PlaneUnit:GetPointVec2() + -- TaskA2ADispatcher:AddCSARTask( Coordinate, 270, Country.RUSSIA ) + -- + function TASK_CARGO_DISPATCHER:AddCSARTask( CSARTaskPrefix, CSARCoordinate, CSARHeading, CSARCountry, CSARBriefing ) + + local CSARCoalition = self.Mission:GetCommandCenter():GetCoalition() + + CSARHeading = CSARHeading or 0 + CSARCountry = CSARCountry or self.Mission:GetCommandCenter():GetCountry() + + self.CSARSpawned = self.CSARSpawned + 1 + + local CSARTaskName = string.format( ( CSARTaskPrefix or "CSAR" ) .. ".%03d", self.CSARSpawned ) + + -- Create the CSAR Pilot SPAWN object. + -- Let us create the Template for the replacement Pilot :-) + local Template = { + ["visible"] = false, + ["hidden"] = false, + ["task"] = "Ground Nothing", + ["name"] = string.format( "CSAR Pilot#%03d", self.CSARSpawned ), + ["x"] = CSARCoordinate.x, + ["y"] = CSARCoordinate.z, + ["units"] = + { + [1] = + { + ["type"] = ( CSARCoalition == coalition.side.BLUE ) and "Soldier M4" or "Infantry AK", + ["name"] = string.format( "CSAR Pilot#%03d-01", self.CSARSpawned ), + ["skill"] = "Excellent", + ["playerCanDrive"] = false, + ["x"] = CSARCoordinate.x, + ["y"] = CSARCoordinate.z, + ["heading"] = CSARHeading, + }, -- end of [1] + }, -- end of ["units"] + } + + local CSARGroup = GROUP:NewTemplate( Template, CSARCoalition, Group.Category.GROUND, CSARCountry ) + + self.CSAR[CSARTaskName] = {} + self.CSAR[CSARTaskName].PilotGroup = CSARGroup + self.CSAR[CSARTaskName].Briefing = CSARBriefing + self.CSAR[CSARTaskName].Task = nil + + return CSARTaskName + end --- Define the radius to when a CSAR task will be generated for any downed pilot within range of the nearest CSAR airbase. @@ -294,11 +398,14 @@ do -- TASK_CARGO_DISPATCHER --- Define one deploy zone for the CSAR tasks. -- @param #TASK_CARGO_DISPATCHER self - -- @param DeployZone A deploy zone. + -- @param #string CSARTaskName (optional) The name of the CSAR task. + -- @param CSARDeployZone A CSAR deploy zone. -- @return #TASK_CARGO_DISPATCHER - function TASK_CARGO_DISPATCHER:SetCSARDeployZone( CSARDeployZone ) + function TASK_CARGO_DISPATCHER:SetCSARDeployZone( CSARTaskName, CSARDeployZone ) - self.CSARDeployZones = { CSARDeployZone } + if CSARTaskName then + self.CSAR[CSARTaskName].DeployZones = { CSARDeployZone } + end return self end @@ -306,16 +413,73 @@ do -- TASK_CARGO_DISPATCHER --- Define the deploy zones for the CSAR tasks. -- @param #TASK_CARGO_DISPATCHER self - -- @param CSARDeployZones A list of the deploy zones. + -- @param #string CSARTaskName (optional) The name of the CSAR task. + -- @param CSARDeployZones A list of the CSAR deploy zones. -- @return #TASK_CARGO_DISPATCHER - function TASK_CARGO_DISPATCHER:SetCSARDeployZones( CSARDeployZones ) + -- + function TASK_CARGO_DISPATCHER:SetCSARDeployZones( CSARTaskName, CSARDeployZones ) - self.CSARDeployZones = CSARDeployZones + if CSARTaskName and self.CSAR[CSARTaskName] then + self.CSAR[CSARTaskName].DeployZones = CSARDeployZones + end + + return self + end + + + --- Add a Transport task to transport cargo from fixed locations to a deployment zone. + -- @param #TASK_CARGO_DISPATCHER self + -- @param #string TransportTaskName (optional) The name of the transport task. + -- @param Core.SetCargo#SET_CARGO SetCargo The SetCargo to be transported. + -- @param #string Briefing The briefing of the task transport to be shown to the player. + -- @return #TASK_CARGO_DISPATCHER + -- @usage + -- + -- -- Add a Transport task to transport cargo of different types to a Transport Deployment Zone. + function TASK_CARGO_DISPATCHER:AddTransportTask( TransportTaskName, SetCargo, Briefing ) + + self.TransportCount = self.TransportCount + 1 + local TaskName = string.format( ( TransportTaskName or "Transport" ) .. ".%03d", self.TransportCount ) + + self.Transport[TaskName] = {} + self.Transport[TaskName].SetCargo = SetCargo + self.Transport[TaskName].Briefing = Briefing + self.Transport[TaskName].Task = nil + + return self + end + + + --- Define one deploy zone for the Transport tasks. + -- @param #TASK_CARGO_DISPATCHER self + -- @param #string TransportTaskName (optional) The name of the Transport task. + -- @param TransportDeployZone A Transport deploy zone. + -- @return #TASK_CARGO_DISPATCHER + function TASK_CARGO_DISPATCHER:SetTransportDeployZone( TransportTaskName, TransportDeployZone ) + + if TransportTaskName then + self.Transport[TransportTaskName].DeployZones = { TransportDeployZone } + end return self end + --- Define the deploy zones for the Transport tasks. + -- @param #TASK_CARGO_DISPATCHER self + -- @param #string TransportTaskName (optional) The name of the Transport task. + -- @param TransportDeployZones A list of the Transport deploy zones. + -- @return #TASK_CARGO_DISPATCHER + -- + function TASK_CARGO_DISPATCHER:SetTransportDeployZones( TransportTaskName, TransportDeployZones ) + + if TransportTaskName then + self.Transport[TransportTaskName].DeployZones = TransportDeployZones + end + + return self + end + --- Evaluates of a CSAR task needs to be started. -- @param #TASK_CARGO_DISPATCHER self -- @return Set#SET_CARGO The SetCargo to be rescued. @@ -368,17 +532,36 @@ do -- TASK_CARGO_DISPATCHER end -- Now that all obsolete tasks are removed, loop through the CSAR pilots. - for CSARID, CSARData in pairs( self.CSAR ) do + for CSARName, CSAR in pairs( self.CSAR ) do - if CSARData.Task then - else + if not CSAR.Task then -- New CSAR Task - local SetCargo = self:EvaluateCSAR( CSARData.PilotGroup ) - local CSARTask = TASK_CARGO_CSAR:New( Mission, self.SetGroup, string.format( "CSAR.%03d", CSARID ), SetCargo ) - CSARTask:SetDeployZones( self.CSARDeployZones or {} ) - Mission:AddTask( CSARTask ) - TaskReport:Add( CSARTask:GetName() ) - CSARData.Task = CSARTask + local SetCargo = self:EvaluateCSAR( CSAR.PilotGroup ) + CSAR.Task = TASK_CARGO_CSAR:New( Mission, self.SetGroup, CSARName, SetCargo, CSAR.Briefing ) + Mission:AddTask( CSAR.Task ) + TaskReport:Add( CSARName ) + if CSAR.DeployZones then + CSAR.Task:SetDeployZones( CSAR.DeployZones or {} ) + else + CSAR.Task:SetDeployZones( self.DefaultDeployZones or {} ) + end + end + end + + + -- Now that all obsolete tasks are removed, loop through the Transport tasks. + for TransportName, Transport in pairs( self.Transport ) do + + if not Transport.Task then + -- New Transport Task + Transport.Task = TASK_CARGO_TRANSPORT:New( Mission, self.SetGroup, TransportName, Transport.SetCargo, Transport.Briefing ) + Mission:AddTask( Transport.Task ) + TaskReport:Add( TransportName ) + if Transport.DeployZones then + Transport.Task:SetDeployZones( Transport.DeployZones or {} ) + else + Transport.Task:SetDeployZones( self.DefaultDeployZones or {} ) + end end end From c60dda2545b8b2c174098ecce783b5317d5fb2b2 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Thu, 5 Apr 2018 15:06:36 +0200 Subject: [PATCH 028/420] Cargo Crate transportation working :-) --- Moose Development/Moose/Cargo/Cargo.lua | 42 ++++++- Moose Development/Moose/Cargo/CargoCrate.lua | 111 +++++++++++++++++- Moose Development/Moose/Core/Database.lua | 46 +++----- Moose Development/Moose/Core/SpawnStatic.lua | 2 +- .../Moose/Tasking/Task_CARGO.lua | 97 ++++++++++++--- .../Moose/Wrapper/Controllable.lua | 10 -- .../Moose/Wrapper/Positionable.lua | 14 ++- Moose Development/Moose/Wrapper/Static.lua | 45 ++++++- 8 files changed, 299 insertions(+), 68 deletions(-) diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 07ee37a6d..2528b1b32 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -259,7 +259,7 @@ do -- CARGO self.Type = Type self.Name = Name - self.Weight = Weight + self.Weight = Weight or 0 self.CargoObject = nil self.CargoCarrier = nil -- Wrapper.Client#CLIENT self.Representable = false @@ -288,6 +288,34 @@ do -- CARGO return CargoFound end + --- Check if the cargo can be Boarded. + -- @param #CARGO self + function CARGO:CanBoard() + return true + end + + --- Check if the cargo can be Unboarded. + -- @param #CARGO self + function CARGO:CanUnboard() + return true + end + + --- Check if the cargo can be Loaded. + -- @param #CARGO self + function CARGO:CanLoad() + return true + end + + --- Check if the cargo can be Unloaded. + -- @param #CARGO self + function CARGO:CanUnload() + return true + end + + + + + --- Destroy the cargo. -- @param #CARGO self function CARGO:Destroy() @@ -315,6 +343,13 @@ do -- CARGO end end + --- Get the amount of Cargo. + -- @param #CARGO self + -- @return #number The amount of Cargo. + function CARGO:GetCount() + return 1 + end + --- Get the type of the Cargo. -- @param #CARGO self -- @return #string The type of the Cargo. @@ -569,8 +604,11 @@ do -- CARGO_REPRESENTABLE -- @param #number NearRadius (optional) -- @return #CARGO_REPRESENTABLE function CARGO_REPRESENTABLE:New( CargoObject, Type, Name, Weight, ReportRadius, NearRadius ) - local self = BASE:Inherit( self, CARGO:New( Type, Name, Weight, ReportRadius, NearRadius ) ) -- #CARGO_REPRESENTABLE + local self = BASE:Inherit( self, CARGO:New( Type, Name, Weight ) ) -- #CARGO_REPRESENTABLE self:F( { Type, Name, Weight, ReportRadius, NearRadius } ) + + self.ReportRadius = ReportRadius or 500 + self.NearRadius = NearRadius or 25 return self end diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua index 3f943d1ed..28a231d91 100644 --- a/Moose Development/Moose/Cargo/CargoCrate.lua +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -42,19 +42,25 @@ do -- CARGO_CRATE -- @param Wrapper.Static#STATIC CargoStatic -- @param #string Type -- @param #string Name - -- @param #number Weight -- @param #number ReportRadius (optional) -- @param #number NearRadius (optional) -- @return #CARGO_CRATE - function CARGO_CRATE:New( CargoStatic, Type, Name, NearRadius ) - local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoStatic, Type, Name, nil, NearRadius ) ) -- #CARGO_CRATE + function CARGO_CRATE:New( CargoStatic, Type, Name, ReportRadius, NearRadius ) + local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoStatic, Type, Name, nil, ReportRadius, NearRadius ) ) -- #CARGO_CRATE self:F( { Type, Name, NearRadius } ) self.CargoObject = CargoStatic self:T( self.ClassName ) - self:SetEventPriority( 5 ) + -- Cargo objects are added to the _DATABASE and SET_CARGO objects. + _EVENTDISPATCHER:CreateEventNewCargo( self ) + + self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead ) + self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead ) + self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead ) + + self:SetEventPriority( 4 ) return self end @@ -116,5 +122,102 @@ do -- CARGO_CRATE end end + --- Check if the cargo can be Boarded. + -- @param #CARGO self + function CARGO:CanBoard() + return false + end + + --- Check if the cargo can be Unboarded. + -- @param #CARGO self + function CARGO_CRATE:CanUnboard() + return false + end + + --- Get the current Coordinate of the CargoGroup. + -- @param #CARGO_CRATE self + -- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup. + -- @return #nil There is no valid Cargo in the CargoGroup. + function CARGO_CRATE:GetCoordinate() + self:F() + + return self.CargoObject:GetCoordinate() + end + + --- Check if the CargoGroup is alive. + -- @param #CARGO_CRATE self + -- @return #boolean true if the CargoGroup is alive. + -- @return #boolean false if the CargoGroup is dead. + function CARGO_CRATE:IsAlive() + + local Alive = true + + -- When the Cargo is Loaded, the Cargo is in the CargoCarrier, so we check if the CargoCarrier is alive. + -- When the Cargo is not Loaded, the Cargo is the CargoObject, so we check if the CargoObject is alive. + if self:IsLoaded() then + Alive = Alive == true and self.CargoCarrier:IsAlive() + else + Alive = Alive == true and self.CargoObject:IsAlive() + end + + return Alive + + end + + + --- Route Cargo to Coordinate and randomize locations. + -- @param #CARGO_CRATE self + -- @param Core.Point#COORDINATE Coordinate + function CARGO_CRATE:RouteTo( Coordinate ) + self:F( {Coordinate = Coordinate } ) + + end + + --- Check if Cargo is near to the Carrier. + -- The Cargo is near to the Carrier within NearRadius. + -- @param #CARGO_CRATE self + -- @param Wrapper.Group#GROUP CargoCarrier + -- @param #number NearRadius + -- @return #boolean The Cargo is near to the Carrier. + -- @return #nil The Cargo is not near to the Carrier. + function CARGO_CRATE:IsNear( CargoCarrier, NearRadius ) + self:F( {NearRadius = NearRadius } ) + + return self:IsNear( CargoCarrier:GetCoordinate(), NearRadius ) + end + + --- Check if CargoGroup is in the ReportRadius for the Cargo to be Loaded. + -- @param #CARGO_CRATE self + -- @param Core.Point#Coordinate Coordinate + -- @return #boolean true if the CargoGroup is within the reporting radius. + function CARGO_CRATE:IsInRadius( Coordinate ) + self:F( { Coordinate } ) + + local Distance = 0 + if self:IsLoaded() then + Distance = Coordinate:DistanceFromPointVec2( self.CargoCarrier:GetPointVec2() ) + else + Distance = Coordinate:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) + end + self:T( Distance ) + + if Distance <= self.ReportRadius then + return true + else + return false + end + end + + --- Respawn the CargoGroup. + -- @param #CARGO_CRATE self + function CARGO_CRATE:Respawn() + + self:F( { "Respawning" } ) + + self:SetDeployed( false ) + self:SetStartState( "UnLoaded" ) + + end + end diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index d90e01284..2235407cb 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -450,8 +450,6 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category local GroupTemplateName = GroupName or env.getValueDictByKey( GroupTemplate.name ) - local TraceTable = {} - if not self.Templates.Groups[GroupTemplateName] then self.Templates.Groups[GroupTemplateName] = {} self.Templates.Groups[GroupTemplateName].Status = nil @@ -475,18 +473,7 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category self.Templates.Groups[GroupTemplateName].CoalitionID = CoalitionSide self.Templates.Groups[GroupTemplateName].CountryID = CountryID - - TraceTable[#TraceTable+1] = "Group" - TraceTable[#TraceTable+1] = self.Templates.Groups[GroupTemplateName].GroupName - - TraceTable[#TraceTable+1] = "Coalition" - TraceTable[#TraceTable+1] = self.Templates.Groups[GroupTemplateName].CoalitionID - TraceTable[#TraceTable+1] = "Category" - TraceTable[#TraceTable+1] = self.Templates.Groups[GroupTemplateName].CategoryID - TraceTable[#TraceTable+1] = "Country" - TraceTable[#TraceTable+1] = self.Templates.Groups[GroupTemplateName].CountryID - - TraceTable[#TraceTable+1] = "Units" + local UnitNames = {} for unit_num, UnitTemplate in pairs( GroupTemplate.units ) do @@ -510,10 +497,16 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category self.Templates.ClientsByID[UnitTemplate.unitId] = UnitTemplate end - TraceTable[#TraceTable+1] = self.Templates.Units[UnitTemplate.name].UnitName + UnitNames[#UnitNames+1] = self.Templates.Units[UnitTemplate.name].UnitName end - self:E( TraceTable ) + self:I( { Group = self.Templates.Groups[GroupTemplateName].GroupName, + Coalition = self.Templates.Groups[GroupTemplateName].CoalitionID, + Category = self.Templates.Groups[GroupTemplateName].CategoryID, + Country = self.Templates.Groups[GroupTemplateName].CountryID, + Units = UnitNames + } + ) end function DATABASE:GetGroupTemplate( GroupName ) @@ -530,8 +523,6 @@ end -- @return #DATABASE self function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID ) - local TraceTable = {} - local StaticTemplateName = env.getValueDictByKey(StaticTemplate.name) self.Templates.Statics[StaticTemplateName] = self.Templates.Statics[StaticTemplateName] or {} @@ -547,18 +538,15 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category self.Templates.Statics[StaticTemplateName].CoalitionID = CoalitionID self.Templates.Statics[StaticTemplateName].CountryID = CountryID + self:I( { Static = self.Templates.Statics[StaticTemplateName].StaticName, + Coalition = self.Templates.Statics[StaticTemplateName].CoalitionID, + Category = self.Templates.Statics[StaticTemplateName].CategoryID, + Country = self.Templates.Statics[StaticTemplateName].CountryID + } + ) + + self:AddStatic( StaticTemplateName ) - TraceTable[#TraceTable+1] = "Static" - TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].StaticName - - TraceTable[#TraceTable+1] = "Coalition" - TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].CoalitionID - TraceTable[#TraceTable+1] = "Category" - TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].CategoryID - TraceTable[#TraceTable+1] = "Country" - TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].CountryID - - self:E( TraceTable ) end diff --git a/Moose Development/Moose/Core/SpawnStatic.lua b/Moose Development/Moose/Core/SpawnStatic.lua index 92a816257..6772956e0 100644 --- a/Moose Development/Moose/Core/SpawnStatic.lua +++ b/Moose Development/Moose/Core/SpawnStatic.lua @@ -85,7 +85,7 @@ function SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, CountryID ) --R2.1 local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC self:F( { SpawnTemplatePrefix } ) - local TemplateStatic = StaticObject.getByName( SpawnTemplatePrefix ) + local TemplateStatic = STATIC:FindByName( SpawnTemplatePrefix ) if TemplateStatic then self.SpawnTemplatePrefix = SpawnTemplatePrefix self.CountryID = CountryID diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index d59473047..78cedeb0c 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -13,6 +13,7 @@ -- The following classes are important to consider: -- -- * @{#TASK_CARGO_TRANSPORT}: Defines a task for a human player to transport a set of cargo between various zones. +-- * @{#TASK_CARGO_CSAR}: Defines a task for a human player to Search and Rescue wounded pilots. -- -- === -- @@ -173,7 +174,7 @@ do -- TASK_CARGO Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "SelectAction", Rejected = "Reject" } ) - Fsm:AddTransition( { "Planned", "Assigned", "WaitingForCommand", "ArrivedAtPickup", "ArrivedAtDeploy", "Boarded", "UnBoarded", "Landed", "Boarding" }, "SelectAction", "*" ) + Fsm:AddTransition( { "Planned", "Assigned", "WaitingForCommand", "ArrivedAtPickup", "ArrivedAtDeploy", "Boarded", "UnBoarded", "Loaded", "UnLoaded", "Landed", "Boarding" }, "SelectAction", "*" ) Fsm:AddTransition( "*", "RouteToPickup", "RoutingToPickup" ) Fsm:AddProcess ( "RoutingToPickup", "RouteToPickupPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtPickup", Cancelled = "CancelRouteToPickup" } ) @@ -191,10 +192,14 @@ do -- TASK_CARGO Fsm:AddTransition( "*", "PrepareBoarding", "AwaitBoarding" ) Fsm:AddTransition( "AwaitBoarding", "Board", "Boarding" ) Fsm:AddTransition( "Boarding", "Boarded", "Boarded" ) + + Fsm:AddTransition( "*", "Load", "Loaded" ) Fsm:AddTransition( "*", "PrepareUnBoarding", "AwaitUnBoarding" ) Fsm:AddTransition( "AwaitUnBoarding", "UnBoard", "UnBoarding" ) Fsm:AddTransition( "UnBoarding", "UnBoarded", "UnBoarded" ) + + Fsm:AddTransition( "*", "Unload", "Unloaded" ) Fsm:AddTransition( "*", "Planned", "Planned" ) @@ -254,7 +259,13 @@ do -- TASK_CARGO end if NotInDeployZones then if not TaskUnit:InAir() then - MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Board cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuBoardCargo, self, Cargo ):SetTime(MenuTime) + if Cargo:CanBoard() then + MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Board cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuBoardCargo, self, Cargo ):SetTime(MenuTime) + else + if Cargo:CanLoad() then + MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Load cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuLoadCargo, self, Cargo ):SetTime(MenuTime) + end + end TaskUnit.Menu:SetTime( MenuTime ) end end @@ -267,7 +278,13 @@ do -- TASK_CARGO if Cargo:IsLoaded() then if not TaskUnit:InAir() then - MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Unboard cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuUnBoardCargo, self, Cargo ):SetTime(MenuTime) + if Cargo:CanUnboard() then + MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Unboard cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuUnboardCargo, self, Cargo ):SetTime(MenuTime) + else + if Cargo:CanUnload() then + MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Unload cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuUnloadCargo, self, Cargo ):SetTime(MenuTime) + end + end TaskUnit.Menu:SetTime( MenuTime ) end -- Deployzones are optional zones that can be selected to request routing information. @@ -305,10 +322,18 @@ do -- TASK_CARGO self:__PrepareBoarding( 1.0, Cargo ) end - function Fsm:MenuUnBoardCargo( Cargo, DeployZone ) + function Fsm:MenuLoadCargo( Cargo ) + self:__Load( 1.0, Cargo ) + end + + function Fsm:MenuUnboardCargo( Cargo, DeployZone ) self:__PrepareUnBoarding( 1.0, Cargo, DeployZone ) end + function Fsm:MenuUnloadCargo( Cargo, DeployZone ) + self:__Unload( 1.0, Cargo, DeployZone ) + end + function Fsm:MenuRouteToPickup( Cargo ) self:__RouteToPickup( 1.0, Cargo ) end @@ -506,9 +531,7 @@ do -- TASK_CARGO self.Cargo:MessageToGroup( "Boarded ...", TaskUnit:GetGroup() ) - TaskUnit:AddCargo( self.Cargo ) - - self:__SelectAction( 1 ) + self:Load( self.Cargo ) -- TODO:I need to find a more decent solution for this. Task:E( { CargoPickedUp = Task.CargoPickedUp } ) @@ -521,6 +544,32 @@ do -- TASK_CARGO end + --- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_Cargo#TASK_CARGO Task + function Fsm:onafterLoad( TaskUnit, Task, From, Event, To, Cargo ) + + local TaskUnitName = TaskUnit:GetName() + self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) + + if not Cargo:IsLoaded() then + Cargo:Load( TaskUnit ) + TaskUnit:AddCargo( Cargo ) + end + + self:__SelectAction( 1 ) + + -- TODO:I need to find a more decent solution for this. + Task:E( { CargoPickedUp = Task.CargoPickedUp } ) + if Cargo:IsAlive() then + if Task.CargoPickedUp then + Task:CargoPickedUp( TaskUnit, Cargo ) + end + end + + end + + --- -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit @@ -530,7 +579,7 @@ do -- TASK_CARGO -- @param To -- @param Cargo -- @param Core.Zone#ZONE_BASE DeployZone - function Fsm:onafterPrepareUnBoarding( TaskUnit, Task, From, Event, To, Cargo ) + function Fsm:onafterPrepareUnBoarding( TaskUnit, Task, From, Event, To, Cargo ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID(), From, Event, To, Cargo } ) self.Cargo = Cargo @@ -586,33 +635,51 @@ do -- TASK_CARGO self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) self.Cargo:MessageToGroup( "UnBoarded ...", TaskUnit:GetGroup() ) + + self:Unload( self.Cargo ) + end - TaskUnit:RemoveCargo( self.Cargo ) + --- + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_Cargo#TASK_CARGO Task + function Fsm:onafterUnload( TaskUnit, Task, From, Event, To, Cargo, DeployZone ) + + local TaskUnitName = TaskUnit:GetName() + self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) + + if not Cargo:IsUnLoaded() then + if DeployZone then + Cargo:UnLoad( DeployZone:GetPointVec2(), 400, self ) + else + Cargo:UnLoad( TaskUnit:GetPointVec2():AddX(60), 400, self ) + end + end + TaskUnit:RemoveCargo( Cargo ) local NotInDeployZones = true for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do - if self.Cargo:IsInZone( DeployZone ) then + if Cargo:IsInZone( DeployZone ) then NotInDeployZones = false end end if NotInDeployZones == false then - self.Cargo:SetDeployed( true ) + Cargo:SetDeployed( true ) end -- TODO:I need to find a more decent solution for this. Task:E( { CargoDeployed = Task.CargoDeployed and "true" or "false" } ) - Task:E( { CargoIsAlive = self.Cargo:IsAlive() and "true" or "false" } ) - if self.Cargo:IsAlive() then + Task:E( { CargoIsAlive = Cargo:IsAlive() and "true" or "false" } ) + if Cargo:IsAlive() then if Task.CargoDeployed then - Task:CargoDeployed( TaskUnit, self.Cargo, self.DeployZone ) + Task:CargoDeployed( TaskUnit, Cargo, self.DeployZone ) end end self:Planned() self:__SelectAction( 1 ) end - return self diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 7da5fe917..1847a16cd 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -2783,16 +2783,6 @@ function CONTROLLABLE:IsAirPlane() return nil end -function CONTROLLABLE:GetSize() - - local DCSObject = self:GetDCSObject() - - if DCSObject then - return 1 - else - return 0 - end -end -- Message APIs \ No newline at end of file diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 5a7f064c4..b36a2d88b 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -129,7 +129,7 @@ function POSITIONABLE:GetPointVec2() local PositionablePointVec2 = POINT_VEC2:NewFromVec3( PositionableVec3 ) - self:T2( PositionablePointVec2 ) + self:T( PositionablePointVec2 ) return PositionablePointVec2 end @@ -309,6 +309,18 @@ function POSITIONABLE:IsAboveRunway() end +function POSITIONABLE:GetSize() + + local DCSObject = self:GetDCSObject() + + if DCSObject then + return 1 + else + return 0 + end +end + + --- Returns the POSITIONABLE heading in degrees. -- @param Wrapper.Positionable#POSITIONABLE self diff --git a/Moose Development/Moose/Wrapper/Static.lua b/Moose Development/Moose/Wrapper/Static.lua index 28e7c364b..2724ef8ed 100644 --- a/Moose Development/Moose/Wrapper/Static.lua +++ b/Moose Development/Moose/Wrapper/Static.lua @@ -48,6 +48,24 @@ STATIC = { } +function STATIC:Register( StaticName ) + local self = BASE:Inherit( self, POSITIONABLE:New( StaticName ) ) + self.StaticName = StaticName + return self +end + + +--- Finds a STATIC from the _DATABASE using a DCSStatic object. +-- @param #STATIC self +-- @param Dcs.DCSWrapper.Static#Static DCSStatic An existing DCS Static object reference. +-- @return #STATIC self +function STATIC:Find( DCSStatic ) + + local StaticName = DCSStatic:getName() + local StaticFound = _DATABASE:FindStatic( StaticName ) + return StaticFound +end + --- Finds a STATIC from the _DATABASE using the relevant Static Name. -- As an optional parameter, a briefing text can be given also. -- @param #STATIC self @@ -71,12 +89,6 @@ function STATIC:FindByName( StaticName, RaiseError ) return nil end -function STATIC:Register( StaticName ) - local self = BASE:Inherit( self, POSITIONABLE:New( StaticName ) ) - self.StaticName = StaticName - return self -end - function STATIC:GetDCSObject() local DCSStatic = StaticObject.getByName( self.StaticName ) @@ -88,6 +100,27 @@ function STATIC:GetDCSObject() return nil end +--- Returns a list of one @{Static}. +-- @param #STATIC self +-- @return #list A list of one @{Static}. +function STATIC:GetUnits() + self:F2( { self.StaticName } ) + local DCSStatic = self:GetDCSObject() + + local Statics = {} + + if DCSStatic then + Statics[1] = STATIC:Find( DCSStatic ) + self:T3( Statics ) + return Statics + end + + return nil +end + + + + function STATIC:GetThreatLevel() return 1, "Static" From 5971f6de090b1ce4ba22b0b404d63840f2adba51 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Thu, 5 Apr 2018 19:43:24 +0200 Subject: [PATCH 029/420] Added CARGO_SLINGLOAD --- Moose Development/Moose/Cargo/CargoCrate.lua | 8 +- .../Moose/Cargo/CargoSlingload.lua | 179 ++++++++++++++++++ Moose Development/Moose/Core/Database.lua | 5 +- Moose Development/Moose/Core/SpawnStatic.lua | 75 +++++--- .../Moose/Tasking/Task_CARGO.lua | 17 +- Moose Development/Moose/Wrapper/Static.lua | 2 +- Moose Setup/Moose.files | 1 + 7 files changed, 236 insertions(+), 51 deletions(-) create mode 100644 Moose Development/Moose/Cargo/CargoSlingload.lua diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua index 28a231d91..1f083a010 100644 --- a/Moose Development/Moose/Cargo/CargoCrate.lua +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -123,13 +123,13 @@ do -- CARGO_CRATE end --- Check if the cargo can be Boarded. - -- @param #CARGO self - function CARGO:CanBoard() + -- @param #CARGO_CRATE self + function CARGO_CRATE:CanBoard() return false end --- Check if the cargo can be Unboarded. - -- @param #CARGO self + -- @param #CARGO_CRATE self function CARGO_CRATE:CanUnboard() return false end @@ -172,6 +172,7 @@ do -- CARGO_CRATE self:F( {Coordinate = Coordinate } ) end + --- Check if Cargo is near to the Carrier. -- The Cargo is near to the Carrier within NearRadius. @@ -186,6 +187,7 @@ do -- CARGO_CRATE return self:IsNear( CargoCarrier:GetCoordinate(), NearRadius ) end + --- Check if CargoGroup is in the ReportRadius for the Cargo to be Loaded. -- @param #CARGO_CRATE self -- @param Core.Point#Coordinate Coordinate diff --git a/Moose Development/Moose/Cargo/CargoSlingload.lua b/Moose Development/Moose/Cargo/CargoSlingload.lua new file mode 100644 index 000000000..21ad7c7e9 --- /dev/null +++ b/Moose Development/Moose/Cargo/CargoSlingload.lua @@ -0,0 +1,179 @@ +--- **Cargo** -- Management of single cargo crates, which are based on a @{Static} object. The cargo can only be slingloaded. +-- +-- === +-- +-- ![Banner Image](..\Presentations\CARGO\Dia1.JPG) +-- +-- === +-- +-- ### [Demo Missions]() +-- +-- ### [YouTube Playlist]() +-- +-- === +-- +-- ### Author: **FlightControl** +-- ### Contributions: +-- +-- === +-- +-- @module CargoCrate + +do -- CARGO_SLINGLOAD + + --- Models the behaviour of cargo crates, which can only be slingloaded. + -- @type CARGO_SLINGLOAD + -- @extends #CARGO_REPRESENTABLE + + --- # CARGO\_CRATE class, extends @{#CARGO_REPRESENTABLE} + -- + -- The CARGO\_CRATE class defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. + -- + -- === + -- + -- @field #CARGO_SLINGLOAD + CARGO_SLINGLOAD = { + ClassName = "CARGO_SLINGLOAD" + } + + --- CARGO_SLINGLOAD Constructor. + -- @param #CARGO_SLINGLOAD self + -- @param Wrapper.Static#STATIC CargoStatic + -- @param #string Type + -- @param #string Name + -- @param #number ReportRadius (optional) + -- @param #number NearRadius (optional) + -- @return #CARGO_SLINGLOAD + function CARGO_SLINGLOAD:New( CargoStatic, Type, Name, ReportRadius, NearRadius ) + local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoStatic, Type, Name, nil, ReportRadius, NearRadius ) ) -- #CARGO_SLINGLOAD + self:F( { Type, Name, NearRadius } ) + + self.CargoObject = CargoStatic + + self:T( self.ClassName ) + + -- Cargo objects are added to the _DATABASE and SET_CARGO objects. + _EVENTDISPATCHER:CreateEventNewCargo( self ) + + self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead ) + self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead ) + self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead ) + + self:SetEventPriority( 4 ) + + return self + end + + + --- Check if the cargo can be Boarded. + -- @param #CARGO_SLINGLOAD self + function CARGO_SLINGLOAD:CanBoard() + return false + end + + --- Check if the cargo can be Unboarded. + -- @param #CARGO_SLINGLOAD self + function CARGO_SLINGLOAD:CanUnboard() + return false + end + + --- Check if the cargo can be Loaded. + -- @param #CARGO_SLINGLOAD self + function CARGO_SLINGLOAD:CanLoad() + return false + end + + --- Check if the cargo can be Unloaded. + -- @param #CARGO_SLINGLOAD self + function CARGO_SLINGLOAD:CanUnload() + return false + end + + --- Get the current Coordinate of the CargoGroup. + -- @param #CARGO_SLINGLOAD self + -- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup. + -- @return #nil There is no valid Cargo in the CargoGroup. + function CARGO_SLINGLOAD:GetCoordinate() + self:F() + + return self.CargoObject:GetCoordinate() + end + + --- Check if the CargoGroup is alive. + -- @param #CARGO_SLINGLOAD self + -- @return #boolean true if the CargoGroup is alive. + -- @return #boolean false if the CargoGroup is dead. + function CARGO_SLINGLOAD:IsAlive() + + local Alive = true + + -- When the Cargo is Loaded, the Cargo is in the CargoCarrier, so we check if the CargoCarrier is alive. + -- When the Cargo is not Loaded, the Cargo is the CargoObject, so we check if the CargoObject is alive. + if self:IsLoaded() then + Alive = Alive == true and self.CargoCarrier:IsAlive() + else + Alive = Alive == true and self.CargoObject:IsAlive() + end + + return Alive + + end + + + --- Route Cargo to Coordinate and randomize locations. + -- @param #CARGO_SLINGLOAD self + -- @param Core.Point#COORDINATE Coordinate + function CARGO_SLINGLOAD:RouteTo( Coordinate ) + self:F( {Coordinate = Coordinate } ) + + end + + + --- Check if Cargo is near to the Carrier. + -- The Cargo is near to the Carrier within NearRadius. + -- @param #CARGO_SLINGLOAD self + -- @param Wrapper.Group#GROUP CargoCarrier + -- @param #number NearRadius + -- @return #boolean The Cargo is near to the Carrier. + -- @return #nil The Cargo is not near to the Carrier. + function CARGO_SLINGLOAD:IsNear( CargoCarrier, NearRadius ) + self:F( {NearRadius = NearRadius } ) + + return self:IsNear( CargoCarrier:GetCoordinate(), NearRadius ) + end + + + --- Check if CargoGroup is in the ReportRadius for the Cargo to be Loaded. + -- @param #CARGO_SLINGLOAD self + -- @param Core.Point#Coordinate Coordinate + -- @return #boolean true if the CargoGroup is within the reporting radius. + function CARGO_SLINGLOAD:IsInRadius( Coordinate ) + self:F( { Coordinate } ) + + local Distance = 0 + if self:IsLoaded() then + Distance = Coordinate:DistanceFromPointVec2( self.CargoCarrier:GetPointVec2() ) + else + Distance = Coordinate:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) + end + self:T( Distance ) + + if Distance <= self.ReportRadius then + return true + else + return false + end + end + + --- Respawn the CargoGroup. + -- @param #CARGO_SLINGLOAD self + function CARGO_SLINGLOAD:Respawn() + + self:F( { "Respawning" } ) + + self:SetDeployed( false ) + self:SetStartState( "UnLoaded" ) + + end + +end diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 2235407cb..01b1c64a0 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -553,10 +553,7 @@ end --- @param #DATABASE self function DATABASE:GetStaticUnitTemplate( StaticName ) local StaticTemplate = self.Templates.Statics[StaticName].UnitTemplate - StaticTemplate.SpawnCoalitionID = self.Templates.Statics[StaticName].CoalitionID - StaticTemplate.SpawnCategoryID = self.Templates.Statics[StaticName].CategoryID - StaticTemplate.SpawnCountryID = self.Templates.Statics[StaticName].CountryID - return StaticTemplate + return StaticTemplate, self.Templates.Statics[StaticName].CoalitionID, self.Templates.Statics[StaticName].CategoryID, self.Templates.Statics[StaticName].CountryID end diff --git a/Moose Development/Moose/Core/SpawnStatic.lua b/Moose Development/Moose/Core/SpawnStatic.lua index 6772956e0..118b74b8c 100644 --- a/Moose Development/Moose/Core/SpawnStatic.lua +++ b/Moose Development/Moose/Core/SpawnStatic.lua @@ -81,14 +81,16 @@ SPAWNSTATIC = { -- @param #SPAWNSTATIC self -- @param #string SpawnTemplatePrefix is the name of the Group in the ME that defines the Template. Each new group will have the name starting with SpawnTemplatePrefix. -- @return #SPAWNSTATIC -function SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, CountryID ) --R2.1 +function SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, SpawnCountryID ) --R2.1 local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC self:F( { SpawnTemplatePrefix } ) - local TemplateStatic = STATIC:FindByName( SpawnTemplatePrefix ) + local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticUnitTemplate( SpawnTemplatePrefix ) if TemplateStatic then self.SpawnTemplatePrefix = SpawnTemplatePrefix - self.CountryID = CountryID + self.CountryID = SpawnCountryID or CountryID + self.CategoryID = CategoryID + self.CoalitionID = CoalitionID self.SpawnIndex = 0 else error( "SPAWNSTATIC:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" ) @@ -124,22 +126,28 @@ end function SPAWNSTATIC:Spawn( Heading, NewName ) --R2.3 self:F( { Heading, NewName } ) - local CountryName = _DATABASE.COUNTRY_NAME[self.CountryID] - local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix ) - StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex ) - StaticTemplate.heading = ( Heading / 180 ) * math.pi + if StaticTemplate then - StaticTemplate.CountryID = nil - StaticTemplate.CoalitionID = nil - StaticTemplate.CategoryID = nil + local CountryID = self.CountryID + local CountryName = _DATABASE.COUNTRY_NAME[CountryID] - local Static = coalition.addStaticObject( self.CountryID, StaticTemplate ) + StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex ) + StaticTemplate.heading = ( Heading / 180 ) * math.pi + + StaticTemplate.CountryID = nil + StaticTemplate.CoalitionID = nil + StaticTemplate.CategoryID = nil + + local Static = coalition.addStaticObject( CountryID, StaticTemplate ) + + self.SpawnIndex = self.SpawnIndex + 1 - self.SpawnIndex = self.SpawnIndex + 1 - - return Static + return Static + end + + return nil end @@ -153,30 +161,35 @@ end function SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1 self:F( { PointVec2, Heading, NewName } ) - local CountryName = _DATABASE.COUNTRY_NAME[self.CountryID] - local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix ) - StaticTemplate.x = PointVec2.x - StaticTemplate.y = PointVec2.z + if StaticTemplate then - StaticTemplate.units = nil - StaticTemplate.route = nil - StaticTemplate.groupId = nil + local CountryID = self.CountryID + local CountryName = _DATABASE.COUNTRY_NAME[CountryID] + StaticTemplate.x = PointVec2.x + StaticTemplate.y = PointVec2.z - StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex ) - StaticTemplate.heading = ( Heading / 180 ) * math.pi + StaticTemplate.units = nil + StaticTemplate.route = nil + StaticTemplate.groupId = nil + + StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex ) + StaticTemplate.heading = ( Heading / 180 ) * math.pi + + StaticTemplate.CountryID = nil + StaticTemplate.CoalitionID = nil + StaticTemplate.CategoryID = nil + + local Static = coalition.addStaticObject( CountryID, StaticTemplate ) + + self.SpawnIndex = self.SpawnIndex + 1 - StaticTemplate.CountryID = nil - StaticTemplate.CoalitionID = nil - StaticTemplate.CategoryID = nil + return Static + end - local Static = coalition.addStaticObject( self.CountryID, StaticTemplate ) - - self.SpawnIndex = self.SpawnIndex + 1 - - return Static + return nil end --- Creates a new @{Static} from a @{Zone}. diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 78cedeb0c..efb21f018 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -259,10 +259,10 @@ do -- TASK_CARGO end if NotInDeployZones then if not TaskUnit:InAir() then - if Cargo:CanBoard() then + if Cargo:CanBoard() == true then MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Board cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuBoardCargo, self, Cargo ):SetTime(MenuTime) else - if Cargo:CanLoad() then + if Cargo:CanLoad() == true then MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Load cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuLoadCargo, self, Cargo ):SetTime(MenuTime) end end @@ -278,10 +278,10 @@ do -- TASK_CARGO if Cargo:IsLoaded() then if not TaskUnit:InAir() then - if Cargo:CanUnboard() then + if Cargo:CanUnboard() == true then MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Unboard cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuUnboardCargo, self, Cargo ):SetTime(MenuTime) else - if Cargo:CanUnload() then + if Cargo:CanUnload() == true then MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Unload cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuUnloadCargo, self, Cargo ):SetTime(MenuTime) end end @@ -492,6 +492,7 @@ do -- TASK_CARGO self:__Board( -0.1 ) end end + --- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit @@ -533,14 +534,6 @@ do -- TASK_CARGO self:Load( self.Cargo ) - -- TODO:I need to find a more decent solution for this. - Task:E( { CargoPickedUp = Task.CargoPickedUp } ) - if self.Cargo:IsAlive() then - if Task.CargoPickedUp then - Task:CargoPickedUp( TaskUnit, self.Cargo ) - end - end - end diff --git a/Moose Development/Moose/Wrapper/Static.lua b/Moose Development/Moose/Wrapper/Static.lua index 2724ef8ed..6479037a9 100644 --- a/Moose Development/Moose/Wrapper/Static.lua +++ b/Moose Development/Moose/Wrapper/Static.lua @@ -134,7 +134,7 @@ function STATIC:ReSpawn( Coordinate, Heading ) -- todo: need to fix country - local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, country.id.USA ) + local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName ) SpawnStatic:SpawnFromPointVec2( Coordinate, Heading, self.StaticName ) end diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index d1328835d..c206b5416 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -36,6 +36,7 @@ Wrapper/Scenery.lua Cargo/Cargo.lua Cargo/CargoUnit.lua +Cargo/CargoSlingload.lua Cargo/CargoCrate.lua Cargo/CargoGroup.lua From 2ebd25d6778dfb4be7db441499fa3602d5c2d946 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Thu, 5 Apr 2018 19:54:24 +0200 Subject: [PATCH 030/420] Finish Cargo/FC/Csar --- Moose Development/Moose/Cargo/CargoSlingload.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/Moose Development/Moose/Cargo/CargoSlingload.lua b/Moose Development/Moose/Cargo/CargoSlingload.lua index 21ad7c7e9..a610e4b34 100644 --- a/Moose Development/Moose/Cargo/CargoSlingload.lua +++ b/Moose Development/Moose/Cargo/CargoSlingload.lua @@ -19,6 +19,7 @@ -- -- @module CargoCrate + do -- CARGO_SLINGLOAD --- Models the behaviour of cargo crates, which can only be slingloaded. From ed0c5b2264719c8b1a1833c67b2f5c83c50e01c4 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 5 Apr 2018 22:44:33 +0200 Subject: [PATCH 031/420] Added Range 1.0.3 Just copied from other branch. --- Moose Development/Moose/Functional/Range.lua | 66 +++++++++++++++++++- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 3c946ea79..e3e3590cb 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -227,7 +227,7 @@ RANGE.id="RANGE | " --- Range script version. -- @field #number version -RANGE.version="1.0.1" +RANGE.version="1.0.3" --TODO list --TODO: Add statics for strafe pits. @@ -1390,6 +1390,9 @@ function RANGE:_CheckInZone(_unitName) else + -- Get current ammo. + local _ammo=self:_GetAmmo(_unitName) + -- Result. local _result = self.strafeStatus[_unitID] @@ -1403,9 +1406,19 @@ function RANGE:_CheckInZone(_unitName) else _result.text = "POOR PASS" end + + local shots=_result.ammo-_ammo + local accur=0 + if shots>0 then + accur=_result.hits/shots*100 + end + -- Message text. local _text=string.format("%s, %s with %d hits on target %s.", self:_myname(_unitName), _result.text, _result.hits, _result.zone.name) + if shots and accur then + _text=_text..string.format("\nTotal rounds fired %d. Accuracy %.1f %%.", shots, accur) + end -- Send message. self:_DisplayMessageToGroup(_unit, _text) @@ -1446,9 +1459,12 @@ function RANGE:_CheckInZone(_unitName) -- Player is inside zone. if unitinzone then + + -- Get ammo at the beginning of the run. + local _ammo=self:_GetAmmo(_unitName) -- Init strafe status for this player. - self.strafeStatus[_unitID] = {hits = 0, zone = _targetZone, time = 1, pastfoulline=false } + self.strafeStatus[_unitID] = {hits = 0, zone = _targetZone, time = 1, ammo=_ammo, pastfoulline=false } -- Rolling in! local _msg=string.format("%s, rolling in on strafe pit %s.", self:_myname(_unitName), _targetZone.name) @@ -1550,6 +1566,52 @@ end ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Helper Functions +--- Get the number of shells a unit currently has. +-- @param #RANGE self +-- @param #string unitname Name of the player unit. +-- @return Number of shells left +function RANGE:_GetAmmo(unitname) + self:F(unitname) + + -- Init counter. + local ammo=0 + + local unit, playername = self:_GetPlayerUnitAndName(unitname) + + if unit and playername then + + local has_ammo=false + + local ammotable=unit:GetAmmo() + self:T2({ammotable=ammotable}) + + if ammotable ~= nil then + + local weapons=#ammotable + self:T2(RANGE.id..string.format("Number of weapons %d.", weapons)) + + for w=1,weapons do + + local Nammo=ammotable[w]["count"] + local Tammo=ammotable[w]["desc"]["typeName"] + + -- We are specifically looking for shells here. + if string.match(Tammo, "shell") then + + -- Add up all shells + ammo=ammo+Nammo + + local text=string.format("Player %s has %d rounds ammo of type %s", playername, Nammo, Tammo) + self:T(RANGE.id..text) + MESSAGE:New(text, 10):ToAllIf(self.Debug) + end + end + end + end + + return ammo +end + --- Mark targets on F10 map. -- @param #RANGE self -- @param #string _unitName Name of the player unit. From 269b52fd0eb7ab05314327700cebeb1ee71d7529 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Fri, 6 Apr 2018 21:28:43 +0200 Subject: [PATCH 032/420] Fixed slingload deploy problem. Fixed messaging. Now a message is shown when cargo reports itself when in LoadRange and when near, it will allow for loading of cargo. Fixed the perception that cargo can be boarded when loaded in an other carrier, which is totally wrong. --- Moose Development/Moose/Cargo/Cargo.lua | 234 ++++++++++++++---- Moose Development/Moose/Cargo/CargoCrate.lua | 51 ++-- Moose Development/Moose/Cargo/CargoGroup.lua | 43 +++- .../Moose/Cargo/CargoSlingload.lua | 52 ++-- Moose Development/Moose/Cargo/CargoUnit.lua | 2 +- .../Moose/Tasking/Task_CARGO.lua | 68 ++--- 6 files changed, 305 insertions(+), 145 deletions(-) diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 2528b1b32..cdba0ec8f 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -224,6 +224,7 @@ do -- CARGO Slingloadable = false, Moveable = false, Containable = false, + Reported = {}, } --- @type CARGO.CargoObjects @@ -235,12 +236,13 @@ do -- CARGO -- @param #string Type -- @param #string Name -- @param #number Weight + -- @param #number LoadRadius (optional) -- @param #number NearRadius (optional) -- @return #CARGO - function CARGO:New( Type, Name, Weight ) --R2.1 + function CARGO:New( Type, Name, Weight, LoadRadius, NearRadius ) --R2.1 local self = BASE:Inherit( self, FSM:New() ) -- #CARGO - self:F( { Type, Name, Weight } ) + self:F( { Type, Name, Weight, LoadRadius, NearRadius } ) self:SetStartState( "UnLoaded" ) self:AddTransition( { "UnLoaded", "Boarding" }, "Board", "Boarding" ) @@ -267,6 +269,9 @@ do -- CARGO self.Moveable = false self.Containable = false + self.LoadRadius = LoadRadius or 500 + self.NearRadius = NearRadius or 25 + self:SetDeployed( false ) self.CargoScheduler = SCHEDULER:New() @@ -405,8 +410,9 @@ do -- CARGO end end - --- Set the cargo as deployed + --- Set the cargo as deployed. -- @param #CARGO self + -- @param #boolean Deployed true if the cargo is to be deployed. false or nil otherwise. function CARGO:SetDeployed( Deployed ) self.Deployed = Deployed end @@ -507,7 +513,86 @@ do -- CARGO end + --- Set the Load radius, which is the radius till when the Cargo can be loaded. + -- @param #CARGO self + -- @param #number LoadRadius The radius till Cargo can be loaded. + -- @return #CARGO + function CARGO:SetLoadRadius( LoadRadius ) + self.LoadRadius = LoadRadius or 150 + end + --- Get the Load radius, which is the radius till when the Cargo can be loaded. + -- @param #CARGO self + -- @return #number The radius till Cargo can be loaded. + function CARGO:GetLoadRadius() + return self.LoadRadius + end + + + + --- Check if Cargo is in the LoadRadius for the Cargo to be Boarded or Loaded. + -- @param #CARGO self + -- @param Core.Point#Coordinate Coordinate + -- @return #boolean true if the CargoGroup is within the loading radius. + function CARGO:IsInLoadRadius( Coordinate ) + self:F( { Coordinate } ) + + local Distance = 0 + if self:IsUnLoaded() then + Distance = Coordinate:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) + self:T( Distance ) + if Distance <= self.LoadRadius then + return true + end + end + + return false + end + + + --- Check if the Cargo can report itself to be Boarded or Loaded. + -- @param #CARGO self + -- @param Core.Point#Coordinate Coordinate + -- @return #boolean true if the Cargo can report itself. + function CARGO:IsInReportRadius( Coordinate ) + self:F( { Coordinate } ) + + local Distance = 0 + if self:IsUnLoaded() then + Distance = Coordinate:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) + self:T( Distance ) + if Distance <= self.LoadRadius then + return true + end + end + + return false + end + + + --- Check if CargoCarrier is near the Cargo to be Loaded. + -- @param #CARGO self + -- @param Core.Point#POINT_VEC2 PointVec2 + -- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). + -- @return #boolean + function CARGO:IsNear( PointVec2, NearRadius ) + self:F( { PointVec2 = PointVec2, NearRadius = NearRadius } ) + + if self.CargoObject:IsAlive() then + --local Distance = PointVec2:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) + self:F( { CargoObjectName = self.CargoObject:GetName() } ) + self:F( { CargoObjectVec2 = self.CargoObject:GetVec2() } ) + self:F( { PointVec2 = PointVec2:GetVec2() } ) + local Distance = PointVec2:Get2DDistance( self.CargoObject:GetPointVec2() ) + self:T( Distance ) + + if Distance <= NearRadius then + return true + end + end + + return false + end @@ -534,30 +619,6 @@ do -- CARGO end - --- Check if CargoCarrier is near the Cargo to be Loaded. - -- @param #CARGO self - -- @param Core.Point#POINT_VEC2 PointVec2 - -- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). - -- @return #boolean - function CARGO:IsNear( PointVec2, NearRadius ) - self:F( { PointVec2 = PointVec2, NearRadius = NearRadius } ) - - if self.CargoObject:IsAlive() then - --local Distance = PointVec2:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) - self:F( { CargoObjectName = self.CargoObject:GetName() } ) - self:F( { CargoObjectVec2 = self.CargoObject:GetVec2() } ) - self:F( { PointVec2 = PointVec2:GetVec2() } ) - local Distance = PointVec2:Get2DDistance( self.CargoObject:GetPointVec2() ) - self:T( Distance ) - - if Distance <= NearRadius then - return true - end - end - - return false - end - --- Get the current PointVec2 of the cargo. -- @param #CARGO self -- @return Core.Point#POINT_VEC2 @@ -580,6 +641,85 @@ do -- CARGO self.Weight = Weight return self end + + --- Send a CC message to a @{Group}. + -- @param #CARGO self + -- @param #string Message + -- @param Wrapper.Group#GROUP CarrierGroup The Carrier Group. + -- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown. + function CARGO:MessageToGroup( Message, CarrierGroup, Name ) + + MESSAGE:New( Message, 20, "Cargo " .. self:GetName() ):ToGroup( CarrierGroup ) + + end + + --- Report to a Carrier Group. + -- @param #CARGO self + -- @param #string Action The string describing the action for the cargo. + -- @param Wrapper.Group#GROUP CarrierGroup The Carrier Group to send the report to. + -- @return #CARGO + function CARGO:Report( ReportText, Action, CarrierGroup ) + + if not self.Reported[CarrierGroup] or not self.Reported[CarrierGroup][Action] then + self.Reported[CarrierGroup] = {} + self.Reported[CarrierGroup][Action] = true + self:MessageToGroup( ReportText, CarrierGroup ) + if self.ReportFlareColor then + if not self.Reported[CarrierGroup]["Flaring"] then + self:Flare( self.ReportFlareColor ) + self.Reported[CarrierGroup]["Flaring"] = true + end + end + if self.ReportSmokeColor then + if not self.Reported[CarrierGroup]["Smoking"] then + self:Smoke( self.ReportSmokeColor ) + self.Reported[CarrierGroup]["Smoking"] = true + end + end + end + end + + + --- Report to a Carrier Group with a Flaring signal. + -- @param #CARGO self + -- @param Utils#UTILS.FlareColor FlareColor the color of the flare. + -- @return #CARGO + function CARGO:ReportFlare( FlareColor ) + + self.ReportFlareColor = FlareColor + end + + + --- Report to a Carrier Group with a Smoking signal. + -- @param #CARGO self + -- @param Utils#UTILS.SmokeColor SmokeColor the color of the smoke. + -- @return #CARGO + function CARGO:ReportSmoke( SmokeColor ) + + self.ReportSmokeColor = SmokeColor + end + + + --- Reset the reporting for a Carrier Group. + -- @param #CARGO self + -- @param #string Action The string describing the action for the cargo. + -- @param Wrapper.Group#GROUP CarrierGroup The Carrier Group to send the report to. + -- @return #CARGO + function CARGO:ReportReset( Action, CarrierGroup ) + + self.Reported[CarrierGroup][Action] = nil + end + + --- Reset all the reporting for a Carrier Group. + -- @param #CARGO self + -- @param Wrapper.Group#GROUP CarrierGroup The Carrier Group to send the report to. + -- @return #CARGO + function CARGO:ReportResetAll( CarrierGroup ) + + self.Reported[CarrierGroup] = nil + end + + end -- CARGO @@ -600,16 +740,13 @@ do -- CARGO_REPRESENTABLE -- @param #string Type -- @param #string Name -- @param #number Weight - -- @param #number ReportRadius (optional) + -- @param #number LoadRadius (optional) -- @param #number NearRadius (optional) -- @return #CARGO_REPRESENTABLE - function CARGO_REPRESENTABLE:New( CargoObject, Type, Name, Weight, ReportRadius, NearRadius ) - local self = BASE:Inherit( self, CARGO:New( Type, Name, Weight ) ) -- #CARGO_REPRESENTABLE - self:F( { Type, Name, Weight, ReportRadius, NearRadius } ) + function CARGO_REPRESENTABLE:New( CargoObject, Type, Name, Weight, LoadRadius, NearRadius ) + local self = BASE:Inherit( self, CARGO:New( Type, Name, Weight, LoadRadius, NearRadius ) ) -- #CARGO_REPRESENTABLE + self:F( { Type, Name, Weight, LoadRadius, NearRadius } ) - self.ReportRadius = ReportRadius or 500 - self.NearRadius = NearRadius or 25 - return self end @@ -661,14 +798,12 @@ do -- CARGO_REPORTABLE -- @param #string Type -- @param #string Name -- @param #number Weight - -- @param #number ReportRadius (optional) + -- @param #number LoadRadius (optional) -- @param #number NearRadius (optional) -- @return #CARGO_REPORTABLE - function CARGO_REPORTABLE:New( Type, Name, Weight, ReportRadius ) - local self = BASE:Inherit( self, CARGO:New( Type, Name, Weight ) ) -- #CARGO_REPORTABLE - self:F( { Type, Name, Weight, ReportRadius } ) - - self.ReportRadius = ReportRadius or 1000 + function CARGO_REPORTABLE:New( Type, Name, Weight, LoadRadius, NearRadius ) + local self = BASE:Inherit( self, CARGO:New( Type, Name, Weight, LoadRadius, NearRadius ) ) -- #CARGO_REPORTABLE + self:F( { Type, Name, Weight, LoadRadius, NearRadius } ) return self end @@ -680,19 +815,10 @@ do -- CARGO_REPORTABLE -- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown. function CARGO_REPORTABLE:MessageToGroup( Message, TaskGroup, Name ) - local Prefix = Name and "@ " .. Name .. ": " or "@ " .. TaskGroup:GetCallsign() .. ": " - Message = Prefix .. Message - MESSAGE:New( Message, 20, "Cargo: " .. self:GetName() ):ToGroup( TaskGroup ) + MESSAGE:New( Message, 20, "Cargo " .. self:GetName() ):ToGroup( TaskGroup ) end - --- Get the Report radius, which is the radius when the Cargo is reporting itself. - -- @param #CARGO_REPORTABLE self - -- @return #number The range till Cargo reports itself. - function CARGO_REPORTABLE:GetBoardingRange() - return self.ReportRadius - end - end @@ -717,12 +843,12 @@ do -- CARGO_PACKAGE -- @param #string Type -- @param #string Name -- @param #number Weight --- @param #number ReportRadius (optional) +-- @param #number LoadRadius (optional) -- @param #number NearRadius (optional) -- @return #CARGO_PACKAGE -function CARGO_PACKAGE:New( CargoCarrier, Type, Name, Weight, ReportRadius, NearRadius ) - local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoCarrier, Type, Name, Weight, ReportRadius, NearRadius ) ) -- #CARGO_PACKAGE - self:F( { Type, Name, Weight, ReportRadius, NearRadius } ) +function CARGO_PACKAGE:New( CargoCarrier, Type, Name, Weight, LoadRadius, NearRadius ) + local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoCarrier, Type, Name, Weight, LoadRadius, NearRadius ) ) -- #CARGO_PACKAGE + self:F( { Type, Name, Weight, LoadRadius, NearRadius } ) self:T( CargoCarrier ) self.CargoCarrier = CargoCarrier diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua index 1f083a010..44ef41af6 100644 --- a/Moose Development/Moose/Cargo/CargoCrate.lua +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -23,7 +23,7 @@ do -- CARGO_CRATE --- Models the behaviour of cargo crates, which can be slingloaded and boarded on helicopters. -- @type CARGO_CRATE - -- @extends #CARGO_REPRESENTABLE + -- @extends Cargo.Cargo#CARGO_REPRESENTABLE --- # CARGO\_CRATE class, extends @{#CARGO_REPRESENTABLE} -- @@ -42,11 +42,11 @@ do -- CARGO_CRATE -- @param Wrapper.Static#STATIC CargoStatic -- @param #string Type -- @param #string Name - -- @param #number ReportRadius (optional) + -- @param #number LoadRadius (optional) -- @param #number NearRadius (optional) -- @return #CARGO_CRATE - function CARGO_CRATE:New( CargoStatic, Type, Name, ReportRadius, NearRadius ) - local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoStatic, Type, Name, nil, ReportRadius, NearRadius ) ) -- #CARGO_CRATE + function CARGO_CRATE:New( CargoStatic, Type, Name, LoadRadius, NearRadius ) + local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoStatic, Type, Name, nil, LoadRadius, NearRadius ) ) -- #CARGO_CRATE self:F( { Type, Name, NearRadius } ) self.CargoObject = CargoStatic @@ -134,6 +134,27 @@ do -- CARGO_CRATE return false end + --- Check if Cargo Crate is in the radius for the Cargo to be Boarded or Loaded. + -- @param #CARGO self + -- @param Core.Point#Coordinate Coordinate + -- @return #boolean true if the Cargo Crate is within the loading radius. + function CARGO_CRATE:IsInLoadRadius( Coordinate ) + self:F( { Coordinate } ) + + local Distance = 0 + if self:IsUnLoaded() then + Distance = Coordinate:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) + self:T( Distance ) + if Distance <= self.NearRadius then + return true + end + end + + return false + end + + + --- Get the current Coordinate of the CargoGroup. -- @param #CARGO_CRATE self -- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup. @@ -188,28 +209,6 @@ do -- CARGO_CRATE end - --- Check if CargoGroup is in the ReportRadius for the Cargo to be Loaded. - -- @param #CARGO_CRATE self - -- @param Core.Point#Coordinate Coordinate - -- @return #boolean true if the CargoGroup is within the reporting radius. - function CARGO_CRATE:IsInRadius( Coordinate ) - self:F( { Coordinate } ) - - local Distance = 0 - if self:IsLoaded() then - Distance = Coordinate:DistanceFromPointVec2( self.CargoCarrier:GetPointVec2() ) - else - Distance = Coordinate:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) - end - self:T( Distance ) - - if Distance <= self.ReportRadius then - return true - else - return false - end - end - --- Respawn the CargoGroup. -- @param #CARGO_CRATE self function CARGO_CRATE:Respawn() diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 2a5f8fee5..07806b45c 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -23,7 +23,7 @@ do -- CARGO_GROUP --- @type CARGO_GROUP - -- @extends #CARGO_REPORTABLE + -- @extends Cargo.Cargo#CARGO_REPORTABLE -- @field Core.Set#SET_CARGO CargoSet The collection of derived CARGO objects. -- @field #string GroupName The name of the CargoGroup. @@ -45,12 +45,12 @@ do -- CARGO_GROUP -- @param Wrapper.Group#GROUP CargoGroup -- @param #string Type -- @param #string Name --- @param #number ReportRadius (optional) +-- @param #number LoadRadius (optional) -- @param #number NearRadius (optional) -- @return #CARGO_GROUP -function CARGO_GROUP:New( CargoGroup, Type, Name, ReportRadius ) - local self = BASE:Inherit( self, CARGO_REPORTABLE:New( Type, Name, 0, ReportRadius ) ) -- #CARGO_GROUP - self:F( { Type, Name, ReportRadius } ) +function CARGO_GROUP:New( CargoGroup, Type, Name, LoadRadius ) + local self = BASE:Inherit( self, CARGO_REPORTABLE:New( Type, Name, 0, LoadRadius ) ) -- #CARGO_GROUP + self:F( { Type, Name, LoadRadius } ) self.CargoSet = SET_CARGO:New() @@ -455,11 +455,11 @@ function CARGO_GROUP:OnEventCargoDead( EventData ) return nil end - --- Check if CargoGroup is in the ReportRadius for the Cargo to be Loaded. + --- Check if Cargo Group is in the radius for the Cargo to be Boarded. -- @param #CARGO_GROUP self -- @param Core.Point#Coordinate Coordinate - -- @return #boolean true if the CargoGroup is within the reporting radius. - function CARGO_GROUP:IsInRadius( Coordinate ) + -- @return #boolean true if the Cargo Group is within the load radius. + function CARGO_GROUP:IsInLoadRadius( Coordinate ) self:F( { Coordinate } ) local Cargo = self.CargoSet:GetFirst() -- #CARGO @@ -473,7 +473,7 @@ function CARGO_GROUP:OnEventCargoDead( EventData ) end self:T( Distance ) - if Distance <= self.ReportRadius then + if Distance <= self.LoadRadius then return true else return false @@ -484,6 +484,31 @@ function CARGO_GROUP:OnEventCargoDead( EventData ) end + + --- Check if Cargo Group is in the report radius. + -- @param #CARGO_GROUP self + -- @param Core.Point#Coordinate Coordinate + -- @return #boolean true if the Cargo Group is within the report radius. + function CARGO_GROUP:IsInReportRadius( Coordinate ) + self:F( { Coordinate } ) + + local Cargo = self.CargoSet:GetFirst() -- #CARGO + + if Cargo then + local Distance = 0 + if Cargo:IsUnLoaded() then + Distance = Coordinate:DistanceFromPointVec2( Cargo.CargoObject:GetPointVec2() ) + self:T( Distance ) + if Distance <= self.LoadRadius then + return true + end + end + end + + return nil + + end + --- Respawn the CargoGroup. -- @param #CARGO_GROUP self function CARGO_GROUP:Respawn() diff --git a/Moose Development/Moose/Cargo/CargoSlingload.lua b/Moose Development/Moose/Cargo/CargoSlingload.lua index a610e4b34..91f69f873 100644 --- a/Moose Development/Moose/Cargo/CargoSlingload.lua +++ b/Moose Development/Moose/Cargo/CargoSlingload.lua @@ -24,7 +24,7 @@ do -- CARGO_SLINGLOAD --- Models the behaviour of cargo crates, which can only be slingloaded. -- @type CARGO_SLINGLOAD - -- @extends #CARGO_REPRESENTABLE + -- @extends Cargo.Cargo#CARGO_REPRESENTABLE --- # CARGO\_CRATE class, extends @{#CARGO_REPRESENTABLE} -- @@ -42,11 +42,11 @@ do -- CARGO_SLINGLOAD -- @param Wrapper.Static#STATIC CargoStatic -- @param #string Type -- @param #string Name - -- @param #number ReportRadius (optional) + -- @param #number LoadRadius (optional) -- @param #number NearRadius (optional) -- @return #CARGO_SLINGLOAD - function CARGO_SLINGLOAD:New( CargoStatic, Type, Name, ReportRadius, NearRadius ) - local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoStatic, Type, Name, nil, ReportRadius, NearRadius ) ) -- #CARGO_SLINGLOAD + function CARGO_SLINGLOAD:New( CargoStatic, Type, Name, LoadRadius, NearRadius ) + local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoStatic, Type, Name, nil, LoadRadius, NearRadius ) ) -- #CARGO_SLINGLOAD self:F( { Type, Name, NearRadius } ) self.CargoObject = CargoStatic @@ -90,6 +90,28 @@ do -- CARGO_SLINGLOAD return false end + + --- Check if Cargo Slingload is in the radius for the Cargo to be Boarded or Loaded. + -- @param #CARGO_SLINGLOAD self + -- @param Core.Point#Coordinate Coordinate + -- @return #boolean true if the Cargo Slingload is within the loading radius. + function CARGO_SLINGLOAD:IsInLoadRadius( Coordinate ) + self:F( { Coordinate } ) + + local Distance = 0 + if self:IsUnLoaded() then + Distance = Coordinate:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) + self:T( Distance ) + if Distance <= self.NearRadius then + return true + end + end + + return false + end + + + --- Get the current Coordinate of the CargoGroup. -- @param #CARGO_SLINGLOAD self -- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup. @@ -144,28 +166,6 @@ do -- CARGO_SLINGLOAD end - --- Check if CargoGroup is in the ReportRadius for the Cargo to be Loaded. - -- @param #CARGO_SLINGLOAD self - -- @param Core.Point#Coordinate Coordinate - -- @return #boolean true if the CargoGroup is within the reporting radius. - function CARGO_SLINGLOAD:IsInRadius( Coordinate ) - self:F( { Coordinate } ) - - local Distance = 0 - if self:IsLoaded() then - Distance = Coordinate:DistanceFromPointVec2( self.CargoCarrier:GetPointVec2() ) - else - Distance = Coordinate:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) - end - self:T( Distance ) - - if Distance <= self.ReportRadius then - return true - else - return false - end - end - --- Respawn the CargoGroup. -- @param #CARGO_SLINGLOAD self function CARGO_SLINGLOAD:Respawn() diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index ba9c17f61..5f6b4e941 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -44,7 +44,7 @@ do -- CARGO_UNIT -- @param #string Type -- @param #string Name -- @param #number Weight - -- @param #number ReportRadius (optional) + -- @param #number LoadRadius (optional) -- @param #number NearRadius (optional) -- @return #CARGO_UNIT function CARGO_UNIT:New( CargoUnit, Type, Name, Weight, NearRadius ) diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index efb21f018..101399783 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -230,7 +230,7 @@ do -- TASK_CARGO Task.SetCargo:ForEachCargo( - --- @param Core.Cargo#CARGO Cargo + --- @param Cargo.Cargo#CARGO Cargo function( Cargo ) if Cargo:IsAlive() then @@ -250,7 +250,7 @@ do -- TASK_CARGO if Cargo:IsUnLoaded() then if CargoItemCount <= Task.CargoLimit then - if Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then + if Cargo:IsInReportRadius( TaskUnit:GetPointVec2() ) then local NotInDeployZones = true for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do if Cargo:IsInZone( DeployZone ) then @@ -260,20 +260,50 @@ do -- TASK_CARGO if NotInDeployZones then if not TaskUnit:InAir() then if Cargo:CanBoard() == true then - MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Board cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuBoardCargo, self, Cargo ):SetTime(MenuTime) + if Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then + Cargo:Report( "Reporting for boarding at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "board", TaskUnit:GetGroup() ) + MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Board cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuBoardCargo, self, Cargo ):SetTime(MenuTime) + else + Cargo:Report( "Reporting at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "reporting", TaskUnit:GetGroup() ) + end else if Cargo:CanLoad() == true then - MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Load cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuLoadCargo, self, Cargo ):SetTime(MenuTime) + if Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then + Cargo:Report( "Reporting for loading at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "load", TaskUnit:GetGroup() ) + MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Load cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuLoadCargo, self, Cargo ):SetTime(MenuTime) + else + Cargo:Report( "Reporting at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "reporting", TaskUnit:GetGroup() ) + end end end TaskUnit.Menu:SetTime( MenuTime ) + else + Cargo:ReportResetAll( TaskUnit:GetGroup() ) end end else MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Route to Pickup cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuRouteToPickup, self, Cargo ):SetTime(MenuTime) TaskUnit.Menu:SetTime( MenuTime ) + Cargo:ReportResetAll( TaskUnit:GetGroup() ) end end + -- Cargo in deployzones are flagged as deployed. + for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do + if Cargo:IsInZone( DeployZone ) then + if not Cargo:IsDeployed() then + Cargo:SetDeployed( true ) + -- TODO:I need to find a more decent solution for this. + Task:E( { CargoDeployed = Task.CargoDeployed and "true" or "false" } ) + Task:E( { CargoIsAlive = Cargo:IsAlive() and "true" or "false" } ) + if Cargo:IsAlive() then + if Task.CargoDeployed then + Task:CargoDeployed( TaskUnit, Cargo, DeployZone ) + end + end + end + end + end + end if Cargo:IsLoaded() then @@ -303,7 +333,7 @@ do -- TASK_CARGO TaskUnit.Menu:Remove( MenuTime ) - self:__SelectAction( -15 ) + self:__SelectAction( -5 ) end @@ -441,7 +471,7 @@ do -- TASK_CARGO self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) if self.Cargo:IsAlive() then - if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then + if self.Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then if TaskUnit:InAir() then self:__Land( -10, Action ) else @@ -465,7 +495,7 @@ do -- TASK_CARGO self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) if self.Cargo:IsAlive() then - if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then + if self.Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then if TaskUnit:InAir() then self:__Land( -0.1, Action ) else @@ -506,7 +536,7 @@ do -- TASK_CARGO end if self.Cargo:IsAlive() then - if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then + if self.Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then if TaskUnit:InAir() then --- ABORT the boarding. Split group if any and go back to select action. else @@ -649,26 +679,6 @@ do -- TASK_CARGO end end TaskUnit:RemoveCargo( Cargo ) - - local NotInDeployZones = true - for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do - if Cargo:IsInZone( DeployZone ) then - NotInDeployZones = false - end - end - - if NotInDeployZones == false then - Cargo:SetDeployed( true ) - end - - -- TODO:I need to find a more decent solution for this. - Task:E( { CargoDeployed = Task.CargoDeployed and "true" or "false" } ) - Task:E( { CargoIsAlive = Cargo:IsAlive() and "true" or "false" } ) - if Cargo:IsAlive() then - if Task.CargoDeployed then - Task:CargoDeployed( TaskUnit, Cargo, self.DeployZone ) - end - end self:Planned() self:__SelectAction( 1 ) @@ -739,7 +749,7 @@ do -- TASK_CARGO local ActRouteCargo = ProcessUnit:GetProcess( "RoutingToPickup", "RouteToPickupPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT ActRouteCargo:Reset() ActRouteCargo:SetCoordinate( Cargo:GetCoordinate() ) - ActRouteCargo:SetRange( Cargo:GetBoardingRange() ) + ActRouteCargo:SetRange( Cargo:GetLoadRadius() ) ActRouteCargo:SetMenuCancel( TaskUnit:GetGroup(), "Cancel Routing to Cargo " .. Cargo:GetName(), TaskUnit.Menu ) ActRouteCargo:Start() return self From a247f56c7e8fed690884e284dfb48671bd1dec01 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sat, 7 Apr 2018 10:18:52 +0200 Subject: [PATCH 033/420] Progress on AI_CARGO_TROOPS --- .../Moose/AI/AI_Cargo_Troops.lua | 170 ++++++++++++++---- Moose Development/Moose/Cargo/Cargo.lua | 14 +- Moose Development/Moose/Cargo/CargoUnit.lua | 72 ++++---- Moose Development/Moose/Core/Zone.lua | 8 + Moose Development/Moose/Wrapper/Group.lua | 9 +- .../Moose/Wrapper/Identifiable.lua | 20 +++ Moose Development/Moose/Wrapper/Object.lua | 2 + 7 files changed, 215 insertions(+), 80 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Troops.lua b/Moose Development/Moose/AI/AI_Cargo_Troops.lua index 754409aeb..2051fa22d 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Troops.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Troops.lua @@ -24,19 +24,16 @@ AI_CARGO_TROOPS = { --- Creates a new AI_CARGO_TROOPS object. -- @param #AI_CARGO_TROOPS self +-- @param Wrapper.Unit#UNIT CargoCarrier +-- @param Cargo.CargoGroup#CARGO_GROUP CargoGroup +-- @param #number CombatRadius -- @return #AI_CARGO_TROOPS function AI_CARGO_TROOPS:New( CargoCarrier, CargoGroup, CombatRadius ) local self = BASE:Inherit( self, FSM_CONTROLLABLE:New( ) ) -- #AI_CARGO_TROOPS - self.CargoCarrier = CargoCarrier -- Wrapper.Unit#UNIT - self.CargoGroup = CargoGroup -- Core.Cargo#CARGO_GROUP + self.CargoGroup = CargoGroup -- Cargo.CargoGroup#CARGO_GROUP self.CombatRadius = CombatRadius - - self.Zone = ZONE_UNIT:New( self.CargoCarrier:GetName() .. "-Zone", self.CargoCarrier, CombatRadius ) - self.Coalition = self.CargoCarrier:GetCoalition() - - self:SetControllable( CargoCarrier ) self:SetStartState( "UnLoaded" ) @@ -50,14 +47,91 @@ function AI_CARGO_TROOPS:New( CargoCarrier, CargoGroup, CombatRadius ) self:AddTransition( "*", "Monitor", "*" ) self:AddTransition( "*", "Follow", "Following" ) self:AddTransition( "*", "Guard", "Guarding" ) + + self:AddTransition( "*", "Destroyed", "Destroyed" ) self:__Monitor( 1 ) - self:__Load( 1 ) + + self:SetCarrier( CargoCarrier ) return self end +--- Set the Carrier. +-- @param #AI_CARGO_TROOPS self +-- @param Wrapper.Unit#UNIT CargoCarrier +-- @return #AI_CARGO_TROOPS +function AI_CARGO_TROOPS:SetCarrier( CargoCarrier ) + + self.CargoCarrier = CargoCarrier -- Wrapper.Unit#UNIT + self.CargoCarrier:SetState( self.CargoCarrier, "AI_CARGO_TROOPS", self ) + + CargoCarrier:HandleEvent( EVENTS.Dead ) + CargoCarrier:HandleEvent( EVENTS.Hit ) + + function CargoCarrier:OnEventDead( EventData ) + self:F({"dead"}) + local AICargoTroops = self:GetState( self, "AI_CARGO_TROOPS" ) + self:F({AICargoTroops=AICargoTroops}) + if AICargoTroops then + self:F({}) + if not AICargoTroops:Is( "Loaded" ) then + -- There are enemies within combat range. Unload the CargoCarrier. + AICargoTroops:Destroyed() + end + end + end + + function CargoCarrier:OnEventHit( EventData ) + self:F({"hit"}) + local AICargoTroops = self:GetState( self, "AI_CARGO_TROOPS" ) + if AICargoTroops then + self:F( { OnHitLoaded = AICargoTroops:Is( "Loaded" ) } ) + if AICargoTroops:Is( "Loaded" ) or AICargoTroops:Is( "Boarding" ) then + -- There are enemies within combat range. Unload the CargoCarrier. + AICargoTroops:Unload() + end + end + end + + self.Zone = ZONE_UNIT:New( self.CargoCarrier:GetName() .. "-Zone", self.CargoCarrier, self.CombatRadius ) + self.Coalition = self.CargoCarrier:GetCoalition() + + self:SetControllable( CargoCarrier ) + + self:Guard() + + return self +end + + +--- Find a free Carrier within a range. +-- @param #AI_CARGO_TROOPS self +-- @param Core.Point#COORDINATE Coordinate +-- @param #number Radius +-- @return Wrapper.Unit#UNIT NewCarrier +function AI_CARGO_TROOPS:FindCarrier( Coordinate, Radius ) + + local CoordinateZone = ZONE_RADIUS:New( "Zone" , Coordinate:GetVec2(), Radius ) + CoordinateZone:Scan( { Object.Category.UNIT } ) + for _, DCSUnit in pairs( CoordinateZone:GetScannedUnits() ) do + local NearUnit = UNIT:Find( DCSUnit ) + self:F({NearUnit=NearUnit}) + if not NearUnit:GetState( NearUnit, "AI_CARGO_TROOPS" ) then + local Attributes = NearUnit:GetDesc() + self:F({Desc=Attributes}) + if NearUnit:HasAttribute( "Trucks" ) then + self:SetCarrier( NearUnit ) + break + end + end + end + +end + + + --- Follow Infantry to the Carrier. -- @param #AI_CARGO_TROOPS self -- @param #AI_CARGO_TROOPS Me @@ -68,40 +142,44 @@ function AI_CARGO_TROOPS:FollowToCarrier( Me, CargoCarrier, InfantryGroup ) self:F( { self = self:GetClassNameAndID(), InfantryGroup = InfantryGroup:GetName() } ) - -- We check if the Cargo is near to the CargoCarrier. - if InfantryGroup:IsPartlyInZone( ZONE_UNIT:New( "Radius", CargoCarrier, 5 ) ) then + --if self:Is( "Following" ) then - -- The Cargo does not need to follow the Carrier. - Me:Guard() + if CargoCarrier:IsAlive() then + -- We check if the Cargo is near to the CargoCarrier. + if InfantryGroup:IsPartlyInZone( ZONE_UNIT:New( "Radius", CargoCarrier, 5 ) ) then - else + -- The Cargo does not need to follow the Carrier. + Me:Guard() - self:F( { InfantryGroup = InfantryGroup:GetName() } ) - - if InfantryGroup:IsAlive() then - + else + self:F( { InfantryGroup = InfantryGroup:GetName() } ) - - local Waypoints = {} - - -- Calculate the new Route. - local FromCoord = InfantryGroup:GetCoordinate() - local FromGround = FromCoord:WaypointGround( 10, "Diamond" ) - self:F({FromGround=FromGround}) - table.insert( Waypoints, FromGround ) - - local ToCoord = CargoCarrier:GetCoordinate() - local ToGround = ToCoord:WaypointGround( 10, "Diamond" ) - self:F({ToGround=ToGround}) - table.insert( Waypoints, ToGround ) - - local TaskRoute = InfantryGroup:TaskFunction( "AI_CARGO_TROOPS.FollowToCarrier", Me, CargoCarrier, InfantryGroup ) - - self:F({Waypoints = Waypoints}) - local Waypoint = Waypoints[#Waypoints] - InfantryGroup:SetTaskWaypoint( Waypoint, TaskRoute ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone. - InfantryGroup:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. + if InfantryGroup:IsAlive() then + + self:F( { InfantryGroup = InfantryGroup:GetName() } ) + + local Waypoints = {} + + -- Calculate the new Route. + local FromCoord = InfantryGroup:GetCoordinate() + local FromGround = FromCoord:WaypointGround( 10, "Diamond" ) + self:F({FromGround=FromGround}) + table.insert( Waypoints, FromGround ) + + local ToCoord = CargoCarrier:GetCoordinate():GetRandomCoordinateInRadius( 10, 5 ) + local ToGround = ToCoord:WaypointGround( 10, "Diamond" ) + self:F({ToGround=ToGround}) + table.insert( Waypoints, ToGround ) + + local TaskRoute = InfantryGroup:TaskFunction( "AI_CARGO_TROOPS.FollowToCarrier", Me, CargoCarrier, InfantryGroup ) + + self:F({Waypoints = Waypoints}) + local Waypoint = Waypoints[#Waypoints] + InfantryGroup:SetTaskWaypoint( Waypoint, TaskRoute ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone. + + InfantryGroup:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. + end end end end @@ -132,6 +210,22 @@ function AI_CARGO_TROOPS:onafterMonitor( CargoCarrier, From, Event, To ) self:Follow() end end + if self:Is( "Following" ) then + local Distance = Coordinate:Get2DDistance( self.CargoGroup:GetCoordinate() ) + self:F( { Distance = Distance } ) + if Distance > 40 then + CargoCarrier:RouteStop() + self.CarrierStopped = true + else + if self.CarrierStopped then + if self.CargoGroup:IsNear( CargoCarrier, 10 ) then + CargoCarrier:RouteResume() + self.CarrierStopped = nil + end + end + end + end + end self.CarrierCoordinate = CargoCarrier:GetCoordinate() end @@ -149,7 +243,7 @@ function AI_CARGO_TROOPS:onafterLoad( CargoCarrier, From, Event, To ) if CargoCarrier and CargoCarrier:IsAlive() then CargoCarrier:RouteStop() self:__Board( 10 ) - self.CargoGroup:Board( CargoCarrier, 100 ) + self.CargoGroup:Board( CargoCarrier, 10 ) end end diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index cdba0ec8f..eb44217a3 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -325,7 +325,7 @@ do -- CARGO -- @param #CARGO self function CARGO:Destroy() if self.CargoObject then - self.CargoObject:Destroy() + self.CargoObject:Destroy( false ) end self:Destroyed() end @@ -576,21 +576,23 @@ do -- CARGO -- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). -- @return #boolean function CARGO:IsNear( PointVec2, NearRadius ) - self:F( { PointVec2 = PointVec2, NearRadius = NearRadius } ) + self:F2( { PointVec2 = PointVec2, NearRadius = NearRadius } ) if self.CargoObject:IsAlive() then --local Distance = PointVec2:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) - self:F( { CargoObjectName = self.CargoObject:GetName() } ) - self:F( { CargoObjectVec2 = self.CargoObject:GetVec2() } ) - self:F( { PointVec2 = PointVec2:GetVec2() } ) + --self:F( { CargoObjectName = self.CargoObject:GetName() } ) + --self:F( { CargoObjectVec2 = self.CargoObject:GetVec2() } ) + --self:F( { PointVec2 = PointVec2:GetVec2() } ) local Distance = PointVec2:Get2DDistance( self.CargoObject:GetPointVec2() ) - self:T( Distance ) + --self:F( Distance ) if Distance <= NearRadius then + self:F( { PointVec2 = PointVec2, NearRadius = NearRadius, IsNear = true } ) return true end end + self:F( { PointVec2 = PointVec2, NearRadius = NearRadius, IsNear = false } ) return false end diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index 5f6b4e941..2ba33aa4b 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -23,7 +23,7 @@ do -- CARGO_UNIT --- Models CARGO in the form of units, which can be boarded, unboarded, loaded, unloaded. -- @type CARGO_UNIT - -- @extends #CARGO_REPRESENTABLE + -- @extends Cargo.Cargo#CARGO_REPRESENTABLE --- # CARGO\_UNIT class, extends @{#CARGO_REPRESENTABLE} -- @@ -82,42 +82,48 @@ do -- CARGO_UNIT if not self:IsDestroyed() then local CargoCarrier = self.CargoCarrier -- Wrapper.Controllable#CONTROLLABLE - - local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() - local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - - - local CargoRoutePointVec2 = CargoCarrierPointVec2:Translate( RouteDistance, CargoDeployHeading ) - - -- if there is no ToPointVec2 given, then use the CargoRoutePointVec2 - local FromDirectionVec3 = CargoCarrierPointVec2:GetDirectionVec3( ToPointVec2 or CargoRoutePointVec2 ) - local FromAngle = CargoCarrierPointVec2:GetAngleDegrees(FromDirectionVec3) - local FromPointVec2 = CargoCarrierPointVec2:Translate( DeployDistance, FromAngle ) - --local CargoDeployPointVec2 = CargoCarrierPointVec2:GetRandomCoordinateInRadius( 10, 5 ) - - ToPointVec2 = ToPointVec2 or CargoCarrierPointVec2:GetRandomCoordinateInRadius( NearRadius, DeployDistance ) - - -- Respawn the group... - if self.CargoObject then - self.CargoObject:ReSpawn( FromPointVec2:GetVec3(), CargoDeployHeading ) - self:F( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } ) - self.CargoCarrier = nil + if CargoCarrier:IsAlive() then - local Points = {} - - -- From - Points[#Points+1] = FromPointVec2:WaypointGround( Speed, "Vee" ) - - -- To - Points[#Points+1] = ToPointVec2:WaypointGround( Speed, "Vee" ) + local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() + local CargoCarrierHeading = self.CargoCarrier:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local TaskRoute = self.CargoObject:TaskRoute( Points ) - self.CargoObject:SetTask( TaskRoute, 1 ) - + + local CargoRoutePointVec2 = CargoCarrierPointVec2:Translate( RouteDistance, CargoDeployHeading ) - self:__UnBoarding( 1, ToPointVec2, NearRadius ) + + -- if there is no ToPointVec2 given, then use the CargoRoutePointVec2 + local FromDirectionVec3 = CargoCarrierPointVec2:GetDirectionVec3( ToPointVec2 or CargoRoutePointVec2 ) + local FromAngle = CargoCarrierPointVec2:GetAngleDegrees(FromDirectionVec3) + local FromPointVec2 = CargoCarrierPointVec2:Translate( DeployDistance, FromAngle ) + --local CargoDeployPointVec2 = CargoCarrierPointVec2:GetRandomCoordinateInRadius( 10, 5 ) + + ToPointVec2 = ToPointVec2 or CargoCarrierPointVec2:GetRandomCoordinateInRadius( NearRadius, DeployDistance ) + + -- Respawn the group... + if self.CargoObject then + self.CargoObject:ReSpawn( FromPointVec2:GetVec3(), CargoDeployHeading ) + self:F( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } ) + self.CargoCarrier = nil + + local Points = {} + + -- From + Points[#Points+1] = FromPointVec2:WaypointGround( Speed, "Vee" ) + + -- To + Points[#Points+1] = ToPointVec2:WaypointGround( Speed, "Vee" ) + + local TaskRoute = self.CargoObject:TaskRoute( Points ) + self.CargoObject:SetTask( TaskRoute, 1 ) + + + self:__UnBoarding( 1, ToPointVec2, NearRadius ) + end + else + -- the Carrier is dead. This cargo is dead too! + self:Destroyed() end end end diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 72317d2ba..8c05d8a14 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -603,6 +603,7 @@ function ZONE_RADIUS:Scan( ObjectCategories ) self.ScanData = {} self.ScanData.Coalitions = {} self.ScanData.Scenery = {} + self.ScanData.Units = {} local ZoneCoord = self:GetCoordinate() local ZoneRadius = self:GetRadius() @@ -625,6 +626,7 @@ function ZONE_RADIUS:Scan( ObjectCategories ) (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then local CoalitionDCSUnit = ZoneObject:getCoalition() self.ScanData.Coalitions[CoalitionDCSUnit] = true + self.ScanData.Units[ZoneObject] = ZoneObject self:F( { Name = ZoneObject:getName(), Coalition = CoalitionDCSUnit } ) end if ObjectCategory == Object.Category.SCENERY then @@ -643,6 +645,12 @@ function ZONE_RADIUS:Scan( ObjectCategories ) end +function ZONE_RADIUS:GetScannedUnits() + + return self.ScanData.Units +end + + function ZONE_RADIUS:CountScannedCoalitions() local Count = 0 diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 26062ea09..23fd5da1a 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -239,14 +239,17 @@ end -- Note that this destroy method also raises a destroy event at run-time. -- So all event listeners will catch the destroy event of this DCS Group. -- @param #GROUP self -function GROUP:Destroy() +-- @param #boolean GenerateEvent +function GROUP:Destroy( GenerateEvent ) self:F2( self.GroupName ) local DCSGroup = self:GetDCSObject() if DCSGroup then - for Index, UnitData in pairs( DCSGroup:getUnits() ) do - self:CreateEventCrash( timer.getTime(), UnitData ) + if not GenerateEvent then + for Index, UnitData in pairs( DCSGroup:getUnits() ) do + self:CreateEventCrash( timer.getTime(), UnitData ) + end end USERFLAG:New( self:GetName() ):Set( 100 ) DCSGroup:destroy() diff --git a/Moose Development/Moose/Wrapper/Identifiable.lua b/Moose Development/Moose/Wrapper/Identifiable.lua index baa83eedb..edd123665 100644 --- a/Moose Development/Moose/Wrapper/Identifiable.lua +++ b/Moose Development/Moose/Wrapper/Identifiable.lua @@ -229,6 +229,26 @@ function IDENTIFIABLE:GetDesc() return nil end +--- Check if the Object has the attribute. +-- @param #IDENTIFIABLE self +-- @param #string AttributeName The attribute name. +-- @return #boolean true if the attribute exists. +-- @return #nil The DCS Identifiable is not existing or alive. +function IDENTIFIABLE:HasAttribute( AttributeName ) + self:F2( self.IdentifiableName ) + + local DCSIdentifiable = self:GetDCSObject() + + if DCSIdentifiable then + local IdentifiableHasAttribute = DCSIdentifiable:hasAttribute( AttributeName ) + self:T2( IdentifiableHasAttribute ) + return IdentifiableHasAttribute + end + + self:F( self.ClassName .. " " .. self.IdentifiableName .. " not found!" ) + return nil +end + --- Gets the CallSign of the IDENTIFIABLE, which is a blank by default. -- @param #IDENTIFIABLE self -- @return #string The CallSign of the IDENTIFIABLE. diff --git a/Moose Development/Moose/Wrapper/Object.lua b/Moose Development/Moose/Wrapper/Object.lua index fc3c11126..29901fd4b 100644 --- a/Moose Development/Moose/Wrapper/Object.lua +++ b/Moose Development/Moose/Wrapper/Object.lua @@ -91,3 +91,5 @@ end + + From 7735120f25b18a54326c85a12d2928de81c8f014 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sat, 7 Apr 2018 14:21:34 +0200 Subject: [PATCH 034/420] Progress on AI_CARGO_TROOPS --- Moose Development/Moose/Cargo/CargoUnit.lua | 53 +++++++++++-------- Moose Development/Moose/Core/Set.lua | 13 +++++ .../Moose/Wrapper/Controllable.lua | 4 +- 3 files changed, 46 insertions(+), 24 deletions(-) diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index 2ba33aa4b..5ac185277 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -231,7 +231,11 @@ do -- CARGO_UNIT local NearRadius = NearRadius or 25 self.CargoInAir = self.CargoObject:InAir() - + + local Desc = self.CargoObject:GetDesc() + local MaxSpeed = Desc.speedMaxOffRoad + local TypeName = Desc.typeName + self:T( self.CargoInAir ) -- Only move the group to the carrier when the cargo is not in the air @@ -240,28 +244,33 @@ do -- CARGO_UNIT if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then self:Load( CargoCarrier, NearRadius, ... ) else - local Speed = 90 - local Angle = 180 - local Distance = 5 + if MaxSpeed and MaxSpeed == 0 or TypeName and TypeName == "Stinger comm" then + self:Load( CargoCarrier, NearRadius, ... ) + else + + local Speed = 90 + local Angle = 180 + local Distance = 5 + + NearRadius = NearRadius or 25 - NearRadius = NearRadius or 25 - - local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() - local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( Distance, CargoDeployHeading ) - - local Points = {} - - local PointStartVec2 = self.CargoObject:GetPointVec2() - - Points[#Points+1] = PointStartVec2:WaypointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed ) - - local TaskRoute = self.CargoObject:TaskRoute( Points ) - self.CargoObject:SetTask( TaskRoute, 2 ) - self:__Boarding( -1, CargoCarrier, NearRadius ) - self.RunCount = 0 + local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() + local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( Distance, CargoDeployHeading ) + + local Points = {} + + local PointStartVec2 = self.CargoObject:GetPointVec2() + + Points[#Points+1] = PointStartVec2:WaypointGround( Speed ) + Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed ) + + local TaskRoute = self.CargoObject:TaskRoute( Points ) + self.CargoObject:SetTask( TaskRoute, 2 ) + self:__Boarding( -1, CargoCarrier, NearRadius ) + self.RunCount = 0 + end end end diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 939973fd1..58c24bcce 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -4131,6 +4131,19 @@ function SET_CARGO:New() --R2.1 return self end + +--- (R2.1) Add CARGO to SET_CARGO. +-- @param Core.Set#SET_CARGO self +-- @param Cargo.Cargo#CARGO Cargo A single cargo. +-- @return self +function SET_CARGO:AddCargo( Cargo ) --R2.4 + + self:Add( Cargo:GetName(), Cargo ) + + return self +end + + --- (R2.1) Add CARGOs to SET_CARGO. -- @param Core.Set#SET_CARGO self -- @param #string AddCargoNames A single name or an array of CARGO names. diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 1847a16cd..f5b414e60 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1892,7 +1892,7 @@ end -- @param #CONTROLLABLE self -- @return #CONTROLLABLE function CONTROLLABLE:RouteStop() - self:F2() + self:F("RouteStop") local CommandStop = self:CommandStopRoute( true ) self:SetCommand( CommandStop ) @@ -1903,7 +1903,7 @@ end -- @param #CONTROLLABLE self -- @return #CONTROLLABLE function CONTROLLABLE:RouteResume() - self:F2() + self:F("RouteResume") local CommandResume = self:CommandStopRoute( false ) self:SetCommand( CommandResume ) From b1ecdc727c98ca17f758a7d0d58b1df91a291a32 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sat, 7 Apr 2018 19:00:18 +0200 Subject: [PATCH 035/420] Faster menu and fix for Disembark loaded engineers in other helicopter. --- Moose Development/Moose/Cargo/Cargo.lua | 8 ++++++++ Moose Development/Moose/Tasking/Task_CARGO.lua | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index eb44217a3..cacd5fa64 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -384,6 +384,14 @@ do -- CARGO return self:Is( "Loaded" ) end + --- Check if cargo is loaded. + -- @param #CARGO self + -- @param Wrapper.Unit#UNIT Carrier + -- @return #boolean true if loaded + function CARGO:IsLoadedInCarrier( Carrier ) + return self.CargoCarrier and self.CargoCarrier:GetName() == Carrier:GetName() + end + --- Check if cargo is unloaded. -- @param #CARGO self -- @return #boolean true if unloaded diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 101399783..933413ebc 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -306,7 +306,7 @@ do -- TASK_CARGO end - if Cargo:IsLoaded() then + if Cargo:IsLoaded() == true and Cargo:IsLoadedInCarrier( TaskUnit ) == true then if not TaskUnit:InAir() then if Cargo:CanUnboard() == true then MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Unboard cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuUnboardCargo, self, Cargo ):SetTime(MenuTime) @@ -333,7 +333,7 @@ do -- TASK_CARGO TaskUnit.Menu:Remove( MenuTime ) - self:__SelectAction( -5 ) + self:__SelectAction( -1 ) end From 303f5cb571fc3d6193402553efb699df49f39f54 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sat, 7 Apr 2018 23:36:09 +0200 Subject: [PATCH 036/420] RANGE v1.1.0 - Missiles are now tracked. - Statics can now be used for strafe pits. - Statics are automatically recognized. - More user functions to add bomb or strafe targets. - Bomb targets are allowed to move. - Bomb targets can automatically move randomly inside the range zone. - Improved trace output. - Documentation updated. --- Moose Development/Moose/Functional/Range.lua | 783 ++++++++++++++----- 1 file changed, 569 insertions(+), 214 deletions(-) diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index e3e3590cb..9748e89d6 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -16,12 +16,12 @@ -- -- ## Features -- --- * Bomb and rocket impact point from closest range target is measured and distance reported to the player. --- * Number of hits on strafing passes are counted. +-- * Impact points of bombs, rockets and missils are recorded and distance to closest range target is measured and reported to the player. +-- * Number of hits on strafing passes are counted and reported. Also the percentage of hits w.r.t fired shots is evaluated. -- * Results of all bombing and strafing runs are stored and top 10 results can be displayed. -- * Range targets can be marked by smoke. -- * Range can be illuminated by illumination bombs for night practices. --- * Rocket or bomb impact points can be marked by smoke. +-- * Bomb, rocket and missile impact points can be marked by smoke. -- * Direct hits on targets can trigger flares. -- * Smoke and flare colors can be adjusted for each player via radio menu. -- * Range information and weather report at the range can be reported via radio menu. @@ -55,8 +55,9 @@ -- @field #string ClassName Name of the Class. -- @field #boolean Debug If true, debug info is send as messages on the screen. -- @field #string rangename Name of the range. --- @field Core.Point#COORDINATE location Coordinate of the range. --- @field #number rangeradius Radius of range defining its total size for e.g. smoking bomb impact points and sending radio messages. Default 10 km. +-- @field Core.Point#COORDINATE location Coordinate of the range location. +-- @field #number rangeradius Radius of range defining its total size for e.g. smoking bomb impact points and sending radio messages. Default 5 km. +-- @field Core.Zone#ZONE rangezone MOOSE zone object of the range. For example, no bomb impacts are smoked if bombs fall outside of the range zone. -- @field #table strafeTargets Table of strafing targets. -- @field #table bombingTargets Table of targets to bomb. -- @field #number nbombtargets Number of bombing targets. @@ -79,14 +80,17 @@ -- @field #number scorebombdistance Distance from closest target up to which bomb hits are counted. Default 1000 m. -- @field #number TdelaySmoke Time delay in seconds between impact of bomb and starting the smoke. Default 3 seconds. -- @field #boolean eventmoose If true, events are handled by MOOSE. If false, events are handled directly by DCS eventhandler. Default true. +-- @field #boolean trackbombs If true (default), all bomb types are tracked and impact point to closest bombing target is evaluated. +-- @field #boolean trackrockets If true (default), all rocket types are tracked and impact point to closest bombing target is evaluated. +-- @field #boolean trackmissiles If true (default), all missile types are tracked and impact point to closest bombing target is evaluated. -- @extends Core.Base#BASE ---# RANGE class, extends @{Base#BASE} -- The RANGE class enables a mission designer to easily set up practice ranges in DCS. A new RANGE object can be created with the @{#RANGE.New}(rangename) contructor. -- The parameter "rangename" defindes the name of the range. It has to be unique since this is also the name displayed in the radio menu. -- --- Generally, a range consits of strafe pits and bombing targets. For strafe pits the number of hits for each pass is counted and tabulated. --- For bombing targets, the distance from the impact point of the bomb or rocket to the closest range target is measured and tabulated. +-- Generally, a range consists of strafe pits and bombing targets. For strafe pits the number of hits for each pass is counted and tabulated. +-- For bombing targets, the distance from the impact point of the bomb, rocket or missile to the closest range target is measured and tabulated. -- Each player can display his best results via a function in the radio menu or see the best best results from all players. -- -- When all targets have been defined in the script, the range is started by the @{#RANGE.Start}() command. @@ -101,31 +105,34 @@ -- ## Strafe Pits -- Each strafe pit can consist of multiple targets. Often one findes two or three strafe targets next to each other. -- --- A strafe pit can be added to the range by the @{#RANGE.AddStrafepit}(unitnames, boxlength, boxwidth, heading, inverseheading, goodpass, foulline) function. +-- A strafe pit can be added to the range by the @{#RANGE.AddStrafepit}(*targetnames, boxlength, boxwidth, heading, inverseheading, goodpass, foulline*) function. -- --- The first parameter defines the target. This has to be given as a lua table which contains the unit names of the targets as defined in the mission editor. +-- * The first parameter *targetnames* defines the target or targets. This has to be given as a lua table which contains the names of @{Unit} or @{Static} objects defined in the mission editor. +-- * In order to perform a valid pass on the strafe pit, the pilot has to begin his run from the correct direction. Therefore, an "approach box" is defined in front +-- of the strafe targets. The parameters *boxlength* and *boxwidth* define the size of the box while the parameter *heading* defines its direction. +-- If the parameter *heading* is passed as **nil**, the heading is automatically taken from the heading of the first target unit as defined in the ME. +-- The parameter *inverseheading* turns the heading around by 180 degrees. This is sometimes useful, since the default heading of strafe target units point in the +-- wrong/opposite direction. +-- * The parameter *goodpass* defines the number of hits a pilot has to achive during a run to be judged as a "good" pass. +-- * The last parameter *foulline* sets the distance from the pit targets to the foul line. Hit from closer than this line are not counted! -- --- In order to perform a valid pass on the strafe pit, the pilot has to begin his run from the correct direction. Therefore, an "approach box" is defined in front --- of the strafe targets. The parameters "boxlength" and "boxwidth" define the size of the box while the parameter "heading" defines its direction. --- If the parameter heading is passed as **nil**, the heading is automatically taken from the heading of the first target unit as defined in the ME. --- The parameter "inverseheading" turns the heading around by 180 degrees. This is sometimes useful, since the default heading of strafe target units point in the --- wrong/opposite direction. --- --- The parameter "goodpass" defines the number of hits a pilot has to achive during a run to be judges as a good pass. --- --- The last parameter "foulline" sets the distance from the pit targets to the foul line. Hit from closer than this line are not counted. +-- Another function to add a strafe pit is @{#RANGE.AddStrafePitGroup}(*group, boxlength, boxwidth, heading, inverseheading, goodpass, foulline*). Here, +-- the first parameter *group* is a MOOSE @{Group} object and **all** units in this group define **one** strafe pit. -- -- Finally, a valid approach has to be performed below a certain maximum altitude. The default is 914 meters (3000 ft) AGL. This is a parameter valid for all -- strafing pits of the range and can be adjusted by the @{#RANGE.SetMaxStrafeAlt}(maxalt) function. -- -- ## Bombing targets --- One ore multiple bombing targets can be added to the range by the @{#RANGE.AddBombingTargets}(unitnames goodhitrange,static) function. +-- One ore multiple bombing targets can be added to the range by the @{#RANGE.AddBombingTargets}(targetnames, goodhitrange, randommove) function. -- --- The first parameter "unitnames" has to be a lua table, which contains the names of the units as defined in the mission editor. --- --- The parameter "goodhitrange" specifies the radius around the target. If a bomb or rocket falls at a distance smaller than this number, the hit is considered to be "good". --- --- The final (optional) parameter "static" can be enabled (set to true) if static bomb targets are used rather than alive units. +-- * The first parameter *targetnames* has to be a lua table, which contains the names of @{Unit} and/or @{Static} objects defined in the mission editor. +-- Note that the @{Range} logic **automatically** determines, if a name belongs to a @{Unit} or @{Static} object now. +-- * The (optional) parameter *goodhitrange* specifies the radius around the target. If a bomb or rocket falls at a distance smaller than this number, the hit is considered to be "good". +-- * If final (optional) parameter "*randommove*" can be enabled to create moving targets. If this parameter is set to true, the units of this bombing target will randomly move within the range zone. +-- Note that there might be quirks since DCS units can get stuck in buildings etc. So it might be safer to manually define a route for the units in the mission editor if moving targets are desired. +-- +-- Another possibility to add bombing targets is the @{#RANGE.AddBombingTargetGroup}(*group, goodhitrange, randommove*) function. Here the parameter *group* is a MOOSE @{Group} object +-- and **all** units in this group are defined as bombing targets. -- -- ## Fine Tuning -- Many range parameters have good default values. However, the mission designer can change these settings easily with the supplied user functions: @@ -138,6 +145,9 @@ -- * @{#RANGE.SetStrafeTargetSmokeColor}() sets the color used to smoke strafe targets. -- * @{#RANGE.SetStrafePitSmokeColor}() sets the color used to smoke strafe pit approach boxes. -- * @{#RANGE.SetSmokeTimeDelay}() sets the time delay between smoking bomb/rocket impact points after impact. +-- * @{#RANGE.TrackBombsON}() or @{#RANGE.TrackBombsOFF}() can be used to enable/disable tracking and evaluating of all bomb types a player fires. +-- * @{#RANGE.TrackRocketsON}() or @{#RANGE.TrackRocketsOFF}() can be used to enable/disable tracking and evaluating of all rocket types a player fires. +-- * @{#RANGE.TrackMissilesON}() or @{#RANGE.TrackMissilesOFF}() can be used to enable/disable tracking and evaluating of all missile types a player fires. -- -- ## Radio Menu -- Each range gets a radio menu with various submenus where each player can adjust his individual settings or request information about the range or his scores. @@ -155,11 +165,11 @@ -- ## Examples -- -- ### Goldwater Range --- This example shows hot to set up the Barry M. Goldwater range. It consists of two strafe pits each has two targets plus three bombing targets. +-- This example shows hot to set up the [Barry M. Goldwater range](https://en.wikipedia.org/wiki/Barry_M._Goldwater_Air_Force_Range). +-- It consists of two strafe pits each has two targets plus three bombing targets. -- --- The [476th - Air Weapons Range Objects mod](http://www.476vfightergroup.com/downloads.php?do=file&id=287) is used in this example. --- --- -- Strafe pits. Each pit can consist of multiple targets. Here we have two pits and each of the pits has two targets. These are names of the corresponding units defined in the ME. +-- -- Strafe pits. Each pit can consist of multiple targets. Here we have two pits and each of the pits has two targets. +-- -- These are names of the corresponding units defined in the ME. -- local strafepit_left={"GWR Strafe Pit Left 1", "GWR Strafe Pit Left 2"} -- local strafepit_right={"GWR Strafe Pit Right 1", "GWR Strafe Pit Right 2"} -- @@ -167,16 +177,15 @@ -- local bombtargets={"GWR Bomb Target Circle Left", "GWR Bomb Target Circle Right", "GWR Bomb Target Hard"} -- -- -- Create a range object. --- local GoldwaterRange=RANGE:New("Goldwater Range") +-- GoldwaterRange=RANGE:New("Goldwater Range") -- --- -- Distance between foul line and strafe target. Note that this could also be done manually by simply measuring the distance between the target and the foul line in the ME. --- local strafe=UNIT:FindByName("GWR Strafe Pit Left 1") --- local foul=UNIT:FindByName("GWR Foul Line Left") --- local fouldist=strafe:GetCoordinate():Get2DDistance(foul:GetCoordinate()) +-- -- Distance between strafe target and foul line. You have to specify the names of the unit or static objects. +-- -- Note that this could also be done manually by simply measuring the distance between the target and the foul line in the ME. +-- GoldwaterRange:GetFoullineDistance("GWR Strafe Pit Left 1", "GWR Foul Line Left") -- -- -- Add strafe pits. Each pit (left and right) consists of two targets. -- GoldwaterRange:AddStrafePit(strafepit_left, 3000, 300, nil, true, 20, fouldist) --- GoldwaterRange:AddStrafePit(strafepit_right, 3000, 300, nil, true, 20, fouldist) +-- GoldwaterRange:AddStrafePit(strafepit_right, nil, nil, nil, true, nil, fouldist) -- -- -- Add bombing targets. A good hit is if the bomb falls less then 50 m from the target. -- GoldwaterRange:AddBombingTargets(bombtargets, 50) @@ -184,6 +193,24 @@ -- -- Start range. -- GoldwaterRange:Start() -- +-- The [476th - Air Weapons Range Objects mod](http://www.476vfightergroup.com/downloads.php?do=file&id=287) is (implicitly) used in this example. +-- +-- ## Debugging +-- +-- In case you have problems, it is always a good idea to have a look at your DCS log file. You find it in your "Saved Games" folder, so for example in +-- C:\Users\\Saved Games\DCS\Logs\dcs.log +-- All output concerning the RANGE class should have the string "RANGE" in the corresponding line. +-- +-- The verbosity of the output can be increased by adding the following lines to your script: +-- +-- BASE:TraceOnOff(true) +-- BASE:TraceLevel(1) +-- BASE:TraceClass("RANGE") +-- +-- To get even more output you can increase the trace level to 2 or even 3, c.f. @{BASE} for more details. +-- +-- The function @{#RANGE.DebugON}() can be used to send messages on screen. It also smokes all defined strafe and bombing targets, the strafe pit approach boxes and the range zone. +-- -- -- -- @field #RANGE @@ -192,7 +219,8 @@ RANGE={ Debug=false, rangename=nil, location=nil, - rangeradius=10000, + rangeradius=5000, + rangezone=nil, strafeTargets={}, bombingTargets={}, nbombtargets=0, @@ -215,8 +243,32 @@ RANGE={ scorebombdistance=1000, TdelaySmoke=3.0, eventmoose=true, + trackbombs=true, + trackrockets=true, + trackmissiles=true, } +--- Default range parameters. +-- @list Defaults +RANGE.Defaults={ + goodhitrange=25, + strafemaxalt=914, + dtBombtrack=0.005, + Tmsg=30, + ndisplayresult=10, + rangeradius=5000, + TdelaySmoke=3.0, + boxlength=3000, + boxwidth=300, + goodpass=20, + goodhitrange=25, + foulline=610, +} + +--- Global list of all defined range names. +-- @field #table Names +RANGE.Names={} + --- Main radio menu. -- @field #table MenuF10 RANGE.MenuF10={} @@ -227,10 +279,13 @@ RANGE.id="RANGE | " --- Range script version. -- @field #number version -RANGE.version="1.0.3" +RANGE.version="1.1.0" ---TODO list ---TODO: Add statics for strafe pits. +--TODO list: +--TODO: Add custom weapons, which can be specified by the user. +--TODO: Check if units are still alive. +--DONE: Add statics for strafe pits. +--DONE: Add missiles. --DONE: Convert env.info() to self:T() --DONE: Add user functions. --DONE: Rename private functions, i.e. start with _functionname. @@ -251,6 +306,7 @@ function RANGE:New(rangename) local self=BASE:Inherit(self, BASE:New()) -- #RANGE -- Get range name. + --TODO: make sure that the range name is not given twice. This would lead to problems in the F10 radio menu. self.rangename=rangename or "Practice Range" -- Debug info. @@ -274,9 +330,10 @@ function RANGE:Start() local _count=0 for _,_target in pairs(self.bombingTargets) do _count=_count+1 - --_target.name + + -- Get range location. if _location==nil then - _location=_target.point --Core.Point#COORDINATE + _location=_target.target:GetCoordinate() --Core.Point#COORDINATE end end self.nbombtargets=_count @@ -285,6 +342,7 @@ function RANGE:Start() _count=0 for _,_target in pairs(self.strafeTargets) do _count=_count+1 + for _,_unit in pairs(_target.targets) do if _location==nil then _location=_unit:GetCoordinate() @@ -293,13 +351,20 @@ function RANGE:Start() end self.nstrafetargets=_count - -- Location of the range. We simply take the first unit/target we find. - self.location=_location + -- Location of the range. We simply take the first unit/target we find if it was not explicitly specified by the user. + if self.location==nil then + self.location=_location + end if self.location==nil then local text=string.format("ERROR! No range location found. Number of strafe targets = %d. Number of bomb targets = %d.", self.rangename, self.nstrafetargets, self.nbombtargets) self:E(RANGE.id..text) - return nil + return + end + + -- Define a MOOSE zone of the range. + if self.rangezone==nil then + self.rangezone=ZONE_RADIUS:New(self.rangename, {x=self.location.x, y=self.location.z}, self.rangeradius) end -- Starting range. @@ -311,9 +376,6 @@ function RANGE:Start() if self.eventmoose then -- Events are handled my MOOSE. self:T(RANGE.id.."Events are handled by MOOSE.") - --self:HandleEvent(EVENTS.Birth, self._OnBirth) - --self:HandleEvent(EVENTS.Hit, self._OnHit) - --self:HandleEvent(EVENTS.Shot, self._OnShot) self:HandleEvent(EVENTS.Birth) self:HandleEvent(EVENTS.Hit) self:HandleEvent(EVENTS.Shot) @@ -323,6 +385,28 @@ function RANGE:Start() world.addEventHandler(self) end + -- Make bomb target move randomly within the range zone. + for _,_target in pairs(self.bombingTargets) do + + -- Check if it is a static object. + local _static=self:_CheckStatic(_target.target:GetName()) + + if _target.move and _static==false and _target.speed>1 then + local unit=_target.target --Wrapper.Unit#UNIT + _target.target:PatrolZones({self.rangezone}, _target.speed*0.75, "Off road") + end + + end + + -- Debug mode: smoke all targets and range zone. + if self.Debug then + self:_MarkTargetsOnMap() + self:_SmokeBombTargets() + self:_SmokeStrafeTargets() + self:_SmokeStrafeTargetBoxes() + self.rangezone:SmokeZone(SMOKECOLOR.White) + end + end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -332,35 +416,50 @@ end -- @param #RANGE self -- @param #number maxalt Maximum altitude AGL in meters. Default is 914 m= 3000 ft. function RANGE:SetMaxStrafeAlt(maxalt) - self.strafemaxalt=maxalt or 914 + self.strafemaxalt=maxalt or RANGE.Defaults.strafemaxalt end --- Set time interval for tracking bombs. A smaller time step increases accuracy but needs more CPU time. -- @param #RANGE self -- @param #number dt Time interval in seconds. Default is 0.005 s. function RANGE:SetBombtrackTimestep(dt) - self.dtBombtrack=dt or 0.005 + self.dtBombtrack=dt or RANGE.Defaults.dtBombtrack end --- Set time how long (most) messages are displayed. -- @param #RANGE self -- @param #number time Time in seconds. Default is 30 s. function RANGE:SetMessageTimeDuration(time) - self.Tmsg=time or 30 + self.Tmsg=time or RANGE.Defaults.Tmsg end --- Set max number of player results that are displayed. -- @param #RANGE self -- @param #number nmax Number of results. Default is 10. function RANGE:SetDisplayedMaxPlayerResults(nmax) - self.ndisplayresult=nmax or 10 + self.ndisplayresult=nmax or RANGE.Defaults.ndisplayresult end --- Set range radius. Defines the area in which e.g. bomb impacts are smoked. -- @param #RANGE self --- @param #number radius Radius in km. Default 10 km. +-- @param #number radius Radius in km. Default 5 km. function RANGE:SetRangeRadius(radius) - self.rangeradius=radius*1000 or 10000 + self.rangeradius=radius*1000 or RANGE.Defaults.rangeradius +end + +--- Set range location. If this is not done, one (random) unit position of the range is used to determine the center of the range. +-- @param #RANGE self +-- @param Core.Point#COORDINATE coordinate Coordinate of the center of the range. +function RANGE:SetRangeLocation(coordinate) + self.location=coordinate +end + +--- Set range zone. For example, no bomb impact points are smoked if a bomb falls outside of this zone. +-- If a zone is not explicitly specified, the range zone is determined by its location and radius. +-- @param #RANGE self +-- @param Core.Zone#ZONE zone MOOSE zone defining the range perimeters. +function RANGE:SetRangeLocation(zone) + self.rangezone=zone end --- Set smoke color for marking bomb targets. By default bomb targets are marked by red smoke. @@ -388,7 +487,7 @@ end -- @param #RANGE self -- @param #number delay Time delay in seconds. Default is 3 seconds. function RANGE:SetSmokeTimeDelay(delay) - self.TdelaySmoke=delay or 3.0 + self.TdelaySmoke=delay or RANGE.Defaults.TdelaySmoke end --- Enable debug modus. @@ -403,24 +502,60 @@ function RANGE:DebugOFF() self.Debug=false end +--- Enables tracking of all bomb types. Note that this is the default setting. +-- @param #RANGE self +function RANGE:TrackBombsON() + self.trackbombs=true +end + +--- Disables tracking of all bomb types. +-- @param #RANGE self +function RANGE:TrackBombsOFF() + self.trackbombs=false +end + +--- Enables tracking of all rocket types. Note that this is the default setting. +-- @param #RANGE self +function RANGE:TrackRocketsON() + self.trackrockets=true +end + +--- Disables tracking of all rocket types. +-- @param #RANGE self +function RANGE:TrackRocketsOFF() + self.trackrockets=false +end + +--- Enables tracking of all missile types. Note that this is the default setting. +-- @param #RANGE self +function RANGE:TrackMissilesON() + self.trackmissiles=true +end + +--- Disables tracking of all missile types. +-- @param #RANGE self +function RANGE:TrackMissilesOFF() + self.trackmissiles=false +end + --- Add new strafe pit. For a strafe pit, hits from guns are counted. One pit can consist of several units. -- Note, an approach is only valid, if the player enters via a zone in front of the pit, which defined by boxlength and boxheading. -- Furthermore, the player must not be too high and fly in the direction of the pit to make a valid target apporoach. -- @param #RANGE self --- @param #table unitnames Table of unit names defining the strafe targets. The first target in the list determines the approach zone (heading and box). +-- @param #table targetnames Table of unit or static names defining the strafe targets. The first target in the list determines the approach zone (heading and box). -- @param #number boxlength (Optional) Length of the approach box in meters. Default is 3000 m. -- @param #number boxwidth (Optional) Width of the approach box in meters. Default is 300 m. -- @param #number heading (Optional) Approach heading in Degrees. Default is heading of the unit as defined in the mission editor. -- @param #boolean inverseheading (Optional) Take inverse heading (heading --> heading - 180 Degrees). Default is false. -- @param #number goodpass (Optional) Number of hits for a "good" strafing pass. Default is 20. -- @param #number foulline (Optional) Foul line distance. Hits from closer than this distance are not counted. Default 610 m = 2000 ft. Set to 0 for no foul line. -function RANGE:AddStrafePit(unitnames, boxlength, boxwidth, heading, inverseheading, goodpass, foulline) - self:F({unitnames=unitnames, boxlength=boxlength, boxwidth=boxwidth, heading=heading, inverseheading=inverseheading, goodpass=goodpass, foulline=foulline}) +function RANGE:AddStrafePit(targetnames, boxlength, boxwidth, heading, inverseheading, goodpass, foulline) + self:F({targetnames=targetnames, boxlength=boxlength, boxwidth=boxwidth, heading=heading, inverseheading=inverseheading, goodpass=goodpass, foulline=foulline}) -- Create table if necessary. - if type(unitnames) ~= "table" then - unitnames={unitnames} + if type(targetnames) ~= "table" then + targetnames={targetnames} end -- Make targets @@ -428,11 +563,34 @@ function RANGE:AddStrafePit(unitnames, boxlength, boxwidth, heading, inversehead local center=nil --Wrapper.Unit#UNIT local ntargets=0 - for _i,_name in ipairs(unitnames) do + for _i,_name in ipairs(targetnames) do - self:T(RANGE.id..string.format("Adding strafe target #%d %s", _i, _name)) - local unit=UNIT:FindByName(_name) + -- Check if we have a static or unit object. + local _isstatic=self:_CheckStatic(_name) + + local unit=nil + if _isstatic==true then + -- Add static object. + self:T(RANGE.id..string.format("Adding STATIC object %s as strafe target #%d.", _name, _i)) + unit=STATIC:FindByName(_name, false) + + elseif _isstatic==false then + + -- Add unit object. + self:T(RANGE.id..string.format("Adding UNIT object %s as strafe target #%d.", _name, _i)) + unit=UNIT:FindByName(_name) + + else + + -- Neither unit nor static object with this name could be found. + local text=string.format("ERROR! Could not find ANY strafe target object with name %s.", _name) + self:E(RANGE.id..text) + MESSAGE:New(text, 10):ToAllIf(self.Debug) + + end + + -- Add object to targets. if unit then table.insert(_targets, unit) -- Define center as the first unit we find @@ -440,17 +598,21 @@ function RANGE:AddStrafePit(unitnames, boxlength, boxwidth, heading, inversehead center=unit end ntargets=ntargets+1 - else - local text=string.format("ERROR! Could not find strafe target with name %s.", _name) - self:E(RANGE.id..text) - MESSAGE:New(text, 10):ToAllIf(self.Debug) end end + + -- Check if at least one target could be found. + if ntargets==0 then + local text=string.format("ERROR! No strafe target could be found when calling RANGE:AddStrafePit() for range %s", self.rangename) + self:E(RANGE.id..text) + MESSAGE:New(text, 10):ToAllIf(self.Debug) + return + end -- Approach box dimensions. - local l=boxlength or 3000 - local w=(boxwidth or 300)/2 + local l=boxlength or RANGE.Defaults.boxlength + local w=(boxwidth or RANGE.Defaults.boxwidth)/2 -- Heading: either manually entered or automatically taken from unit heading. local heading=heading or center:GetHeading() @@ -469,10 +631,10 @@ function RANGE:AddStrafePit(unitnames, boxlength, boxwidth, heading, inversehead end -- Number of hits called a "good" pass. - local goodpass=goodpass or 20 + goodpass=goodpass or RANGE.Defaults.goodpass -- Foule line distance. - local foulline=foulline or 610 + foulline=foulline or RANGE.Defaults.foulline -- Coordinate of the range. local Ccenter=center:GetCoordinate() @@ -499,96 +661,195 @@ function RANGE:AddStrafePit(unitnames, boxlength, boxwidth, heading, inversehead --_polygon:BoundZone() -- Add zone to table. - table.insert(self.strafeTargets, {name=_name, polygon=_polygon, goodPass=goodpass, targets=_targets, foulline=foulline, smokepoints=p, heading=heading}) + table.insert(self.strafeTargets, {name=_name, polygon=_polygon, coordinate= Ccenter, goodPass=goodpass, targets=_targets, foulline=foulline, smokepoints=p, heading=heading}) -- Debug info - local text=string.format("Adding new strafe target %s with %d targets: heading = %03d, box_L = %.1f, box_W = %.1f, goodpass = %d, foul line = %.1f", _name, ntargets, heading, boxlength, boxwidth, goodpass, foulline) + local text=string.format("Adding new strafe target %s with %d targets: heading = %03d, box_L = %.1f, box_W = %.1f, goodpass = %d, foul line = %.1f", _name, ntargets, heading, l, w, goodpass, foulline) self:T(RANGE.id..text) MESSAGE:New(text, 5):ToAllIf(self.Debug) end ---- Add bombing target(s) to range. --- @param #RANGE self --- @param #table unitnames Table containing the unit names acting as bomb targets. --- @param #number goodhitrange (Optional) Max distance from target unit (in meters) which is considered as a good hit. Default is 25 m. --- @param #boolean static (Optional) Target is static. Default false. -function RANGE:AddBombingTargets(unitnames, goodhitrange, static) - self:F({unitnames=unitnames, goodhitrange=goodhitrange, static=static}) - -- Create a table if necessary. - if type(unitnames) ~= "table" then - unitnames={unitnames} - end - - if static == nil or static == false then - static=false - else - static=true - end - - -- Default range is 25 m. - goodhitrange=goodhitrange or 25 - - for _,name in pairs(unitnames) do - local _unit - local _static +--- Add all units of a group as one new strafe target pit. +-- For a strafe pit, hits from guns are counted. One pit can consist of several units. +-- Note, an approach is only valid, if the player enters via a zone in front of the pit, which defined by boxlength and boxheading. +-- Furthermore, the player must not be too high and fly in the direction of the pit to make a valid target apporoach. +-- @param #RANGE self +-- @param Wrapper.Group#GROUP group MOOSE group of unit names defining the strafe target pit. The first unit in the group determines the approach zone (heading and box). +-- @param #number boxlength (Optional) Length of the approach box in meters. Default is 3000 m. +-- @param #number boxwidth (Optional) Width of the approach box in meters. Default is 300 m. +-- @param #number heading (Optional) Approach heading in Degrees. Default is heading of the unit as defined in the mission editor. +-- @param #boolean inverseheading (Optional) Take inverse heading (heading --> heading - 180 Degrees). Default is false. +-- @param #number goodpass (Optional) Number of hits for a "good" strafing pass. Default is 20. +-- @param #number foulline (Optional) Foul line distance. Hits from closer than this distance are not counted. Default 610 m = 2000 ft. Set to 0 for no foul line. +function RANGE:AddStrafePitGroup(group, boxlength, boxwidth, heading, inverseheading, goodpass, foulline) + self:F({group=group, boxlength=boxlength, boxwidth=boxwidth, heading=heading, inverseheading=inverseheading, goodpass=goodpass, foulline=foulline}) + + if group and group:IsAlive() then - if static then - - -- Add static object. Workaround since cargo objects are not yet in database because DCS function does not add those. - local _DCSstatic=StaticObject.getByName(name) - if _DCSstatic and _DCSstatic:isExist() then - self:T(RANGE.id..string.format("Adding DCS static to database. Name = %s.", name)) - _DATABASE:AddStatic(name) - else - self:E(RANGE.id..string.format("ERROR! DCS static DOES NOT exist! Name = %s.", name)) - end - - -- Now we can find it... - _static=STATIC:FindByName(name) - if _static then - self:AddBombingTargetUnit(_static, goodhitrange) - self:T(RANGE.id..string.format("Adding static bombing target %s with hit range %d.", name, goodhitrange)) - else - self:E(RANGE.id..string.format("ERROR! Cound not find static bombing target %s.", name)) - end - - else + -- Get units of group. + local _units=group:GetUnits() - _unit=UNIT:FindByName(name) - if _unit then - self:AddBombingTargetUnit(_unit, goodhitrange) - self:T(RANGE.id..string.format("Adding bombing target %s with hit range %d.", name, goodhitrange)) - else - self:E(RANGE.id..string.format("ERROR! Could not find bombing target %s.", name)) + -- Make table of unit names. + local _names={} + for _,_unit in ipairs(_units) do + + local _unit=_unit --Wrapper.Unit#UNIT + + if _unit and _unit:IsAlive() then + local _name=_unit:GetName() + table.insert(_names,_name) end end + + -- Add strafe pit. + self:AddStrafePit(_names, boxlength, boxwidth, heading, inverseheading, goodpass, foulline) + end +end + +--- Add bombing target(s) to range. +-- @param #RANGE self +-- @param #table targetnames Table containing names of unit or static objects serving as bomb targets. +-- @param #number goodhitrange (Optional) Max distance from target unit (in meters) which is considered as a good hit. Default is 25 m. +-- @param #boolean randommove If true, unit will move randomly within the range. Default is false. +function RANGE:AddBombingTargets(targetnames, goodhitrange, randommove) + self:F({targetnames=targetnames, goodhitrange=goodhitrange, randommove=randommove}) + + -- Create a table if necessary. + if type(targetnames) ~= "table" then + targetnames={targetnames} + end + + -- Default range is 25 m. + goodhitrange=goodhitrange or RANGE.Defaults.goodhitrange + + for _,name in pairs(targetnames) do + + -- Check if we have a static or unit object. + local _isstatic=self:_CheckStatic(name) + + if _isstatic==true then + local _static=STATIC:FindByName(name) + self:T2(RANGE.id..string.format("Adding static bombing target %s with hit range %d.", name, goodhitrange, false)) + self:AddBombingTargetUnit(_static, goodhitrange) + elseif _isstatic==false then + local _unit=UNIT:FindByName(name) + self:T2(RANGE.id..string.format("Adding unit bombing target %s with hit range %d.", name, goodhitrange, randommove)) + self:AddBombingTargetUnit(_unit, goodhitrange) + else + self:E(RANGE.id..string.format("ERROR! Could not find bombing target %s.", name)) + end + end end ---- Add a unit as bombing target. +--- Add a unit or static object as bombing target. -- @param #RANGE self --- @param Wrapper.Unit#UNIT unit Unit of the strafe target. +-- @param Wrapper.Positionable#POSITIONABLE unit Positionable (unit or static) of the strafe target. -- @param #number goodhitrange Max distance from unit which is considered as a good hit. -function RANGE:AddBombingTargetUnit(unit, goodhitrange) - self:F({unit=unit, goodhitrange=goodhitrange}) +-- @param #boolean randommove If true, unit will move randomly within the range. Default is false. +function RANGE:AddBombingTargetUnit(unit, goodhitrange, randommove) + self:F({unit=unit, goodhitrange=goodhitrange, randommove=randommove}) - local coord=unit:GetCoordinate() + -- Get name of positionable. local name=unit:GetName() - -- Default range is 25 m. - goodhitrange=goodhitrange or 25 + -- Check if we have a static or unit object. + local _isstatic=self:_CheckStatic(name) - -- Create a zone around the unit. - local Vec2=coord:GetVec2() - local Rzone=ZONE_RADIUS:New(name, Vec2, goodhitrange) + -- Default range is 25 m. + goodhitrange=goodhitrange or RANGE.Defaults.goodhitrange + + -- Set randommove to false if it was not specified. + if randommove==nil or _isstatic==true then + randommove=false + end + + -- Debug or error output. + if _isstatic==true then + self:T(RANGE.id..string.format("Adding STATIC bombing target %s with good hit range %d. Random move = %s.", name, goodhitrange, tostring(randommove))) + elseif _isstatic==false then + self:T(RANGE.id..string.format("Adding UNIT bombing target %s with good hit range %d. Random move = %s.", name, goodhitrange, tostring(randommove))) + else + self:E(RANGE.id..string.format("ERROR! No bombing target with name %s could be found. Carefully check all UNIT and STATIC names defined in the mission editor!", name)) + end + + -- Get max speed of unit in km/h. + local speed=0 + if _isstatic==false then + speed=self:_GetSpeed(unit) + end -- Insert target to table. - table.insert(self.bombingTargets, {name=name, point=coord, zone=Rzone, target=unit, goodhitrange=goodhitrange}) + table.insert(self.bombingTargets, {name=name, target=unit, goodhitrange=goodhitrange, move=randommove, speed=speed}) end +--- Add all units of a group as bombing targets. +-- @param #RANGE self +-- @param Wrapper.Group#GROUP group Group of bombing targets. +-- @param #number goodhitrange Max distance from unit which is considered as a good hit. +-- @param #boolean randommove If true, unit will move randomly within the range. Default is false. +function RANGE:AddBombingTargetGroup(group, goodhitrange, randommove) + self:F({group=group, goodhitrange=goodhitrange, randommove=randommove}) + + if group then + + local _units=group:GetUnits() + + for _,_unit in pairs(_units) do + if _unit and _unit:IsAlive() then + self:AddBombingTargetUnit(_unit, goodhitrange, randommove) + end + end + end + +end + +--- Measures the foule line distance between two unit or static objects. +-- @param #RANGE self +-- @param #string namepit Name of the strafe pit target object. +-- @param #string namefoulline Name of the fould line distance marker object. +-- @return #number Foul line distance in meters. +function RANGE:GetFoullineDistance(namepit, namefoulline) + self:F({namepit=namepit, namefoulline=namefoulline}) + + -- Check if we have units or statics. + local _staticpit=self:_CheckStatic(namepit) + local _staticfoul=self:_CheckStatic(namefoulline) + + -- Get the unit or static pit object. + local pit=nil + if _staticpit==true then + pit=STATIC:FindByName(namepit, false) + elseif _staticpit==false then + pit=UNIT:FindByName(namepit) + else + self:E(RANGE.id..string.format("ERROR! Pit object %s could not be found in GetFoullineDistance function. Check the name in the ME.", namepit)) + end + + -- Get the unit or static foul line object. + local foul=nil + if _staticfoul==true then + foul=STATIC:FindByName(namefoulline, false) + elseif _staticfoul==false then + foul=UNIT:FindByName(namefoulline) + else + self:E(RANGE.id..string.format("ERROR! Foul line object %s could not be found in GetFoullineDistance function. Check the name in the ME.", namefoulline)) + end + + -- Get the distance between the two objects. + local fouldist=0 + if pit~=nil and foul~=nil then + fouldist=pit:GetCoordinate():Get2DDistance(foul:GetCoordinate()) + else + self:E(RANGE.id..string.format("ERROR! Foul line distance could not be determined. Check pit object name %s and foul line object name %s in the ME.", namepit, namefoulline)) + end + + self:T(RANGE.id..string.format("Foul line distance = %.1f m.", fouldist)) + return fouldist +end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Event Handling @@ -600,11 +861,11 @@ function RANGE:onEvent(Event) self:F3(Event) if Event == nil or Event.initiator == nil then - self:T2("Skipping onEvent. Event or Event.initiator unknown.") + self:T3("Skipping onEvent. Event or Event.initiator unknown.") return true end if Unit.getByName(Event.initiator:getName()) == nil then - self:T2("Skipping onEvent. Initiator unit name unknown.") + self:T3("Skipping onEvent. Initiator unit name unknown.") return true end @@ -646,19 +907,16 @@ function RANGE:onEvent(Event) -- Call event Birth function. if Event.id==world.event.S_EVENT_BIRTH and _playername then self:OnEventBirth(EventData) - --self:_OnBirth(EventData) end -- Call event Shot function. if Event.id==world.event.S_EVENT_SHOT and _playername and Event.weapon then self:OnEventShot(EventData) - --self:_OnShot(EventData) end -- Call event Hit function. if Event.id==world.event.S_EVENT_HIT and _playername and DCStgtunit then self:OnEventHit(EventData) - --self:_OnHit(EventData) end end @@ -668,7 +926,6 @@ end -- @param #RANGE self -- @param Core.Event#EVENTDATA EventData function RANGE:OnEventBirth(EventData) ---function RANGE:_OnBirth(EventData) self:F({eventbirth = EventData}) local _unitName=EventData.IniUnitName @@ -690,6 +947,8 @@ function RANGE:OnEventBirth(EventData) self:T(RANGE.id..text) MESSAGE:New(text, 5):ToAllIf(self.Debug) + self:_GetAmmo(_unitName) + -- Reset current strafe status. self.strafeStatus[_uid] = nil @@ -717,7 +976,6 @@ end -- @param #RANGE self -- @param Core.Event#EVENTDATA EventData function RANGE:OnEventHit(EventData) ---function RANGE:_OnHit(EventData) self:F({eventhit = EventData}) -- Debug info. @@ -733,7 +991,7 @@ function RANGE:OnEventHit(EventData) end -- Unit ID - local _unitID = _unit:GetID() + local _unitID = _unit:GetID() -- Target local target = EventData.TgtUnit @@ -743,7 +1001,7 @@ function RANGE:OnEventHit(EventData) local _currentTarget = self.strafeStatus[_unitID] -- Player has rolled in on a strafing target. - if _currentTarget then + if _currentTarget and target:IsAlive() then local playerPos = _unit:GetCoordinate() local targetPos = target:GetCoordinate() @@ -752,7 +1010,7 @@ function RANGE:OnEventHit(EventData) for _,_target in pairs(_currentTarget.zone.targets) do -- Check the the target is the same that was actually hit. - if _target:GetName() == targetname then + if _target and _target:IsAlive() and _target:GetName() == targetname then -- Get distance between player and target. local dist=playerPos:Get2DDistance(targetPos) @@ -768,7 +1026,7 @@ function RANGE:OnEventHit(EventData) else -- Too close to the target. if _currentTarget.pastfoulline==false and _unit and _playername then - local _d=_currentTarget.zone.foulline + local _d=_currentTarget.zone.foulline local text=string.format("%s, Invalid hit!\nYou already passed foul line distance of %d m for target %s.", self:_myname(_unitName), _d, targetname) self:_DisplayMessageToGroup(_unit, text, 10) self:T2(RANGE.id..text) @@ -781,15 +1039,17 @@ function RANGE:OnEventHit(EventData) end -- Bombing Targets - for _,_target in pairs(self.bombingTargets) do + for _,_bombtarget in pairs(self.bombingTargets) do + + local _target=_bombtarget.target --Wrapper.Positionable#POSITIONABLE -- Check if one of the bomb targets was hit. - if _target.name == targetname then + if _target and _target:IsAlive() and _bombtarget.name == targetname then if _unit and _playername then - local playerPos = _unit:GetCoordinate() - local targetPos = target:GetCoordinate() + -- Position of target. + local targetPos = _target:GetCoordinate() -- Message to player. --local text=string.format("%s, direct hit on target %s.", self:_myname(_unitName), targetname) @@ -809,7 +1069,6 @@ end -- @param #RANGE self -- @param Core.Event#EVENTDATA EventData function RANGE:OnEventShot(EventData) ---function RANGE:_OnShot(EventData) self:F({eventshot = EventData}) -- Weapon data. @@ -818,13 +1077,23 @@ function RANGE:OnEventShot(EventData) local _weaponName = _weaponStrArray[#_weaponStrArray] -- Debug info. - self:T3(RANGE.id.."EVENT SHOT: Ini unit = "..EventData.IniUnitName) - self:T3(RANGE.id.."EVENT SHOT: Ini group = "..EventData.IniGroupName) - self:T3(RANGE.id.."EVENT SHOT: Weapon type = ".._weapon) - self:T3(RANGE.id.."EVENT SHOT: Weapon name = ".._weaponName) + self:T(RANGE.id.."EVENT SHOT: Ini unit = "..EventData.IniUnitName) + self:T(RANGE.id.."EVENT SHOT: Ini group = "..EventData.IniGroupName) + self:T(RANGE.id.."EVENT SHOT: Weapon type = ".._weapon) + self:T(RANGE.id.."EVENT SHOT: Weapon name = ".._weaponName) - -- Monitor only bombs and rockets. - if (string.match(_weapon, "weapons.bombs") or string.match(_weapon, "weapons.nurs")) then + -- Special cases: + local _viggen=string.match(_weapon, "ROBOT") or string.match(_weapon, "RB75") or string.match(_weapon, "BK90") or string.match(_weapon, "RB15") or string.match(_weapon, "RB04") + + -- Tracking conditions for bombs, rockets and missiles. + local _bombs=string.match(_weapon, "weapons.bombs") + local _rockets=string.match(_weapon, "weapons.nurs") + local _missiles=string.match(_weapon, "weapons.missiles") or _viggen + + -- Check if any condition applies here. + local _track = (_bombs and self.trackbombs) or (_rockets and self.trackrockets) or (_missiles and self.trackmissiles) + + if _track then -- Weapon local _ordnance = EventData.weapon @@ -887,11 +1156,15 @@ function RANGE:OnEventShot(EventData) -- Loop over defined bombing targets. for _,_bombtarget in pairs(self.bombingTargets) do - -- Distance between bomb and target. - local _temp = impactcoord:Get2DDistance(_bombtarget.point) - - -- Find closest target to last known position of the bomb. - if _distance == nil or _temp < _distance then + local _target=_bombtarget.target --Wrapper.Positionable#POSITIONABLE + + if _target and _target:IsAlive() then + + -- Distance between bomb and target. + local _temp = impactcoord:Get2DDistance(_target:GetCoordinate()) + + -- Find closest target to last known position of the bomb. + if _distance == nil or _temp < _distance then _distance = _temp _closetTarget = _bombtarget if _distance <= 0.5*_bombtarget.goodhitrange then @@ -903,6 +1176,8 @@ function RANGE:OnEventShot(EventData) else _hitquality = "POOR" end + + end end end @@ -1335,7 +1610,7 @@ end -- @param #RANGE self -- @param #string _unitName Name of player unit. function RANGE:_CheckInZone(_unitName) - self:F(_unitName) + self:F2(_unitName) -- Get player unit and name. local _unit, _playername = self:_GetPlayerUnitAndName(_unitName) @@ -1364,7 +1639,7 @@ function RANGE:_CheckInZone(_unitName) -- Debug output local text=string.format("Checking stil in zone. Unit = %s, player = %s in zone = %s. alt = %d, delta heading = %d", _unitName, _playername, tostring(unitinzone), unitalt, deltaheading) - self:T(RANGE.id..text) + self:T2(RANGE.id..text) -- Check if player is in strafe zone and below max alt. if unitinzone then @@ -1407,13 +1682,13 @@ function RANGE:_CheckInZone(_unitName) _result.text = "POOR PASS" end + -- Calculate accuracy of run. Number of hits wrt number of rounds fired. local shots=_result.ammo-_ammo local accur=0 if shots>0 then accur=_result.hits/shots*100 end - - + -- Message text. local _text=string.format("%s, %s with %d hits on target %s.", self:_myname(_unitName), _result.text, _result.hits, _result.zone.name) if shots and accur then @@ -1455,7 +1730,7 @@ function RANGE:_CheckInZone(_unitName) -- Debug info. local text=string.format("Checking zone %s. Unit = %s, player = %s in zone = %s. alt = %d, delta heading = %d", _targetZone.name, _unitName, _playername, tostring(unitinzone), unitalt, deltaheading) - self:T(RANGE.id..text) + self:T2(RANGE.id..text) -- Player is inside zone. if unitinzone then @@ -1509,7 +1784,7 @@ function RANGE:_AddF10Commands(_unitName) -- Enable switch so we don't do this twice. self.MenuAddedTo[_gid] = true - -- Main F10 menu: F10/On the Range + -- Main F10 menu: F10/On the Range// if RANGE.MenuF10[_gid] == nil then RANGE.MenuF10[_gid]=missionCommands.addSubMenuForGroup(_gid, "On the Range") end @@ -1517,40 +1792,39 @@ function RANGE:_AddF10Commands(_unitName) local _statsPath = missionCommands.addSubMenuForGroup(_gid, "Statistics", _rangePath) local _markPath = missionCommands.addSubMenuForGroup(_gid, "Mark Targets", _rangePath) local _settingsPath = missionCommands.addSubMenuForGroup(_gid, "My Settings", _rangePath) - -- F10/On the Range/My Settings/ + -- F10/On the Range//My Settings/ local _mysmokePath = missionCommands.addSubMenuForGroup(_gid, "Smoke Color", _settingsPath) local _myflarePath = missionCommands.addSubMenuForGroup(_gid, "Flare Color", _settingsPath) - --TODO: Convert to MOOSE menu. - -- F10/On the Range/Mark Targets/ + -- F10/On the Range//Mark Targets/ missionCommands.addCommandForGroup(_gid, "Mark On Map", _markPath, self._MarkTargetsOnMap, self, _unitName) missionCommands.addCommandForGroup(_gid, "Illuminate Range", _markPath, self._IlluminateBombTargets, self, _unitName) missionCommands.addCommandForGroup(_gid, "Smoke Strafe Pits", _markPath, self._SmokeStrafeTargetBoxes, self, _unitName) missionCommands.addCommandForGroup(_gid, "Smoke Strafe Tgts", _markPath, self._SmokeStrafeTargets, self, _unitName) missionCommands.addCommandForGroup(_gid, "Smoke Bomb Tgts", _markPath, self._SmokeBombTargets, self, _unitName) - -- F10/On the Range/Stats/ + -- F10/On the Range//Stats/ missionCommands.addCommandForGroup(_gid, "All Strafe Results", _statsPath, self._DisplayStrafePitResults, self, _unitName) missionCommands.addCommandForGroup(_gid, "All Bombing Results", _statsPath, self._DisplayBombingResults, self, _unitName) missionCommands.addCommandForGroup(_gid, "My Strafe Results", _statsPath, self._DisplayMyStrafePitResults, self, _unitName) missionCommands.addCommandForGroup(_gid, "My Bomb Results", _statsPath, self._DisplayMyBombingResults, self, _unitName) missionCommands.addCommandForGroup(_gid, "Reset All Stats", _statsPath, self._ResetRangeStats, self, _unitName) - -- F10/On the Range/My Settings/Smoke Color/ + -- F10/On the Range//My Settings/Smoke Color/ missionCommands.addCommandForGroup(_gid, "Blue Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Blue) missionCommands.addCommandForGroup(_gid, "Green Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Green) missionCommands.addCommandForGroup(_gid, "Orange Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Orange) missionCommands.addCommandForGroup(_gid, "Red Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.Red) missionCommands.addCommandForGroup(_gid, "White Smoke", _mysmokePath, self._playersmokecolor, self, _unitName, SMOKECOLOR.White) - -- F10/On the Range/My Settings/Flare Color/ + -- F10/On the Range//My Settings/Flare Color/ missionCommands.addCommandForGroup(_gid, "Green Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Green) missionCommands.addCommandForGroup(_gid, "Red Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Red) missionCommands.addCommandForGroup(_gid, "White Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.White) missionCommands.addCommandForGroup(_gid, "Yellow Flares", _myflarePath, self._playerflarecolor, self, _unitName, FLARECOLOR.Yellow) - -- F10/On the Range/My Settings/ + -- F10/On the Range//My Settings/ missionCommands.addCommandForGroup(_gid, "Smoke Delay On/Off", _settingsPath, self._SmokeBombDelayOnOff, self, _unitName) missionCommands.addCommandForGroup(_gid, "Smoke Impact On/Off", _settingsPath, self._SmokeBombImpactOnOff, self, _unitName) missionCommands.addCommandForGroup(_gid, "Flare Hits On/Off", _settingsPath, self._FlareDirectHitsOnOff, self, _unitName) - -- F10/On the Range/ + -- F10/On the Range// missionCommands.addCommandForGroup(_gid, "Range Information", _rangePath, self._DisplayRangeInfo, self, _unitName) missionCommands.addCommandForGroup(_gid, "Weather Report", _rangePath, self._DisplayRangeWeather, self, _unitName) end @@ -1571,7 +1845,7 @@ end -- @param #string unitname Name of the player unit. -- @return Number of shells left function RANGE:_GetAmmo(unitname) - self:F(unitname) + self:F2(unitname) -- Init counter. local ammo=0 @@ -1603,7 +1877,11 @@ function RANGE:_GetAmmo(unitname) local text=string.format("Player %s has %d rounds ammo of type %s", playername, Nammo, Tammo) self:T(RANGE.id..text) - MESSAGE:New(text, 10):ToAllIf(self.Debug) + MESSAGE:New(text, 10):ToAllIf(self.Debug) + else + local text=string.format("Player %s has %d ammo of type %s", playername, Nammo, Tammo) + self:T(RANGE.id..text) + MESSAGE:New(text, 10):ToAllIf(self.Debug) end end end @@ -1619,31 +1897,45 @@ function RANGE:_MarkTargetsOnMap(_unitName) self:F(_unitName) -- Get group. - local group=UNIT:FindByName(_unitName):GetGroup() - - if group then + local group=nil + if _unitName then + group=UNIT:FindByName(_unitName):GetGroup() + end - -- Mark bomb targets. - for _,_target in pairs(self.bombingTargets) do - local coord=_target.point --Core.Point#COORDINATE - coord:MarkToGroup("Bomb target ".._target.name, group) - end - - -- Mark strafe targets. - for _,_strafepit in pairs(self.strafeTargets) do - for _,_target in pairs(_strafepit.targets) do - local coord=_target:GetCoordinate() --Core.Point#COORDINATE - coord:MarkToGroup("Strafe target ".._target:GetName(), group) + -- Mark bomb targets. + for _,_bombtarget in pairs(self.bombingTargets) do + local _target=_bombtarget.target --Wrapper.Positionable#POSITIONABLE + if _target and _target:IsAlive() then + local coord=_target:GetCoordinate() --Core.Point#COORDINATE + if group then + coord:MarkToGroup("Bomb target ".._bombtarget.name, group) + else + coord:MarkToAll("Bomb target ".._bombtarget.name) end end - - if _unitName then - local _unit, _playername = self:_GetPlayerUnitAndName(_unitName) - local text=string.format("%s, %s, range targets are now marked on F10 map.", self.rangename, _playername) - self:_DisplayMessageToGroup(_unit, text, 5) - end - end + + -- Mark strafe targets. + for _,_strafepit in pairs(self.strafeTargets) do + for _,_target in pairs(_strafepit.targets) do + local _target=_target --Wrapper.Positionable#POSITIONABLE + if _target and _target:IsAlive() then + local coord=_target:GetCoordinate() --Core.Point#COORDINATE + if group then + coord:MarkToGroup("Strafe target ".._target:GetName(), group) + else + coord:MarkToAll("Strafe target ".._target:GetName()) + end + end + end + end + + if _unitName then + local _unit, _playername = self:_GetPlayerUnitAndName(_unitName) + local text=string.format("%s, %s, range targets are now marked on F10 map.", self.rangename, _playername) + self:_DisplayMessageToGroup(_unit, text, 5) + end + end --- Illuminate targets. Fires illumination bombs at one random bomb and one random strafe target at a random altitude between 400 and 800 m. @@ -1655,9 +1947,12 @@ function RANGE:_IlluminateBombTargets(_unitName) -- All bombing target coordinates. local bomb={} - for _,_target in pairs(self.bombingTargets) do - local coord=_target.point --Core.Point#COORDINATE - table.insert(bomb, coord) + for _,_bombtarget in pairs(self.bombingTargets) do + local _target=_bombtarget.target --Wrapper.Positionable#POSITIONABLE + if _target and _target:IsAlive() then + local coord=_target:GetCoordinate() --Core.Point#COORDINATE + table.insert(bomb, coord) + end end if #bomb>0 then @@ -1671,8 +1966,11 @@ function RANGE:_IlluminateBombTargets(_unitName) for _,_strafepit in pairs(self.strafeTargets) do for _,_target in pairs(_strafepit.targets) do - local coord=_target:GetCoordinate() --Core.Point#COORDINATE - table.insert(strafe, coord) + local _target=_target --Wrapper.Positionable#POSITIONABLE + if _target and _target:IsAlive() then + local coord=_target:GetCoordinate() --Core.Point#COORDINATE + table.insert(strafe, coord) + end end end @@ -1803,9 +2101,12 @@ end function RANGE:_SmokeBombTargets(unitname) self:F(unitname) - for _,_target in pairs(self.bombingTargets) do - local coord = _target.point --Core.Point#COORDINATE - coord:Smoke(self.BombSmokeColor) + for _,_bombtarget in pairs(self.bombingTargets) do + local _target=_bombtarget.target --Wrapper.Positionable#POSITIONABLE + if _target and _target:IsAlive() then + local coord = _target:GetCoordinate() --Core.Point#COORDINATE + coord:Smoke(self.BombSmokeColor) + end end if unitname then @@ -1823,10 +2124,7 @@ function RANGE:_SmokeStrafeTargets(unitname) self:F(unitname) for _,_target in pairs(self.strafeTargets) do - for _,_unit in pairs(_target.targets) do - local coord = _unit:GetCoordinate() --Core.Point#COORDINATE - coord:Smoke(self.StrafeSmokeColor) - end + _target.coordinate:Smoke(self.StrafeSmokeColor) end if unitname then @@ -1939,6 +2237,63 @@ function RANGE:_flarecolor2text(color) return txt end +--- Checks if a static object with a certain name exists. It also added it to the MOOSE data base, if it is not already in there. +-- @param #RANGE self +-- @param #string name Name of the potential static object. +-- @return #boolean Returns true if a static with this name exists. Retruns false if a unit with this name exists. Returns nil if neither unit or static exist. +function RANGE:_CheckStatic(name) + self:F2(name) + + -- Get DCS static object. + local _DCSstatic=StaticObject.getByName(name) + + if _DCSstatic and _DCSstatic:isExist() then + + --Static does exist at least in DCS. Check if it also in the MOOSE DB. + local _MOOSEstatic=STATIC:FindByName(name, false) + + -- If static is not yet in MOOSE DB, we add it. Can happen for cargo statics! + if not _MOOSEstatic then + self:T(RANGE.id..string.format("Adding DCS static to MOOSE database. Name = %s.", name)) + _DATABASE:AddStatic(name) + end + + return true + else + self:T3(RANGE.id..string.format("No static object with name %s exists.", name)) + end + + -- Check if a unit has this name. + if UNIT:FindByName(name) then + return false + else + self:T3(RANGE.id..string.format("No unit object with name %s exists.", name)) + end + + -- If not unit or static exist, we return nil. + return nil +end + +--- Get max speed of controllable. +-- @param #RANGE self +-- @param Wrapper.Controllable#CONTROLLABLE controllable +-- @return Maximum speed in km/h. +function RANGE:_GetSpeed(controllable) + self:F2(controllable) + + -- Get DCS descriptors + local desc=controllable:GetDesc() + + -- Get speed + local speed=0 + if desc then + speed=desc.speedMax*3.6 + self:T({speed=speed}) + end + + return speed +end + --- Returns the unit of a player and the player name. If the unit does not belong to a player, nil is returned. -- @param #RANGE self -- @param #string _unitName Name of the player unit. @@ -1946,7 +2301,7 @@ end -- @return #string Name of the player. -- @return nil If player does not exist. function RANGE:_GetPlayerUnitAndName(_unitName) - self:F(_unitName) + self:F2(_unitName) if _unitName ~= nil then @@ -1958,7 +2313,7 @@ function RANGE:_GetPlayerUnitAndName(_unitName) local playername=DCSunit:getPlayerName() local unit=UNIT:Find(DCSunit) - self:T({DCSunit=DCSunit, unit=unit, playername=playername}) + self:T2({DCSunit=DCSunit, unit=unit, playername=playername}) if DCSunit and unit and playername then return unit, playername end @@ -1975,7 +2330,7 @@ end -- @param #RANGE self -- @param #string unitname Name of the player unit. function RANGE:_myname(unitname) - self:F(unitname) + self:F2(unitname) local unit=UNIT:FindByName(unitname) local pname=unit:GetPlayerName() @@ -1984,13 +2339,13 @@ function RANGE:_myname(unitname) return string.format("%s (%s)", csign, pname) end ---- http://stackoverflow.com/questions/1426954/split-string-in-lua +--- Split string. Cf http://stackoverflow.com/questions/1426954/split-string-in-lua -- @param #RANGE self -- @param #string str Sting to split. -- @param #string sep Speparator for split. -- @return #table Split text. function RANGE:_split(str, sep) - self:F({str=str, sep=sep}) + self:F2({str=str, sep=sep}) local result = {} local regex = ("([^%s]+)"):format(sep) From af23aa3b794a364cbb7d91d7bc99bfcdf040ce5a Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sun, 8 Apr 2018 11:01:46 +0200 Subject: [PATCH 037/420] -- Lots of fixes done. Especially on the - Messaging - Menu system - Crashing DCS - Routing --- Moose Development/Moose/Actions/Act_Route.lua | 17 ++-- Moose Development/Moose/Cargo/Cargo.lua | 56 +++++++++++-- Moose Development/Moose/Cargo/CargoCrate.lua | 2 +- Moose Development/Moose/Cargo/CargoGroup.lua | 1 + .../Moose/Cargo/CargoSlingload.lua | 6 ++ Moose Development/Moose/Core/Event.lua | 6 ++ Moose Development/Moose/Core/Fsm.lua | 4 + .../Moose/Tasking/Task_CARGO.lua | 80 +++++++++++-------- Moose Development/Moose/Wrapper/Unit.lua | 3 + 9 files changed, 131 insertions(+), 44 deletions(-) diff --git a/Moose Development/Moose/Actions/Act_Route.lua b/Moose Development/Moose/Actions/Act_Route.lua index e50c3ffcc..cbf089140 100644 --- a/Moose Development/Moose/Actions/Act_Route.lua +++ b/Moose Development/Moose/Actions/Act_Route.lua @@ -123,16 +123,21 @@ do -- ACT_ROUTE --- Set a Cancel Menu item. -- @param #ACT_ROUTE self -- @return #ACT_ROUTE - function ACT_ROUTE:SetMenuCancel( MenuGroup, MenuText, ParentMenu, MenuTime ) + function ACT_ROUTE:SetMenuCancel( MenuGroup, MenuText, ParentMenu ) - MENU_GROUP_COMMAND:New( + local MenuTime = timer.getTime() + 1 + + self.CancelMenuGroupCommand = MENU_GROUP_COMMAND:New( MenuGroup, MenuText, ParentMenu, self.MenuCancel, self - ):SetTime(MenuTime) - + ):SetTime( MenuTime ) + + ParentMenu:SetTime( MenuTime ) + ParentMenu:Remove( MenuTime ) + return self end @@ -206,7 +211,9 @@ do -- ACT_ROUTE function ACT_ROUTE:MenuCancel() - self:Cancel() + self:F("Cancelled") + self.CancelMenuGroupCommand:Remove() + self:__Cancel( 1 ) end --- Task Events diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index cacd5fa64..09fac4fc3 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -258,7 +258,6 @@ do -- CARGO self:AddTransition( "*", "Destroyed", "Destroyed" ) self:AddTransition( "*", "Respawn", "UnLoaded" ) - self.Type = Type self.Name = Name self.Weight = Weight or 0 @@ -268,6 +267,9 @@ do -- CARGO self.Slingloadable = false self.Moveable = false self.Containable = false + self.LoadAction = "" + + self.CargoLimit = 0 self.LoadRadius = LoadRadius or 500 self.NearRadius = NearRadius or 25 @@ -293,6 +295,12 @@ do -- CARGO return CargoFound end + --- Check if the cargo can be Slingloaded. + -- @param #CARGO self + function CARGO:CanSlingload() + return false + end + --- Check if the cargo can be Boarded. -- @param #CARGO self function CARGO:CanBoard() @@ -317,9 +325,6 @@ do -- CARGO return true end - - - --- Destroy the cargo. -- @param #CARGO self @@ -354,13 +359,26 @@ do -- CARGO function CARGO:GetCount() return 1 end - + --- Get the type of the Cargo. -- @param #CARGO self -- @return #string The type of the Cargo. function CARGO:GetType() return self.Type end + + + --- Get the coalition of the Cargo. + -- @param #CARGO self + -- @return Coalition + function CARGO:GetCoalition() + if self:IsLoaded() then + return self.CargoCarrier:GetCoalition() + else + return self.CargoObject:GetCoalition() + end + end + --- Get the current coordinates of the Cargo. -- @param #CARGO self @@ -792,6 +810,32 @@ do -- CARGO_REPRESENTABLE return self end + --- Send a message to a @{Group} through a communication channel near the cargo. + -- @param #CARGO_REPRESENTABLE self + -- @param #string Message + -- @param Wrapper.Group#GROUP TaskGroup + -- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown. + function CARGO_REPRESENTABLE:MessageToGroup( Message, TaskGroup, Name ) + + local CoordinateZone = ZONE_RADIUS:New( "Zone" , self:GetCoordinate():GetVec2(), 500 ) + CoordinateZone:Scan( { Object.Category.UNIT } ) + for _, DCSUnit in pairs( CoordinateZone:GetScannedUnits() ) do + local NearUnit = UNIT:Find( DCSUnit ) + self:F({NearUnit=NearUnit}) + local NearUnitCoalition = NearUnit:GetCoalition() + local CargoCoalition = self:GetCoalition() + if NearUnitCoalition == CargoCoalition then + local Attributes = NearUnit:GetDesc() + self:F({Desc=Attributes}) + if NearUnit:HasAttribute( "Trucks" ) then + MESSAGE:New( Message, 20, NearUnit:GetCallsign() .. " reporting - Cargo " .. self:GetName() ):ToGroup( TaskGroup ) + break + end + end + end + + end + end -- CARGO_REPRESENTABLE @@ -825,7 +869,7 @@ do -- CARGO_REPORTABLE -- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown. function CARGO_REPORTABLE:MessageToGroup( Message, TaskGroup, Name ) - MESSAGE:New( Message, 20, "Cargo " .. self:GetName() ):ToGroup( TaskGroup ) + MESSAGE:New( Message, 20, "Cargo " .. self:GetName() .. " reporting" ):ToGroup( TaskGroup ) end diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua index 44ef41af6..b4a61a58b 100644 --- a/Moose Development/Moose/Cargo/CargoCrate.lua +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -50,7 +50,7 @@ do -- CARGO_CRATE self:F( { Type, Name, NearRadius } ) self.CargoObject = CargoStatic - + self:T( self.ClassName ) -- Cargo objects are added to the _DATABASE and SET_CARGO objects. diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 07806b45c..57ec3a0af 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -95,6 +95,7 @@ function CARGO_GROUP:New( CargoGroup, Type, Name, LoadRadius ) self:SetWeight( WeightGroup ) + self.CargoLimit = 10 self:T( { "Weight Cargo", WeightGroup } ) diff --git a/Moose Development/Moose/Cargo/CargoSlingload.lua b/Moose Development/Moose/Cargo/CargoSlingload.lua index 91f69f873..5e979d93c 100644 --- a/Moose Development/Moose/Cargo/CargoSlingload.lua +++ b/Moose Development/Moose/Cargo/CargoSlingload.lua @@ -66,6 +66,12 @@ do -- CARGO_SLINGLOAD end + --- Check if the cargo can be Slingloaded. + -- @param #CARGO self + function CARGO:CanSlingload() + return true + end + --- Check if the cargo can be Boarded. -- @param #CARGO_SLINGLOAD self function CARGO_SLINGLOAD:CanBoard() diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index a21911627..c68fec80b 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -174,6 +174,7 @@ EVENT = { ClassName = "EVENT", ClassID = 0, + MissionEnd = false, } world.event.S_EVENT_NEW_CARGO = world.event.S_EVENT_MAX + 1000 @@ -748,8 +749,13 @@ function EVENT:onEvent( Event ) if self and self.Events and self.Events[Event.id] and + self.MissionEnd == false and ( Event.initiator ~= nil or ( Event.initiator == nil and Event.id ~= EVENTS.PlayerLeaveUnit ) ) then + if Event.id and Event.id == EVENTS.MissionEnd then + self.MissionEnd = true + end + if Event.initiator then Event.IniObjectCategory = Event.initiator:getCategory() diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index 0c49c79e3..efcea3aee 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -724,6 +724,10 @@ do -- FSM return self.current end + function FSM:GetCurrentState() + return self.current + end + function FSM:Is( State ) return self.current == State diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 933413ebc..a6a723054 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -174,17 +174,17 @@ do -- TASK_CARGO Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "SelectAction", Rejected = "Reject" } ) - Fsm:AddTransition( { "Planned", "Assigned", "WaitingForCommand", "ArrivedAtPickup", "ArrivedAtDeploy", "Boarded", "UnBoarded", "Loaded", "UnLoaded", "Landed", "Boarding" }, "SelectAction", "*" ) + Fsm:AddTransition( { "Planned", "Assigned", "Cancelled", "WaitingForCommand", "ArrivedAtPickup", "ArrivedAtDeploy", "Boarded", "UnBoarded", "Loaded", "UnLoaded", "Landed", "Boarding" }, "SelectAction", "*" ) Fsm:AddTransition( "*", "RouteToPickup", "RoutingToPickup" ) Fsm:AddProcess ( "RoutingToPickup", "RouteToPickupPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtPickup", Cancelled = "CancelRouteToPickup" } ) Fsm:AddTransition( "Arrived", "ArriveAtPickup", "ArrivedAtPickup" ) - Fsm:AddTransition( "Cancelled", "CancelRouteToPickup", "WaitingForCommand" ) + Fsm:AddTransition( "Cancelled", "CancelRouteToPickup", "Cancelled" ) Fsm:AddTransition( "*", "RouteToDeploy", "RoutingToDeploy" ) Fsm:AddProcess ( "RoutingToDeploy", "RouteToDeployZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtDeploy", Cancelled = "CancelRouteToDeploy" } ) Fsm:AddTransition( "Arrived", "ArriveAtDeploy", "ArrivedAtDeploy" ) - Fsm:AddTransition( "Cancelled", "CancelRouteToDeploy", "WaitingForCommand" ) + Fsm:AddTransition( "Cancelled", "CancelRouteToDeploy", "Cancelled" ) Fsm:AddTransition( { "ArrivedAtPickup", "ArrivedAtDeploy", "Landing" }, "Land", "Landing" ) Fsm:AddTransition( "Landing", "Landed", "Landed" ) @@ -249,7 +249,7 @@ do -- TASK_CARGO self:F( { CargoUnloaded = Cargo:IsUnLoaded(), CargoLoaded = Cargo:IsLoaded(), CargoItemCount = CargoItemCount } ) if Cargo:IsUnLoaded() then - if CargoItemCount <= Task.CargoLimit then + if CargoItemCount < 1 then if Cargo:IsInReportRadius( TaskUnit:GetPointVec2() ) then local NotInDeployZones = true for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do @@ -261,18 +261,26 @@ do -- TASK_CARGO if not TaskUnit:InAir() then if Cargo:CanBoard() == true then if Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then - Cargo:Report( "Reporting for boarding at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "board", TaskUnit:GetGroup() ) + Cargo:Report( "ready for boarding at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "board", TaskUnit:GetGroup() ) MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Board cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuBoardCargo, self, Cargo ):SetTime(MenuTime) else - Cargo:Report( "Reporting at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "reporting", TaskUnit:GetGroup() ) + Cargo:Report( "Board at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "reporting", TaskUnit:GetGroup() ) end else if Cargo:CanLoad() == true then if Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then - Cargo:Report( "Reporting for loading at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "load", TaskUnit:GetGroup() ) + Cargo:Report( "ready for loading at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "load", TaskUnit:GetGroup() ) MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Load cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuLoadCargo, self, Cargo ):SetTime(MenuTime) else - Cargo:Report( "Reporting at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "reporting", TaskUnit:GetGroup() ) + Cargo:Report( "Load at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "reporting", TaskUnit:GetGroup() ) + end + else + if Cargo:CanSlingload() == true then + if Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then + Cargo:Report( "ready for slingloading at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "slingload", TaskUnit:GetGroup() ) + else + Cargo:Report( "Slingload at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "reporting", TaskUnit:GetGroup() ) + end end end end @@ -282,9 +290,13 @@ do -- TASK_CARGO end end else - MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Route to Pickup cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuRouteToPickup, self, Cargo ):SetTime(MenuTime) - TaskUnit.Menu:SetTime( MenuTime ) - Cargo:ReportResetAll( TaskUnit:GetGroup() ) + if self:Is( "RoutingToPickup" ) then + else + self:F("route menu set") + MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Route to Pickup cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuRouteToPickup, self, Cargo ):SetTime(MenuTime) + TaskUnit.Menu:SetTime( MenuTime ) + Cargo:ReportResetAll( TaskUnit:GetGroup() ) + end end end -- Cargo in deployzones are flagged as deployed. @@ -405,7 +417,6 @@ do -- TASK_CARGO function Fsm:onafterArriveAtPickup( TaskUnit, Task ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) if self.Cargo:IsAlive() then - self.Cargo:Smoke( Task:GetSmokeColor(), 15 ) if TaskUnit:IsAir() then Task:GetMission():GetCommandCenter():MessageToGroup( "Land", TaskUnit:GetGroup() ) self:__Land( -0.1, "Pickup" ) @@ -422,6 +433,7 @@ do -- TASK_CARGO function Fsm:onafterCancelRouteToPickup( TaskUnit, Task ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + Task:GetMission():GetCommandCenter():MessageToGroup( "Cancelled routing to Cargo " .. self.Cargo:GetName(), TaskUnit:GetGroup() ) self:__SelectAction( -0.1 ) end @@ -459,6 +471,7 @@ do -- TASK_CARGO function Fsm:onafterCancelRouteToDeploy( TaskUnit, Task ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + Task:GetMission():GetCommandCenter():MessageToGroup( "Cancelled routing to deploy zone " .. self.DeployZone:GetName(), TaskUnit:GetGroup() ) self:__SelectAction( -0.1 ) end @@ -518,8 +531,7 @@ do -- TASK_CARGO self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) if Cargo and Cargo:IsAlive() then - self.Cargo = Cargo -- Core.Cargo#CARGO_GROUP - self:__Board( -0.1 ) + self:__Board( -0.1, Cargo ) end end @@ -527,22 +539,22 @@ do -- TASK_CARGO --- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_Cargo#TASK_CARGO Task - function Fsm:onafterBoard( TaskUnit, Task ) + function Fsm:onafterBoard( TaskUnit, Task, From, Event, To, Cargo ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - function self.Cargo:OnEnterLoaded( From, Event, To, TaskUnit, TaskProcess ) + function Cargo:OnEnterLoaded( From, Event, To, TaskUnit, TaskProcess ) self:F({From, Event, To, TaskUnit, TaskProcess }) - TaskProcess:__Boarded( 0.1 ) + TaskProcess:__Boarded( 0.1, self ) end - if self.Cargo:IsAlive() then - if self.Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then + if Cargo:IsAlive() then + if Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then if TaskUnit:InAir() then --- ABORT the boarding. Split group if any and go back to select action. else - self.Cargo:MessageToGroup( "Boarding ...", TaskUnit:GetGroup() ) - if not self.Cargo:IsBoarding() then - self.Cargo:Board( TaskUnit, 20, self ) + Cargo:MessageToGroup( "Boarding ...", TaskUnit:GetGroup() ) + if not Cargo:IsBoarding() then + Cargo:Board( TaskUnit, 20, self ) end end else @@ -555,14 +567,14 @@ do -- TASK_CARGO --- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_Cargo#TASK_CARGO Task - function Fsm:onafterBoarded( TaskUnit, Task ) + function Fsm:onafterBoarded( TaskUnit, Task, From, Event, To, Cargo ) local TaskUnitName = TaskUnit:GetName() self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) - - self.Cargo:MessageToGroup( "Boarded ...", TaskUnit:GetGroup() ) - self:Load( self.Cargo ) + Cargo:MessageToGroup( "Boarded ...", TaskUnit:GetGroup() ) + + self:__Load( -0.1, Cargo ) end @@ -570,16 +582,18 @@ do -- TASK_CARGO --- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_Cargo#TASK_CARGO Task - function Fsm:onafterLoad( TaskUnit, Task, From, Event, To, Cargo ) + function Fsm:onenterLoaded( TaskUnit, Task, From, Event, To, Cargo ) local TaskUnitName = TaskUnit:GetName() self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) if not Cargo:IsLoaded() then Cargo:Load( TaskUnit ) - TaskUnit:AddCargo( Cargo ) end + Cargo:MessageToGroup( "Loaded ...", TaskUnit:GetGroup() ) + TaskUnit:AddCargo( Cargo ) + self:__SelectAction( 1 ) -- TODO:I need to find a more decent solution for this. @@ -640,9 +654,9 @@ do -- TASK_CARGO if self.Cargo:IsAlive() then self.Cargo:MessageToGroup( "UnBoarding ...", TaskUnit:GetGroup() ) if DeployZone then - self.Cargo:UnBoard( DeployZone:GetPointVec2(), 400, self ) + self.Cargo:UnBoard( DeployZone:GetCoordinate():GetRandomCoordinateInRadius( 25, 10 ), 400, self ) else - self.Cargo:UnBoard( TaskUnit:GetPointVec2():AddX(60), 400, self ) + self.Cargo:UnBoard( TaskUnit:GetCoordinate():GetRandomCoordinateInRadius( 25, 10 ), 400, self ) end end end @@ -673,9 +687,9 @@ do -- TASK_CARGO if not Cargo:IsUnLoaded() then if DeployZone then - Cargo:UnLoad( DeployZone:GetPointVec2(), 400, self ) + Cargo:UnLoad( DeployZone:GetCoordinate():GetRandomCoordinateInRadius( 25, 10 ), 400, self ) else - Cargo:UnLoad( TaskUnit:GetPointVec2():AddX(60), 400, self ) + Cargo:UnLoad( TaskUnit:GetCoordinate():GetRandomCoordinateInRadius( 25, 10 ), 400, self ) end end TaskUnit:RemoveCargo( Cargo ) @@ -752,6 +766,7 @@ do -- TASK_CARGO ActRouteCargo:SetRange( Cargo:GetLoadRadius() ) ActRouteCargo:SetMenuCancel( TaskUnit:GetGroup(), "Cancel Routing to Cargo " .. Cargo:GetName(), TaskUnit.Menu ) ActRouteCargo:Start() + return self end @@ -769,6 +784,7 @@ do -- TASK_CARGO ActRouteDeployZone:SetZone( DeployZone ) ActRouteDeployZone:SetMenuCancel( TaskUnit:GetGroup(), "Cancel Routing to Deploy Zone" .. DeployZone:GetName(), TaskUnit.Menu ) ActRouteDeployZone:Start() + return self end diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 219705f85..ed536a603 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -326,6 +326,9 @@ function UNIT:GetCallsign() if DCSUnit then local UnitCallSign = DCSUnit:getCallsign() + if UnitCallSign == "" then + UnitCallSign = DCSUnit:getName() + end return UnitCallSign end From 31f0bb9fef3d1bdff7d4cac40f306e820be47be3 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sun, 8 Apr 2018 13:33:18 +0200 Subject: [PATCH 038/420] Adding events for Deployed and PickedUp. --- .../Moose/Tasking/Task_CARGO.lua | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index a6a723054..ece266d49 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -167,6 +167,61 @@ do -- TASK_CARGO self.DeployZones = {} -- setmetatable( {}, { __mode = "v" } ) -- weak table on value + self:AddTransition( "*", "Deployed", "*" ) + + --- Deployed Handler OnBefore for Type + -- @function [parent=#Type] OnBeforeDeployed + -- @param #Type self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- Deployed Handler OnAfter for Type + -- @function [parent=#Type] OnAfterDeployed + -- @param #Type self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- Deployed Trigger for Type + -- @function [parent=#Type] Deployed + -- @param #Type self + + --- Deployed Asynchronous Trigger for Type + -- @function [parent=#Type] __Deployed + -- @param #Type self + -- @param #number Delay + + + self:AddTransition( "*", "PickedUp", "*" ) + + --- PickedUp Handler OnBefore for Type + -- @function [parent=#Type] OnBeforePickedUp + -- @param #Type self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @return #boolean + + --- PickedUp Handler OnAfter for Type + -- @function [parent=#Type] OnAfterPickedUp + -- @param #Type self + -- @param #string From + -- @param #string Event + -- @param #string To + + --- PickedUp Trigger for Type + -- @function [parent=#Type] PickedUp + -- @param #Type self + + --- PickedUp Asynchronous Trigger for Type + -- @function [parent=#Type] __PickedUp + -- @param #Type self + -- @param #number Delay + + + local Fsm = self:GetUnitProcess() From 8b8fcaaacddac424ccc43d5f104ce4f5e40269d6 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sun, 8 Apr 2018 18:59:42 +0200 Subject: [PATCH 039/420] # Conflicts: # Moose Development/Moose/Tasking/Task_CARGO.lua --- Moose Development/Moose/Cargo/Cargo.lua | 2 +- Moose Development/Moose/Cargo/CargoCrate.lua | 22 +++++++++++++++- .../Moose/Cargo/CargoSlingload.lua | 20 ++++++++++++++ .../Moose/Tasking/Task_CARGO.lua | 26 ++++++++----------- 4 files changed, 53 insertions(+), 17 deletions(-) diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 09fac4fc3..52ad16b29 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -561,7 +561,7 @@ do -- CARGO -- @param Core.Point#Coordinate Coordinate -- @return #boolean true if the CargoGroup is within the loading radius. function CARGO:IsInLoadRadius( Coordinate ) - self:F( { Coordinate } ) + self:F( { Coordinate, LoadRadius = self.LoadRadius } ) local Distance = 0 if self:IsUnLoaded() then diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua index b4a61a58b..1edd1c167 100644 --- a/Moose Development/Moose/Cargo/CargoCrate.lua +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -134,12 +134,32 @@ do -- CARGO_CRATE return false end + --- Check if Cargo Crate is in the radius for the Cargo to be reported. + -- @param #CARGO self + -- @param Core.Point#Coordinate Coordinate + -- @return #boolean true if the Cargo Crate is within the report radius. + function CARGO_CRATE:IsInReportRadius( Coordinate ) + self:F( { Coordinate, LoadRadius = self.LoadRadius } ) + + local Distance = 0 + if self:IsUnLoaded() then + Distance = Coordinate:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) + self:T( Distance ) + if Distance <= self.LoadRadius then + return true + end + end + + return false + end + + --- Check if Cargo Crate is in the radius for the Cargo to be Boarded or Loaded. -- @param #CARGO self -- @param Core.Point#Coordinate Coordinate -- @return #boolean true if the Cargo Crate is within the loading radius. function CARGO_CRATE:IsInLoadRadius( Coordinate ) - self:F( { Coordinate } ) + self:F( { Coordinate, LoadRadius = self.NearRadius } ) local Distance = 0 if self:IsUnLoaded() then diff --git a/Moose Development/Moose/Cargo/CargoSlingload.lua b/Moose Development/Moose/Cargo/CargoSlingload.lua index 5e979d93c..f8be5c183 100644 --- a/Moose Development/Moose/Cargo/CargoSlingload.lua +++ b/Moose Development/Moose/Cargo/CargoSlingload.lua @@ -97,6 +97,26 @@ do -- CARGO_SLINGLOAD end + --- Check if Cargo Crate is in the radius for the Cargo to be reported. + -- @param #CARGO_SLINGLOAD self + -- @param Core.Point#Coordinate Coordinate + -- @return #boolean true if the Cargo Crate is within the report radius. + function CARGO_SLINGLOAD:IsInReportRadius( Coordinate ) + self:F( { Coordinate, LoadRadius = self.LoadRadius } ) + + local Distance = 0 + if self:IsUnLoaded() then + Distance = Coordinate:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) + self:T( Distance ) + if Distance <= self.LoadRadius then + return true + end + end + + return false + end + + --- Check if Cargo Slingload is in the radius for the Cargo to be Boarded or Loaded. -- @param #CARGO_SLINGLOAD self -- @param Core.Point#Coordinate Coordinate diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index ece266d49..055604c9c 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -457,7 +457,7 @@ do -- TASK_CARGO self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) if Cargo:IsAlive() then - self.Cargo = Cargo -- Core.Cargo#CARGO + self.Cargo = Cargo -- Cargo.Cargo#CARGO Task:SetCargoPickup( self.Cargo, TaskUnit ) self:__RouteToPickupPoint( -0.1 ) end @@ -539,7 +539,7 @@ do -- TASK_CARGO self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) if self.Cargo:IsAlive() then - if self.Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then + if self.Cargo:IsInReportRadius( TaskUnit:GetPointVec2() ) then if TaskUnit:InAir() then self:__Land( -10, Action ) else @@ -548,9 +548,9 @@ do -- TASK_CARGO end else if Action == "Pickup" then - self:__RouteToPickupZone( -0.1 ) + self:__RouteToPickup( -0.1, self.Cargo ) else - self:__RouteToDeployZone( -0.1 ) + self:__RouteToDeploy( -0.1, self.Cargo ) end end end @@ -563,7 +563,7 @@ do -- TASK_CARGO self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) if self.Cargo:IsAlive() then - if self.Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then + if self.Cargo:IsInReportRadius( TaskUnit:GetPointVec2() ) then if TaskUnit:InAir() then self:__Land( -0.1, Action ) else @@ -571,9 +571,9 @@ do -- TASK_CARGO end else if Action == "Pickup" then - self:__RouteToPickupZone( -0.1 ) + self:__RouteToPickup( -0.1, self.Cargo ) else - self:__RouteToDeployZone( -0.1 ) + self:__RouteToDeploy( -0.1, self.Cargo ) end end end @@ -624,6 +624,8 @@ do -- TASK_CARGO -- @param Tasking.Task_Cargo#TASK_CARGO Task function Fsm:onafterBoarded( TaskUnit, Task, From, Event, To, Cargo ) + self:F( { Cargo = Cargo } ) + local TaskUnitName = TaskUnit:GetName() self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) @@ -639,6 +641,8 @@ do -- TASK_CARGO -- @param Tasking.Task_Cargo#TASK_CARGO Task function Fsm:onenterLoaded( TaskUnit, Task, From, Event, To, Cargo ) + self:F( { Cargo = Cargo } ) + local TaskUnitName = TaskUnit:GetName() self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) @@ -651,14 +655,6 @@ do -- TASK_CARGO self:__SelectAction( 1 ) - -- TODO:I need to find a more decent solution for this. - Task:E( { CargoPickedUp = Task.CargoPickedUp } ) - if Cargo:IsAlive() then - if Task.CargoPickedUp then - Task:CargoPickedUp( TaskUnit, Cargo ) - end - end - end From a95afe995610a1d53babcaa74794d1546b196868 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sun, 8 Apr 2018 22:36:58 +0200 Subject: [PATCH 040/420] # Conflicts: # Moose Development/Moose/Tasking/Task_CARGO.lua --- .../Moose/Tasking/Task_CARGO.lua | 2 ++ .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 26 +++++++++++-------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 055604c9c..7976fe39e 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -643,6 +643,8 @@ do -- TASK_CARGO self:F( { Cargo = Cargo } ) + self:F( { Cargo = Cargo } ) + local TaskUnitName = TaskUnit:GetName() self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index ca96b2d87..11c9feede 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -429,17 +429,17 @@ do -- TASK_CARGO_DISPATCHER --- Add a Transport task to transport cargo from fixed locations to a deployment zone. -- @param #TASK_CARGO_DISPATCHER self - -- @param #string TransportTaskName (optional) The name of the transport task. + -- @param #string TaskName (optional) The name of the transport task. -- @param Core.SetCargo#SET_CARGO SetCargo The SetCargo to be transported. -- @param #string Briefing The briefing of the task transport to be shown to the player. -- @return #TASK_CARGO_DISPATCHER -- @usage -- -- -- Add a Transport task to transport cargo of different types to a Transport Deployment Zone. - function TASK_CARGO_DISPATCHER:AddTransportTask( TransportTaskName, SetCargo, Briefing ) + function TASK_CARGO_DISPATCHER:AddTransportTask( TaskName, SetCargo, Briefing ) self.TransportCount = self.TransportCount + 1 - local TaskName = string.format( ( TransportTaskName or "Transport" ) .. ".%03d", self.TransportCount ) + local TaskName = string.format( ( TaskName or "Transport" ) .. ".%03d", self.TransportCount ) self.Transport[TaskName] = {} self.Transport[TaskName].SetCargo = SetCargo @@ -452,13 +452,15 @@ do -- TASK_CARGO_DISPATCHER --- Define one deploy zone for the Transport tasks. -- @param #TASK_CARGO_DISPATCHER self - -- @param #string TransportTaskName (optional) The name of the Transport task. + -- @param #string TaskName (optional) The name of the Transport task. -- @param TransportDeployZone A Transport deploy zone. -- @return #TASK_CARGO_DISPATCHER - function TASK_CARGO_DISPATCHER:SetTransportDeployZone( TransportTaskName, TransportDeployZone ) + function TASK_CARGO_DISPATCHER:SetTransportDeployZone( TaskName, TransportDeployZone ) - if TransportTaskName then - self.Transport[TransportTaskName].DeployZones = { TransportDeployZone } + if self.Transport[TaskName] then + self.Transport[TaskName].DeployZones = { TransportDeployZone } + else + error( "TaskName does not exist" ) end return self @@ -467,14 +469,16 @@ do -- TASK_CARGO_DISPATCHER --- Define the deploy zones for the Transport tasks. -- @param #TASK_CARGO_DISPATCHER self - -- @param #string TransportTaskName (optional) The name of the Transport task. + -- @param #string TaskName (optional) The name of the Transport task. -- @param TransportDeployZones A list of the Transport deploy zones. -- @return #TASK_CARGO_DISPATCHER -- - function TASK_CARGO_DISPATCHER:SetTransportDeployZones( TransportTaskName, TransportDeployZones ) + function TASK_CARGO_DISPATCHER:SetTransportDeployZones( TaskName, TransportDeployZones ) - if TransportTaskName then - self.Transport[TransportTaskName].DeployZones = TransportDeployZones + if self.Transport[TaskName] then + self.Transport[TaskName].DeployZones = TransportDeployZones + else + error( "TaskName does not exist" ) end return self From 86cc1f81b68d58caec0b24e18b1da09ac99f1743 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sun, 8 Apr 2018 22:38:45 +0200 Subject: [PATCH 041/420] Finish Cargo/FC/Transport --- Moose Development/Moose/Tasking/Task_CARGO.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 7976fe39e..2cb400dd8 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -645,6 +645,8 @@ do -- TASK_CARGO self:F( { Cargo = Cargo } ) + self:F( { Cargo = Cargo } ) + local TaskUnitName = TaskUnit:GetName() self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) From 4eb596f5bf8644258b40caab4ebafe6f8bdfc8e2 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 9 Apr 2018 12:56:11 +0200 Subject: [PATCH 042/420] Finish Cargo/FC/Transport --- .../Moose/Tasking/Task_CARGO.lua | 324 ++++-------------- .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 11 + .../Moose/Tasking/Task_Cargo_Transport.lua | 77 +---- .../Moose/Wrapper/Controllable.lua | 2 +- Moose Development/Moose/Wrapper/Group.lua | 4 +- 5 files changed, 91 insertions(+), 327 deletions(-) diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 2cb400dd8..56d0619fe 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -23,7 +23,7 @@ -- -- === -- --- @module Task_Cargo +-- @module Tasking.Task_Cargo do -- TASK_CARGO @@ -167,60 +167,50 @@ do -- TASK_CARGO self.DeployZones = {} -- setmetatable( {}, { __mode = "v" } ) -- weak table on value - self:AddTransition( "*", "Deployed", "*" ) + self:AddTransition( "*", "CargoDeployed", "*" ) - --- Deployed Handler OnBefore for Type - -- @function [parent=#Type] OnBeforeDeployed - -- @param #Type self + --- CargoDeployed Handler OnBefore for TASK_CARGO + -- @function [parent=#TASK_CARGO] OnBeforeCargoDeployed + -- @param #TASK_CARGO self -- @param #string From -- @param #string Event -- @param #string To + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. -- @return #boolean - --- Deployed Handler OnAfter for Type - -- @function [parent=#Type] OnAfterDeployed - -- @param #Type self + --- CargoDeployed Handler OnAfter for TASK_CARGO + -- @function [parent=#TASK_CARGO] OnAfterCargoDeployed + -- @param #TASK_CARGO self -- @param #string From -- @param #string Event -- @param #string To - - --- Deployed Trigger for Type - -- @function [parent=#Type] Deployed - -- @param #Type self - - --- Deployed Asynchronous Trigger for Type - -- @function [parent=#Type] __Deployed - -- @param #Type self - -- @param #number Delay - - - self:AddTransition( "*", "PickedUp", "*" ) + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. + -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. - --- PickedUp Handler OnBefore for Type - -- @function [parent=#Type] OnBeforePickedUp - -- @param #Type self + + self:AddTransition( "*", "CargoPickedUp", "*" ) + + --- CargoPickedUp Handler OnBefore for TASK_CARGO + -- @function [parent=#TASK_CARGO] OnBeforeCargoPickedUp + -- @param #TASK_CARGO self -- @param #string From -- @param #string Event -- @param #string To + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. -- @return #boolean - --- PickedUp Handler OnAfter for Type - -- @function [parent=#Type] OnAfterPickedUp - -- @param #Type self + --- CargoPickedUp Handler OnAfter for TASK_CARGO + -- @function [parent=#TASK_CARGO] OnAfterCargoPickedUp + -- @param #TASK_CARGO self -- @param #string From -- @param #string Event -- @param #string To - - --- PickedUp Trigger for Type - -- @function [parent=#Type] PickedUp - -- @param #Type self - - --- PickedUp Asynchronous Trigger for Type - -- @function [parent=#Type] __PickedUp - -- @param #Type self - -- @param #number Delay - - + -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. + -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. local Fsm = self:GetUnitProcess() @@ -302,6 +292,7 @@ do -- TASK_CARGO -- end self:F( { CargoUnloaded = Cargo:IsUnLoaded(), CargoLoaded = Cargo:IsLoaded(), CargoItemCount = CargoItemCount } ) + Task:E( { TaskDeployZones = Task.DeployZones, TaskName = Task:GetName() } ) if Cargo:IsUnLoaded() then if CargoItemCount < 1 then @@ -354,18 +345,18 @@ do -- TASK_CARGO end end end + -- Cargo in deployzones are flagged as deployed. for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do + Task:E( { DeployZone = DeployZone } ) if Cargo:IsInZone( DeployZone ) then - if not Cargo:IsDeployed() then + Task:E( { CargoIsDeployed = Task.CargoDeployed and "true" or "false" } ) + if Cargo:IsDeployed() == false then Cargo:SetDeployed( true ) - -- TODO:I need to find a more decent solution for this. - Task:E( { CargoDeployed = Task.CargoDeployed and "true" or "false" } ) + -- Now we call a callback method to handle the CargoDeployed event. Task:E( { CargoIsAlive = Cargo:IsAlive() and "true" or "false" } ) if Cargo:IsAlive() then - if Task.CargoDeployed then - Task:CargoDeployed( TaskUnit, Cargo, DeployZone ) - end + Task:CargoDeployed( TaskUnit, Cargo, DeployZone ) end end end @@ -538,17 +529,28 @@ do -- TASK_CARGO function Fsm:onafterLand( TaskUnit, Task, From, Event, To, Action ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - if self.Cargo:IsAlive() then - if self.Cargo:IsInReportRadius( TaskUnit:GetPointVec2() ) then - if TaskUnit:InAir() then - self:__Land( -10, Action ) + if Action == "Pickup" then + if self.Cargo:IsAlive() then + if self.Cargo:IsInReportRadius( TaskUnit:GetPointVec2() ) then + if TaskUnit:InAir() then + self:__Land( -10, Action ) + else + Task:GetMission():GetCommandCenter():MessageToGroup( "Landed at pickup location...", TaskUnit:GetGroup() ) + self:__Landed( -0.1, Action ) + end else - Task:GetMission():GetCommandCenter():MessageToGroup( "Landed ...", TaskUnit:GetGroup() ) - self:__Landed( -0.1, Action ) - end - else - if Action == "Pickup" then self:__RouteToPickup( -0.1, self.Cargo ) + end + end + else + if TaskUnit:IsAlive() then + if TaskUnit:IsInZone( self.DeployZone ) then + if TaskUnit:InAir() then + self:__Land( -10, Action ) + else + Task:GetMission():GetCommandCenter():MessageToGroup( "Landed at deploy zone " .. self.DeployZone:GetName(), TaskUnit:GetGroup() ) + self:__Landed( -0.1, Action ) + end else self:__RouteToDeploy( -0.1, self.Cargo ) end @@ -562,16 +564,26 @@ do -- TASK_CARGO function Fsm:onafterLanded( TaskUnit, Task, From, Event, To, Action ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - if self.Cargo:IsAlive() then - if self.Cargo:IsInReportRadius( TaskUnit:GetPointVec2() ) then - if TaskUnit:InAir() then - self:__Land( -0.1, Action ) + if Action == "Pickup" then + if self.Cargo:IsAlive() then + if self.Cargo:IsInReportRadius( TaskUnit:GetPointVec2() ) then + if TaskUnit:InAir() then + self:__Land( -0.1, Action ) + else + self:__SelectAction( -0.1 ) + end else - self:__SelectAction( -0.1 ) - end - else - if Action == "Pickup" then self:__RouteToPickup( -0.1, self.Cargo ) + end + end + else + if TaskUnit:IsAlive() then + if TaskUnit:IsInZone( self.DeployZone ) then + if TaskUnit:InAir() then + self:__Land( -10, Action ) + else + self:__SelectAction( -0.1 ) + end else self:__RouteToDeploy( -0.1, self.Cargo ) end @@ -624,8 +636,6 @@ do -- TASK_CARGO -- @param Tasking.Task_Cargo#TASK_CARGO Task function Fsm:onafterBoarded( TaskUnit, Task, From, Event, To, Cargo ) - self:F( { Cargo = Cargo } ) - local TaskUnitName = TaskUnit:GetName() self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) @@ -641,12 +651,6 @@ do -- TASK_CARGO -- @param Tasking.Task_Cargo#TASK_CARGO Task function Fsm:onenterLoaded( TaskUnit, Task, From, Event, To, Cargo ) - self:F( { Cargo = Cargo } ) - - self:F( { Cargo = Cargo } ) - - self:F( { Cargo = Cargo } ) - local TaskUnitName = TaskUnit:GetName() self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) @@ -657,7 +661,9 @@ do -- TASK_CARGO Cargo:MessageToGroup( "Loaded ...", TaskUnit:GetGroup() ) TaskUnit:AddCargo( Cargo ) - self:__SelectAction( 1 ) + --Task:CargoPickedUp( TaskUnit, Cargo ) + + self:SelectAction( ) end @@ -967,183 +973,3 @@ do -- TASK_CARGO end -do -- TASK_CARGO_TRANSPORT - - --- The TASK_CARGO_TRANSPORT class - -- @type TASK_CARGO_TRANSPORT - -- @extends #TASK_CARGO - TASK_CARGO_TRANSPORT = { - ClassName = "TASK_CARGO_TRANSPORT", - } - - --- Instantiates a new TASK_CARGO_TRANSPORT. - -- @param #TASK_CARGO_TRANSPORT self - -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. - -- @param #string TaskName The name of the Task. - -- @param Core.Set#SET_CARGO SetCargo The scope of the cargo to be transported. - -- @param #string TaskBriefing The Cargo Task briefing. - -- @return #TASK_CARGO_TRANSPORT self - function TASK_CARGO_TRANSPORT:New( Mission, SetGroup, TaskName, SetCargo, TaskBriefing ) - local self = BASE:Inherit( self, TASK_CARGO:New( Mission, SetGroup, TaskName, SetCargo, "Transport", TaskBriefing ) ) -- #TASK_CARGO_TRANSPORT - self:F() - - Mission:AddTask( self ) - - - -- Events - - self:AddTransition( "*", "CargoPickedUp", "*" ) - self:AddTransition( "*", "CargoDeployed", "*" ) - - self:F( { CargoDeployed = self.CargoDeployed ~= nil and "true" or "false" } ) - - --- OnBefore Transition Handler for Event CargoPickedUp. - -- @function [parent=#TASK_CARGO_TRANSPORT] OnBeforeCargoPickedUp - -- @param #TASK_CARGO_TRANSPORT self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. - -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event CargoPickedUp. - -- @function [parent=#TASK_CARGO_TRANSPORT] OnAfterCargoPickedUp - -- @param #TASK_CARGO_TRANSPORT self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. - -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - - --- Synchronous Event Trigger for Event CargoPickedUp. - -- @function [parent=#TASK_CARGO_TRANSPORT] CargoPickedUp - -- @param #TASK_CARGO_TRANSPORT self - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. - -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - - --- Asynchronous Event Trigger for Event CargoPickedUp. - -- @function [parent=#TASK_CARGO_TRANSPORT] __CargoPickedUp - -- @param #TASK_CARGO_TRANSPORT self - -- @param #number Delay The delay in seconds. - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. - -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - - --- OnBefore Transition Handler for Event CargoDeployed. - -- @function [parent=#TASK_CARGO_TRANSPORT] OnBeforeCargoDeployed - -- @param #TASK_CARGO_TRANSPORT self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. - -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event CargoDeployed. - -- @function [parent=#TASK_CARGO_TRANSPORT] OnAfterCargoDeployed - -- @param #TASK_CARGO_TRANSPORT self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. - -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. - - --- Synchronous Event Trigger for Event CargoDeployed. - -- @function [parent=#TASK_CARGO_TRANSPORT] CargoDeployed - -- @param #TASK_CARGO_TRANSPORT self - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. - -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. - - --- Asynchronous Event Trigger for Event CargoDeployed. - -- @function [parent=#TASK_CARGO_TRANSPORT] __CargoDeployed - -- @param #TASK_CARGO_TRANSPORT self - -- @param #number Delay The delay in seconds. - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. - -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. - - local Fsm = self:GetUnitProcess() - - local CargoReport = REPORT:New( "Transport Cargo. The following cargo needs to be transported including initial positions:") - - SetCargo:ForEachCargo( - --- @param Core.Cargo#CARGO Cargo - function( Cargo ) - local CargoType = Cargo:GetType() - local CargoName = Cargo:GetName() - local CargoCoordinate = Cargo:GetCoordinate() - CargoReport:Add( string.format( '- "%s" (%s) at %s', CargoName, CargoType, CargoCoordinate:ToStringMGRS() ) ) - end - ) - - self:SetBriefing( - TaskBriefing or - CargoReport:Text() - ) - - - return self - end - - function TASK_CARGO_TRANSPORT:ReportOrder( ReportGroup ) - - return 0 - end - - - --- - -- @param #TASK_CARGO_TRANSPORT self - -- @return #boolean - function TASK_CARGO_TRANSPORT:IsAllCargoTransported() - - local CargoSet = self:GetCargoSet() - local Set = CargoSet:GetSet() - - local DeployZones = self:GetDeployZones() - - local CargoDeployed = true - - -- Loop the CargoSet (so evaluate each Cargo in the SET_CARGO ). - for CargoID, CargoData in pairs( Set ) do - local Cargo = CargoData -- Core.Cargo#CARGO - - self:F( { Cargo = Cargo:GetName(), CargoDeployed = Cargo:IsDeployed() } ) - - if Cargo:IsDeployed() then - --- -- Loop the DeployZones set for the TASK_CARGO_TRANSPORT. --- for DeployZoneID, DeployZone in pairs( DeployZones ) do --- --- -- If all cargo is in one of the deploy zones, then all is good. --- self:T( { Cargo.CargoObject } ) --- if Cargo:IsInZone( DeployZone ) == false then --- CargoDeployed = false --- end --- end - else - CargoDeployed = false - end - end - - self:F( { CargoDeployed = CargoDeployed } ) - - return CargoDeployed - end - - --- @param #TASK_CARGO_TRANSPORT self - function TASK_CARGO_TRANSPORT:onafterGoal( TaskUnit, From, Event, To ) - local CargoSet = self.CargoSet - - if self:IsAllCargoTransported() then - self:Success() - end - - self:__Goal( -10 ) - end - -end - diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index 11c9feede..bdac4b931 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -449,6 +449,17 @@ do -- TASK_CARGO_DISPATCHER return self end + + --- Add a Transport task to transport cargo from fixed locations to a deployment zone. + -- @param #TASK_CARGO_DISPATCHER self + -- @param #string TaskName (optional) The name of the transport task. + -- @return Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT + function TASK_CARGO_DISPATCHER:GetTransportTask( TaskName ) + + self:ManageTasks() + return self.Transport[TaskName] and self.Transport[TaskName].Task + end + --- Define one deploy zone for the Transport tasks. -- @param #TASK_CARGO_DISPATCHER self diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua index 0deff06e4..611eefa49 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua @@ -3,6 +3,7 @@ -- ![Banner Image](..\Presentations\TASK_CARGO\Dia1.JPG) -- -- === +-- @module do -- TASK_CARGO_TRANSPORT @@ -28,82 +29,6 @@ do -- TASK_CARGO_TRANSPORT Mission:AddTask( self ) - - -- Events - - self:AddTransition( "*", "CargoPickedUp", "*" ) - self:AddTransition( "*", "CargoDeployed", "*" ) - - self:F( { CargoDeployed = self.CargoDeployed ~= nil and "true" or "false" } ) - - --- OnBefore Transition Handler for Event CargoPickedUp. - -- @function [parent=#TASK_CARGO_TRANSPORT] OnBeforeCargoPickedUp - -- @param #TASK_CARGO_TRANSPORT self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. - -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event CargoPickedUp. - -- @function [parent=#TASK_CARGO_TRANSPORT] OnAfterCargoPickedUp - -- @param #TASK_CARGO_TRANSPORT self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. - -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - - --- Synchronous Event Trigger for Event CargoPickedUp. - -- @function [parent=#TASK_CARGO_TRANSPORT] CargoPickedUp - -- @param #TASK_CARGO_TRANSPORT self - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. - -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - - --- Asynchronous Event Trigger for Event CargoPickedUp. - -- @function [parent=#TASK_CARGO_TRANSPORT] __CargoPickedUp - -- @param #TASK_CARGO_TRANSPORT self - -- @param #number Delay The delay in seconds. - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. - -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - - --- OnBefore Transition Handler for Event CargoDeployed. - -- @function [parent=#TASK_CARGO_TRANSPORT] OnBeforeCargoDeployed - -- @param #TASK_CARGO_TRANSPORT self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. - -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. - -- @return #boolean Return false to cancel Transition. - - --- OnAfter Transition Handler for Event CargoDeployed. - -- @function [parent=#TASK_CARGO_TRANSPORT] OnAfterCargoDeployed - -- @param #TASK_CARGO_TRANSPORT self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. - -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. - - --- Synchronous Event Trigger for Event CargoDeployed. - -- @function [parent=#TASK_CARGO_TRANSPORT] CargoDeployed - -- @param #TASK_CARGO_TRANSPORT self - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. - -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. - - --- Asynchronous Event Trigger for Event CargoDeployed. - -- @function [parent=#TASK_CARGO_TRANSPORT] __CargoDeployed - -- @param #TASK_CARGO_TRANSPORT self - -- @param #number Delay The delay in seconds. - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. - -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. - local Fsm = self:GetUnitProcess() local CargoReport = REPORT:New( "Transport Cargo. The following cargo needs to be transported including initial positions:") diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index f5b414e60..2d6551fd2 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -8,7 +8,7 @@ -- -- === -- --- @module Controllable +-- @module Wrapper.Controllable diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 23fd5da1a..609b1aa91 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -23,7 +23,7 @@ -- -- === -- --- @module Group +-- @module Wrapper.Group --- @type GROUP @@ -1143,6 +1143,8 @@ function GROUP:Respawn( Template, Reset ) self:ResetEvents() + return self + end From a6889be6762245097120c44163ebef997c0ec1fa Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 9 Apr 2018 13:30:33 +0200 Subject: [PATCH 043/420] # Conflicts: # Moose Development/Moose/Tasking/Task_CARGO.lua --- Moose Development/Moose/Tasking/Task_CARGO.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 56d0619fe..278354142 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -649,7 +649,7 @@ do -- TASK_CARGO --- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Tasking.Task_Cargo#TASK_CARGO Task - function Fsm:onenterLoaded( TaskUnit, Task, From, Event, To, Cargo ) + function Fsm:onafterLoad( TaskUnit, Task, From, Event, To, Cargo ) local TaskUnitName = TaskUnit:GetName() self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) @@ -661,9 +661,9 @@ do -- TASK_CARGO Cargo:MessageToGroup( "Loaded ...", TaskUnit:GetGroup() ) TaskUnit:AddCargo( Cargo ) - --Task:CargoPickedUp( TaskUnit, Cargo ) + Task:CargoPickedUp( TaskUnit, Cargo ) - self:SelectAction( ) + self:SelectAction( -1 ) end From 1beb34231eaf6099c76c1d4817386d04e0a3a475 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 9 Apr 2018 13:38:32 +0200 Subject: [PATCH 044/420] Getting rid of an old annoyance. --- Moose Development/Moose/Wrapper/Client.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Client.lua b/Moose Development/Moose/Wrapper/Client.lua index 71e2066cb..dcf44d8d8 100644 --- a/Moose Development/Moose/Wrapper/Client.lua +++ b/Moose Development/Moose/Wrapper/Client.lua @@ -182,7 +182,6 @@ function CLIENT:ShowBriefing() if self.ClientBriefing then Briefing = Briefing .. self.ClientBriefing end - Briefing = Briefing .. " Press [LEFT ALT]+[B] to view the complete mission briefing." self:Message( Briefing, 60, "Briefing" ) end From ffe4d9a1439c25de8ba4050e21b60991664c746f Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Tue, 10 Apr 2018 20:02:40 +0200 Subject: [PATCH 045/420] Finish Cargo/FC/Transport --- Moose Development/Moose/Actions/Act_Route.lua | 9 +- Moose Development/Moose/Cargo/Cargo.lua | 17 ++ Moose Development/Moose/Cargo/CargoCrate.lua | 61 ++++++- Moose Development/Moose/Cargo/CargoGroup.lua | 154 ++++++++---------- .../Moose/Cargo/CargoSlingload.lua | 51 +++++- Moose Development/Moose/Cargo/CargoUnit.lua | 4 +- Moose Development/Moose/Core/Event.lua | 10 ++ Moose Development/Moose/Core/Menu.lua | 17 ++ Moose Development/Moose/Core/Set.lua | 10 +- Moose Development/Moose/Core/SpawnStatic.lua | 70 ++++++++ Moose Development/Moose/Tasking/Task.lua | 82 ++++++++-- .../Moose/Tasking/Task_CARGO.lua | 68 ++++---- Moose Development/Moose/Wrapper/Static.lua | 15 +- Moose Development/Moose/Wrapper/Unit.lua | 22 +-- 14 files changed, 434 insertions(+), 156 deletions(-) diff --git a/Moose Development/Moose/Actions/Act_Route.lua b/Moose Development/Moose/Actions/Act_Route.lua index cbf089140..8a0d53b0f 100644 --- a/Moose Development/Moose/Actions/Act_Route.lua +++ b/Moose Development/Moose/Actions/Act_Route.lua @@ -123,9 +123,7 @@ do -- ACT_ROUTE --- Set a Cancel Menu item. -- @param #ACT_ROUTE self -- @return #ACT_ROUTE - function ACT_ROUTE:SetMenuCancel( MenuGroup, MenuText, ParentMenu ) - - local MenuTime = timer.getTime() + 1 + function ACT_ROUTE:SetMenuCancel( MenuGroup, MenuText, ParentMenu, MenuTime, MenuTag ) self.CancelMenuGroupCommand = MENU_GROUP_COMMAND:New( MenuGroup, @@ -133,10 +131,11 @@ do -- ACT_ROUTE ParentMenu, self.MenuCancel, self - ):SetTime( MenuTime ) + ):SetTime( MenuTime ):SetTag( MenuTag ) ParentMenu:SetTime( MenuTime ) - ParentMenu:Remove( MenuTime ) + + ParentMenu:Remove( MenuTime, MenuTag ) return self end diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 52ad16b29..b7f52ee4e 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -257,6 +257,7 @@ do -- CARGO self:AddTransition( "*", "Damaged", "Damaged" ) self:AddTransition( "*", "Destroyed", "Destroyed" ) self:AddTransition( "*", "Respawn", "UnLoaded" ) + self:AddTransition( "*", "Reset", "UnLoaded" ) self.Type = Type self.Name = Name @@ -747,6 +748,22 @@ do -- CARGO self.Reported[CarrierGroup] = nil end + --- Respawn the cargo when destroyed + -- @param #CARGO self + -- @param #boolean RespawnDestroyed + function CARGO:RespawnOnDestroyed( RespawnDestroyed ) + + if RespawnDestroyed then + self.onenterDestroyed = function( self ) + self:Respawn() + end + else + self.onenterDestroyed = nil + end + + end + + end -- CARGO diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua index 1edd1c167..104e26438 100644 --- a/Moose Development/Moose/Cargo/CargoCrate.lua +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -65,6 +65,33 @@ do -- CARGO_CRATE return self end + --- @param #CARGO_CRATE self + -- @param Core.Event#EVENTDATA EventData + function CARGO_CRATE:OnEventCargoDead( EventData ) + + local Destroyed = false + + if self:IsDestroyed() or self:IsUnLoaded() or self:IsBoarding() then + if self.CargoObject:GetName() == EventData.IniUnitName then + Destroyed = true + end + else + if self:IsLoaded() then + local CarrierName = self.CargoCarrier:GetName() + if CarrierName == EventData.IniDCSUnitName then + MESSAGE:New( "Cargo is lost from carrier " .. CarrierName, 15 ):ToAll() + Destroyed = true + self.CargoCarrier:ClearCargo() + end + end + end + + if Destroyed then + self:I( { "Cargo crate destroyed: " .. self.CargoObject:GetName() } ) + self:Destroyed() + end + + end --- Enter UnLoaded State. @@ -90,7 +117,7 @@ do -- CARGO_CRATE -- Respawn the group... if self.CargoObject then - self.CargoObject:ReSpawn( ToPointVec2, 0 ) + self.CargoObject:ReSpawnAt( ToPointVec2, 0 ) self.CargoCarrier = nil end @@ -228,15 +255,39 @@ do -- CARGO_CRATE return self:IsNear( CargoCarrier:GetCoordinate(), NearRadius ) end - --- Respawn the CargoGroup. -- @param #CARGO_CRATE self function CARGO_CRATE:Respawn() - self:F( { "Respawning" } ) + self:F( { "Respawning crate " .. self:GetName() } ) + + + -- Respawn the group... + if self.CargoObject then + self.CargoObject:ReSpawn() -- A cargo destroy crates a DEAD event. + self:__Reset( -0.1 ) + end + + + end + + + --- Respawn the CargoGroup. + -- @param #CARGO_CRATE self + function CARGO_CRATE:onafterReset() + + self:F( { "Reset crate " .. self:GetName() } ) + + + -- Respawn the group... + if self.CargoObject then + self:SetDeployed( false ) + self:SetStartState( "UnLoaded" ) + self.CargoCarrier = nil + -- Cargo objects are added to the _DATABASE and SET_CARGO objects. + _EVENTDISPATCHER:CreateEventNewCargo( self ) + end - self:SetDeployed( false ) - self:SetStartState( "UnLoaded" ) end diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 57ec3a0af..4ad076362 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -38,82 +38,82 @@ do -- CARGO_GROUP ClassName = "CARGO_GROUP", } ---- CARGO_GROUP constructor. --- This make a new CARGO_GROUP from a @{Group} object. --- It will "ungroup" the group object within the sim, and will create a @{Set} of individual Unit objects. --- @param #CARGO_GROUP self --- @param Wrapper.Group#GROUP CargoGroup --- @param #string Type --- @param #string Name --- @param #number LoadRadius (optional) --- @param #number NearRadius (optional) --- @return #CARGO_GROUP -function CARGO_GROUP:New( CargoGroup, Type, Name, LoadRadius ) - local self = BASE:Inherit( self, CARGO_REPORTABLE:New( Type, Name, 0, LoadRadius ) ) -- #CARGO_GROUP - self:F( { Type, Name, LoadRadius } ) - - self.CargoSet = SET_CARGO:New() + --- CARGO_GROUP constructor. + -- This make a new CARGO_GROUP from a @{Group} object. + -- It will "ungroup" the group object within the sim, and will create a @{Set} of individual Unit objects. + -- @param #CARGO_GROUP self + -- @param Wrapper.Group#GROUP CargoGroup + -- @param #string Type + -- @param #string Name + -- @param #number LoadRadius (optional) + -- @param #number NearRadius (optional) + -- @return #CARGO_GROUP + function CARGO_GROUP:New( CargoGroup, Type, Name, LoadRadius ) + local self = BASE:Inherit( self, CARGO_REPORTABLE:New( Type, Name, 0, LoadRadius ) ) -- #CARGO_GROUP + self:F( { Type, Name, LoadRadius } ) - self:SetDeployed( false ) - - local WeightGroup = 0 - - self.GroupName = CargoGroup:GetName() - self.CargoTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplate( self.GroupName ) ) - - CargoGroup:Destroy() - - -- We iterate through the group template and for each unit in the template, we create a new group with one unit. - for UnitID, UnitTemplate in pairs( self.CargoTemplate.units ) do + self.CargoSet = SET_CARGO:New() - local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate ) - local GroupName = env.getValueDictByKey( GroupTemplate.name ) + self:SetDeployed( false ) - -- We create a new group object with one unit... - -- First we prepare the template... - GroupTemplate.name = GroupName .. "#CARGO#" .. UnitID - GroupTemplate.groupId = nil - GroupTemplate.units = {} - GroupTemplate.units[1] = UnitTemplate - local UnitName = UnitTemplate.name .. "#CARGO" - GroupTemplate.units[1].name = UnitTemplate.name .. "#CARGO" - - - -- Then we register the new group in the database - local CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID) + local WeightGroup = 0 - -- Now we spawn the new group based on the template created. - _DATABASE:Spawn( GroupTemplate ) + self.GroupName = CargoGroup:GetName() + self.CargoTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplate( self.GroupName ) ) - -- And we register the spawned unit as part of the CargoSet. - local Unit = UNIT:FindByName( UnitName ) - --local WeightUnit = Unit:GetDesc().massEmpty - --WeightGroup = WeightGroup + WeightUnit - local CargoUnit = CARGO_UNIT:New( Unit, Type, UnitName, 10 ) - self.CargoSet:Add( UnitName, CargoUnit ) + CargoGroup:Destroy() + + -- We iterate through the group template and for each unit in the template, we create a new group with one unit. + for UnitID, UnitTemplate in pairs( self.CargoTemplate.units ) do + + local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate ) + local GroupName = env.getValueDictByKey( GroupTemplate.name ) + + -- We create a new group object with one unit... + -- First we prepare the template... + GroupTemplate.name = GroupName .. "#CARGO#" .. UnitID + GroupTemplate.groupId = nil + GroupTemplate.units = {} + GroupTemplate.units[1] = UnitTemplate + local UnitName = UnitTemplate.name .. "#CARGO" + GroupTemplate.units[1].name = UnitTemplate.name .. "#CARGO" + + + -- Then we register the new group in the database + local CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID) + + -- Now we spawn the new group based on the template created. + _DATABASE:Spawn( GroupTemplate ) + + -- And we register the spawned unit as part of the CargoSet. + local Unit = UNIT:FindByName( UnitName ) + --local WeightUnit = Unit:GetDesc().massEmpty + --WeightGroup = WeightGroup + WeightUnit + local CargoUnit = CARGO_UNIT:New( Unit, Type, UnitName, 10 ) + self.CargoSet:Add( UnitName, CargoUnit ) + end + + + self:SetWeight( WeightGroup ) + self.CargoLimit = 10 + + self:T( { "Weight Cargo", WeightGroup } ) + + -- Cargo objects are added to the _DATABASE and SET_CARGO objects. + _EVENTDISPATCHER:CreateEventNewCargo( self ) + + self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead ) + self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead ) + self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead ) + + self:SetEventPriority( 4 ) + + return self end - - self:SetWeight( WeightGroup ) - self.CargoLimit = 10 - - self:T( { "Weight Cargo", WeightGroup } ) - - -- Cargo objects are added to the _DATABASE and SET_CARGO objects. - _EVENTDISPATCHER:CreateEventNewCargo( self ) - - self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead ) - self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead ) - self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead ) - - self:SetEventPriority( 4 ) - - return self -end - ---- @param #CARGO_GROUP self --- @param Core.Event#EVENTDATA EventData -function CARGO_GROUP:OnEventCargoDead( EventData ) + --- @param #CARGO_GROUP self + -- @param Core.Event#EVENTDATA EventData + function CARGO_GROUP:OnEventCargoDead( EventData ) local Destroyed = false @@ -364,22 +364,6 @@ function CARGO_GROUP:OnEventCargoDead( EventData ) end - --- Respawn the cargo when destroyed - -- @param #CARGO_GROUP self - -- @param #boolean RespawnDestroyed - function CARGO_GROUP:RespawnOnDestroyed( RespawnDestroyed ) - self:F({"In function RespawnOnDestroyed"}) - if RespawnDestroyed then - self.onenterDestroyed = function( self ) - self:F("IN FUNCTION") - self:Respawn() - end - else - self.onenterDestroyed = nil - end - - end - --- Get the current Coordinate of the CargoGroup. -- @param #CARGO_GROUP self -- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup. diff --git a/Moose Development/Moose/Cargo/CargoSlingload.lua b/Moose Development/Moose/Cargo/CargoSlingload.lua index f8be5c183..04626385b 100644 --- a/Moose Development/Moose/Cargo/CargoSlingload.lua +++ b/Moose Development/Moose/Cargo/CargoSlingload.lua @@ -64,6 +64,26 @@ do -- CARGO_SLINGLOAD return self end + + + --- @param #CARGO_SLINGLOAD self + -- @param Core.Event#EVENTDATA EventData + function CARGO_SLINGLOAD:OnEventCargoDead( EventData ) + + local Destroyed = false + + if self:IsDestroyed() or self:IsUnLoaded() then + if self.CargoObject:GetName() == EventData.IniUnitName then + Destroyed = true + end + end + + if Destroyed then + self:I( { "Cargo crate destroyed: " .. self.CargoObject:GetName() } ) + self:Destroyed() + end + + end --- Check if the cargo can be Slingloaded. @@ -196,10 +216,35 @@ do -- CARGO_SLINGLOAD -- @param #CARGO_SLINGLOAD self function CARGO_SLINGLOAD:Respawn() - self:F( { "Respawning" } ) + self:F( { "Respawning slingload " .. self:GetName() } ) + + + -- Respawn the group... + if self.CargoObject then + self.CargoObject:ReSpawn() -- A cargo destroy crates a DEAD event. + self:__Reset( -0.1 ) + end + + + end + + + --- Respawn the CargoGroup. + -- @param #CARGO_SLINGLOAD self + function CARGO_SLINGLOAD:onafterReset() + + self:F( { "Reset slingload " .. self:GetName() } ) + + + -- Respawn the group... + if self.CargoObject then + self:SetDeployed( false ) + self:SetStartState( "UnLoaded" ) + self.CargoCarrier = nil + -- Cargo objects are added to the _DATABASE and SET_CARGO objects. + _EVENTDISPATCHER:CreateEventNewCargo( self ) + end - self:SetDeployed( false ) - self:SetStartState( "UnLoaded" ) end diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index 5ac185277..70a42b6c8 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -103,7 +103,7 @@ do -- CARGO_UNIT -- Respawn the group... if self.CargoObject then - self.CargoObject:ReSpawn( FromPointVec2:GetVec3(), CargoDeployHeading ) + self.CargoObject:ReSpawnAt( FromPointVec2, CargoDeployHeading ) self:F( { "CargoUnits:", self.CargoObject:GetGroup():GetName() } ) self.CargoCarrier = nil @@ -207,7 +207,7 @@ do -- CARGO_UNIT -- Respawn the group... if self.CargoObject then - self.CargoObject:ReSpawn( ToPointVec2:GetVec3(), 0 ) + self.CargoObject:ReSpawnAt( ToPointVec2, 0 ) self.CargoCarrier = nil end diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index c68fec80b..dd26c3bf6 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -794,6 +794,16 @@ function EVENT:onEvent( Event ) Event.IniTypeName = Event.IniDCSUnit:getTypeName() end + if Event.IniObjectCategory == Object.Category.CARGO then + Event.IniDCSUnit = Event.initiator + Event.IniDCSUnitName = Event.IniDCSUnit:getName() + Event.IniUnitName = Event.IniDCSUnitName + Event.IniUnit = CARGO:FindByName( Event.IniDCSUnitName ) + Event.IniCoalition = Event.IniDCSUnit:getCoalition() + Event.IniCategory = Event.IniDCSUnit:getDesc().category + Event.IniTypeName = Event.IniDCSUnit:getTypeName() + end + if Event.IniObjectCategory == Object.Category.SCENERY then Event.IniDCSUnit = Event.initiator Event.IniDCSUnitName = Event.IniDCSUnit:getName() diff --git a/Moose Development/Moose/Core/Menu.lua b/Moose Development/Moose/Core/Menu.lua index 9bb30be21..5ee9f9fff 100644 --- a/Moose Development/Moose/Core/Menu.lua +++ b/Moose Development/Moose/Core/Menu.lua @@ -213,6 +213,7 @@ do -- MENU_BASE self.Menus = {} self.MenuCount = 0 self.MenuTime = timer.getTime() + self.MenuRemoveParent = false if self.ParentMenu then self.ParentMenu.Menus = self.ParentMenu.Menus or {} @@ -226,14 +227,30 @@ do -- MENU_BASE if self.ParentMenu then self.ParentMenu.Menus = self.ParentMenu.Menus or {} self.ParentMenu.Menus[MenuText] = Menu + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1 end end function MENU_BASE:ClearParentMenu( MenuText ) if self.ParentMenu and self.ParentMenu.Menus[MenuText] then self.ParentMenu.Menus[MenuText] = nil + self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1 + if self.ParentMenu.MenuCount == 0 then + --self.ParentMenu:Remove() + end end end + + --- Sets a @{Menu} to remove automatically the parent menu when the menu removed is the last child menu of that parent @{Menu}. + -- @param #MENU_BASE self + -- @param #boolean RemoveParent If true, the parent menu is automatically removed when this menu is the last child menu of that parent @{Menu}. + -- @return #MENU_BASE + function MENU_BASE:SetRemoveParent( RemoveParent ) + self:F( { RemoveParent } ) + self.MenuRemoveParent = RemoveParent + return self + end + --- Gets a @{Menu} from a parent @{Menu} -- @param #MENU_BASE self diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 58c24bcce..48973c2b2 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -173,7 +173,7 @@ end -- @param Core.Base#BASE Object -- @return Core.Base#BASE The added BASE Object. function SET_BASE:Add( ObjectName, Object ) - self:F3( { ObjectName = ObjectName, Object = Object } ) + self:F( { 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 @@ -4349,7 +4349,7 @@ function SET_CARGO:IsIncludeObject( MCargo ) --R2.1 MCargoCoalition = true end end - self:T( { "Evaluated Coalition", MCargoCoalition } ) + self:F( { "Evaluated Coalition", MCargoCoalition } ) MCargoInclude = MCargoInclude and MCargoCoalition end @@ -4361,7 +4361,7 @@ function SET_CARGO:IsIncludeObject( MCargo ) --R2.1 MCargoType = true end end - self:T( { "Evaluated Type", MCargoType } ) + self:F( { "Evaluated Type", MCargoType } ) MCargoInclude = MCargoInclude and MCargoType end @@ -4373,7 +4373,7 @@ function SET_CARGO:IsIncludeObject( MCargo ) --R2.1 MCargoPrefix = true end end - self:T( { "Evaluated Prefix", MCargoPrefix } ) + self:F( { "Evaluated Prefix", MCargoPrefix } ) MCargoInclude = MCargoInclude and MCargoPrefix end end @@ -4387,6 +4387,8 @@ end -- @param Core.Event#EVENTDATA EventData function SET_CARGO:OnEventNewCargo( EventData ) --R2.1 + self:F( { "New Cargo", EventData } ) + if EventData.Cargo then if EventData.Cargo and self:IsIncludeObject( EventData.Cargo ) then self:Add( EventData.Cargo.Name , EventData.Cargo ) diff --git a/Moose Development/Moose/Core/SpawnStatic.lua b/Moose Development/Moose/Core/SpawnStatic.lua index 118b74b8c..1f5b8a97a 100644 --- a/Moose Development/Moose/Core/SpawnStatic.lua +++ b/Moose Development/Moose/Core/SpawnStatic.lua @@ -118,6 +118,7 @@ function SPAWNSTATIC:NewFromType( SpawnTypeName, SpawnShapeName, SpawnCategory, return self end + --- Creates a new @{Static} at the original position. -- @param #SPAWNSTATIC self -- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360. @@ -192,6 +193,75 @@ function SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1 return nil end + +--- Creates the original @{Static} at a POINT_VEC2. +-- @param #SPAWNSTATIC self +-- @param Core.Point#POINT_VEC2 PointVec2 The 2D coordinate where to spawn the static. +-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360. +-- @param #string (optional) The name of the new static. +-- @return #SPAWNSTATIC +function SPAWNSTATIC:ReSpawn() + + local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix ) + + if StaticTemplate then + + local CountryID = self.CountryID + local CountryName = _DATABASE.COUNTRY_NAME[CountryID] + + StaticTemplate.units = nil + StaticTemplate.route = nil + StaticTemplate.groupId = nil + + StaticTemplate.CountryID = nil + StaticTemplate.CoalitionID = nil + StaticTemplate.CategoryID = nil + + local Static = coalition.addStaticObject( CountryID, StaticTemplate ) + + return Static + end + + return nil +end + + +--- Creates the original @{Static} at a POINT_VEC2. +-- @param #SPAWNSTATIC self +-- @param Core.Point#COORDINATE Coordinate The 2D coordinate where to spawn the static. +-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360. +-- @return #SPAWNSTATIC +function SPAWNSTATIC:ReSpawnAt( Coordinate, Heading ) + + local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix ) + + if StaticTemplate then + + local CountryID = self.CountryID + local CountryName = _DATABASE.COUNTRY_NAME[CountryID] + + StaticTemplate.x = Coordinate.x + StaticTemplate.y = Coordinate.z + + StaticTemplate.units = nil + StaticTemplate.route = nil + StaticTemplate.groupId = nil + + StaticTemplate.heading = Heading and ( ( Heading / 180 ) * math.pi ) or StaticTemplate.heading + + StaticTemplate.CountryID = nil + StaticTemplate.CoalitionID = nil + StaticTemplate.CategoryID = nil + + local Static = coalition.addStaticObject( CountryID, StaticTemplate ) + + return Static + end + + return nil +end + + --- Creates a new @{Static} from a @{Zone}. -- @param #SPAWNSTATIC self -- @param Core.Zone#ZONE_BASE Zone The Zone where to spawn the static. diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index e8ae381a1..4a4677053 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -597,7 +597,9 @@ function TASK:UnAssignFromUnit( TaskUnit ) self:F( TaskUnit:GetName() ) self:RemoveStateMachine( TaskUnit ) - + + -- If a Task Control Menu had been set, then this will be removed. + self:RemoveTaskControlMenu( TaskUnit ) return self end @@ -779,15 +781,16 @@ function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime ) local TaskText = string.format( "%s%s", self:GetName(), TaskPlayerString ) --, TaskThreatLevelString ) local TaskName = string.format( "%s", self:GetName() ) - local MissionMenu = Mission:GetMenu( TaskGroup ) --- local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ):SetTime( MenuTime ) --- local MissionMenu = Mission:GetMenu( TaskGroup ) - - self.MenuAssigned = self.MenuAssigned or {} - self.MenuAssigned[TaskGroup] = MENU_GROUP_DELAYED:New( TaskGroup, string.format( "Assigned Task %s", TaskName ), MissionMenu ):SetTime( MenuTime ):SetTag( "Tasking" ) - local TaskMenu = MENU_GROUP_COMMAND_DELAYED:New( TaskGroup, string.format( "Abort Task" ), self.MenuAssigned[TaskGroup], self.MenuTaskAbort, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ) - local MarkMenu = MENU_GROUP_COMMAND_DELAYED:New( TaskGroup, string.format( "Mark Task Location on Map" ), self.MenuAssigned[TaskGroup], self.MenuMarkToGroup, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ) - local TaskTypeMenu = MENU_GROUP_COMMAND_DELAYED:New( TaskGroup, string.format( "Report Task Details" ), self.MenuAssigned[TaskGroup], self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ) + for UnitName, TaskUnit in pairs( TaskGroup:GetUnits() ) do + local TaskUnit = TaskUnit -- Wrapper.Unit#UNIT + if TaskUnit then + local MenuControl = self:GetTaskControlMenu( TaskUnit ) + local TaskControl = MENU_GROUP:New( TaskGroup, "Control Task", MenuControl ):SetTime( MenuTime ):SetTag( "Tasking" ) + local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Abort Task" ), TaskControl, self.MenuTaskAbort, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ) + local MarkMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Mark Task Location on Map" ), TaskControl, self.MenuMarkToGroup, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ) + local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Report Task Details" ), TaskControl, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ) + end + end return self end @@ -1595,3 +1598,62 @@ do -- Additional Task Scoring and Task Progress end end + +do -- Task Control Menu + + -- The Task Control Menu is a menu attached to the task at the main menu to quickly be able to do actions in the task. + -- The Task Control Menu can only be shown when the task is assigned to the player. + -- The Task Control Menu is linked to the process executing the task, so no task menu can be set to the main static task definition. + + --- Init Task Control Menu + -- @param #TASK self + -- @param Wrapper.Unit#UNIT TaskUnit The @{Unit} that contains a player. + -- @return Task Control Menu Refresh ID + function TASK:InitTaskControlMenu( TaskUnit ) + + self.TaskControlMenuTime = timer.getTime() + + return self.TaskControlMenuTime + end + + --- Get Task Control Menu + -- @param #TASK self + -- @param Wrapper.Unit#UNIT TaskUnit The @{Unit} that contains a player. + -- @return Core.Menu#MENU_GROUP TaskControlMenu The Task Control Menu + function TASK:GetTaskControlMenu( TaskUnit, TaskName ) + + TaskName = TaskName or "" + + if not self.TaskControlMenu then + self.TaskControlMenu = MENU_GROUP:New( TaskUnit:GetGroup(), "Assigned Task " .. TaskUnit:GetPlayerName() .. " - " .. self:GetName() .. " " .. TaskName ):SetTime( self.TaskControlMenuTime ) + else + self.TaskControlMenu:SetTime( self.TaskControlMenuTime ) + end + + return self.TaskControlMenu + end + + --- Remove Task Control Menu + -- @param #TASK self + -- @param Wrapper.Unit#UNIT TaskUnit The @{Unit} that contains a player. + function TASK:RemoveTaskControlMenu( TaskUnit ) + + if self.TaskControlMenu then + self.TaskControlMenu:Remove() + self.TaskControlMenu = nil + end + end + + --- Refresh Task Control Menu + -- @param #TASK self + -- @param Wrapper.Unit#UNIT TaskUnit The @{Unit} that contains a player. + -- @param MenuTime The refresh time that was used to refresh the Task Control Menu items. + -- @param MenuTag The tag. + function TASK:RefreshTaskControlMenu( TaskUnit, MenuTime, MenuTag ) + + if self.TaskControlMenu then + self.TaskControlMenu:Remove( MenuTime, MenuTag ) + end + end + +end diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 278354142..a6ab41fc3 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -257,17 +257,17 @@ do -- TASK_CARGO --- -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_CARGO#TASK_CARGO Task + -- @param #TASK_CARGO Task function Fsm:onafterSelectAction( TaskUnit, Task ) local TaskUnitName = TaskUnit:GetName() self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) - local MenuTime = timer.getTime() + local MenuTime = Task:InitTaskControlMenu( TaskUnit ) + + local MenuControl = Task:GetTaskControlMenu( TaskUnit ) - TaskUnit.Menu = MENU_GROUP:New( TaskUnit:GetGroup(), Task:GetName() .. " @ " .. TaskUnit:GetName() ) - local CargoItemCount = TaskUnit:CargoItemCount() --Task:GetMission():GetCommandCenter():MessageToGroup( "Cargo in carrier: " .. CargoItemCount, TaskUnit:GetGroup() ) @@ -284,7 +284,7 @@ do -- TASK_CARGO -- MENU_GROUP_COMMAND:New( -- TaskUnit:GetGroup(), -- "Cancel Route " .. Cargo.Name, --- TaskUnit.Menu, +-- MenuControl, -- self.MenuRouteToPickupCancel, -- self, -- Cargo @@ -294,6 +294,8 @@ do -- TASK_CARGO self:F( { CargoUnloaded = Cargo:IsUnLoaded(), CargoLoaded = Cargo:IsLoaded(), CargoItemCount = CargoItemCount } ) Task:E( { TaskDeployZones = Task.DeployZones, TaskName = Task:GetName() } ) + local TaskGroup = TaskUnit:GetGroup() + if Cargo:IsUnLoaded() then if CargoItemCount < 1 then if Cargo:IsInReportRadius( TaskUnit:GetPointVec2() ) then @@ -308,7 +310,8 @@ do -- TASK_CARGO if Cargo:CanBoard() == true then if Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then Cargo:Report( "ready for boarding at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "board", TaskUnit:GetGroup() ) - MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Board cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuBoardCargo, self, Cargo ):SetTime(MenuTime) + local BoardMenu = MENU_GROUP:New( TaskGroup, "Board cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" ) + MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, BoardMenu, self.MenuBoardCargo, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() else Cargo:Report( "Board at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "reporting", TaskUnit:GetGroup() ) end @@ -316,7 +319,8 @@ do -- TASK_CARGO if Cargo:CanLoad() == true then if Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then Cargo:Report( "ready for loading at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "load", TaskUnit:GetGroup() ) - MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Load cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuLoadCargo, self, Cargo ):SetTime(MenuTime) + local LoadMenu = MENU_GROUP:New( TaskGroup, "Load cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" ) + MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, LoadMenu, self.MenuLoadCargo, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() else Cargo:Report( "Load at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "reporting", TaskUnit:GetGroup() ) end @@ -330,17 +334,14 @@ do -- TASK_CARGO end end end - TaskUnit.Menu:SetTime( MenuTime ) else Cargo:ReportResetAll( TaskUnit:GetGroup() ) end end else - if self:Is( "RoutingToPickup" ) then - else - self:F("route menu set") - MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Route to Pickup cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuRouteToPickup, self, Cargo ):SetTime(MenuTime) - TaskUnit.Menu:SetTime( MenuTime ) + if not Cargo:IsDeployed() == true then + local RouteToPickupMenu = MENU_GROUP:New( TaskGroup, "Route to pickup cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" ) + MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, RouteToPickupMenu, self.MenuRouteToPickup, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() Cargo:ReportResetAll( TaskUnit:GetGroup() ) end end @@ -367,20 +368,22 @@ do -- TASK_CARGO if Cargo:IsLoaded() == true and Cargo:IsLoadedInCarrier( TaskUnit ) == true then if not TaskUnit:InAir() then if Cargo:CanUnboard() == true then - MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Unboard cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuUnboardCargo, self, Cargo ):SetTime(MenuTime) + local UnboardMenu = MENU_GROUP:New( TaskGroup, "Unboard cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" ) + MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, UnboardMenu, self.MenuUnboardCargo, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() else if Cargo:CanUnload() == true then - MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Unload cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuUnloadCargo, self, Cargo ):SetTime(MenuTime) + local UnloadMenu = MENU_GROUP:New( TaskGroup, "Unload cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" ) + MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, UnloadMenu, self.MenuUnloadCargo, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() end end - TaskUnit.Menu:SetTime( MenuTime ) end - -- Deployzones are optional zones that can be selected to request routing information. - for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do - if not Cargo:IsInZone( DeployZone ) then - MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Route to Deploy cargo at " .. DeployZoneName, TaskUnit.Menu, self.MenuRouteToDeploy, self, DeployZone ):SetTime(MenuTime) - TaskUnit.Menu:SetTime( MenuTime ) - end + end + + -- Deployzones are optional zones that can be selected to request routing information. + for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do + if not Cargo:IsInZone( DeployZone ) then + local RouteToDeployMenu = MENU_GROUP:New( TaskGroup, "Route to deploy cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" ) + MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Zone " .. DeployZoneName, RouteToDeployMenu, self.MenuRouteToDeploy, self, DeployZone ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() end end end @@ -388,8 +391,7 @@ do -- TASK_CARGO end ) - TaskUnit.Menu:Remove( MenuTime ) - + Task:RefreshTaskControlMenu( TaskUnit, MenuTime, "Cargo" ) self:__SelectAction( -1 ) @@ -399,11 +401,13 @@ do -- TASK_CARGO --- -- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit - -- @param Tasking.Task_Cargo#TASK_CARGO Task + -- @param #TASK_CARGO Task function Fsm:OnLeaveWaitingForCommand( TaskUnit, Task ) self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) - TaskUnit.Menu:Remove() + --local MenuControl = Task:GetTaskControlMenu( TaskUnit ) + + --MenuControl:Remove() end function Fsm:MenuBoardCargo( Cargo ) @@ -820,12 +824,15 @@ do -- TASK_CARGO self:F({Cargo, TaskUnit}) local ProcessUnit = self:GetUnitProcess( TaskUnit ) + + local MenuTime = self:InitTaskControlMenu( TaskUnit ) + local MenuControl = self:GetTaskControlMenu( TaskUnit ) local ActRouteCargo = ProcessUnit:GetProcess( "RoutingToPickup", "RouteToPickupPoint" ) -- Actions.Act_Route#ACT_ROUTE_POINT ActRouteCargo:Reset() ActRouteCargo:SetCoordinate( Cargo:GetCoordinate() ) ActRouteCargo:SetRange( Cargo:GetLoadRadius() ) - ActRouteCargo:SetMenuCancel( TaskUnit:GetGroup(), "Cancel Routing to Cargo " .. Cargo:GetName(), TaskUnit.Menu ) + ActRouteCargo:SetMenuCancel( TaskUnit:GetGroup(), "Cancel Routing to Cargo " .. Cargo:GetName(), MenuControl, MenuTime, "Cargo" ) ActRouteCargo:Start() return self @@ -840,10 +847,13 @@ do -- TASK_CARGO local ProcessUnit = self:GetUnitProcess( TaskUnit ) + local MenuTime = self:InitTaskControlMenu( TaskUnit ) + local MenuControl = self:GetTaskControlMenu( TaskUnit ) + local ActRouteDeployZone = ProcessUnit:GetProcess( "RoutingToDeploy", "RouteToDeployZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE ActRouteDeployZone:Reset() ActRouteDeployZone:SetZone( DeployZone ) - ActRouteDeployZone:SetMenuCancel( TaskUnit:GetGroup(), "Cancel Routing to Deploy Zone" .. DeployZone:GetName(), TaskUnit.Menu ) + ActRouteDeployZone:SetMenuCancel( TaskUnit:GetGroup(), "Cancel Routing to Deploy Zone" .. DeployZone:GetName(), MenuControl, MenuTime, "Cargo" ) ActRouteDeployZone:Start() return self @@ -970,6 +980,8 @@ do -- TASK_CARGO return 0 end + + end diff --git a/Moose Development/Moose/Wrapper/Static.lua b/Moose Development/Moose/Wrapper/Static.lua index 6479037a9..2e5318f68 100644 --- a/Moose Development/Moose/Wrapper/Static.lua +++ b/Moose Development/Moose/Wrapper/Static.lua @@ -130,12 +130,21 @@ end -- @param #UNIT self -- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static. -- @param #number Heading The heading of the unit respawn. -function STATIC:ReSpawn( Coordinate, Heading ) +function STATIC:SpawnAt( Coordinate, Heading ) - - -- todo: need to fix country local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName ) SpawnStatic:SpawnFromPointVec2( Coordinate, Heading, self.StaticName ) end + +--- Respawn the @{Unit} using a (tweaked) template of the parent Group. +-- @param #UNIT self +-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static. +-- @param #number Heading The heading of the unit respawn. +function STATIC:ReSpawn() + + local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName ) + + SpawnStatic:ReSpawn() +end diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index ed536a603..fe4b8cca0 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -189,9 +189,9 @@ end -- * Then it will respawn the re-modelled group. -- -- @param #UNIT self --- @param Dcs.DCSTypes#Vec3 SpawnVec3 The position where to Spawn the new Unit at. +-- @param Core.Point#COORDINATE Coordinate The position where to Spawn the new Unit at. -- @param #number Heading The heading of the unit respawn. -function UNIT:ReSpawn( SpawnVec3, Heading ) +function UNIT:ReSpawnAt( Coordinate, Heading ) self:T( self:Name() ) local SpawnGroupTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplateFromUnitName( self:Name() ) ) @@ -203,8 +203,8 @@ function UNIT:ReSpawn( SpawnVec3, Heading ) if SpawnGroup then local Vec3 = SpawnGroup:GetVec3() - SpawnGroupTemplate.x = SpawnVec3.x - SpawnGroupTemplate.y = SpawnVec3.z + SpawnGroupTemplate.x = Coordinate.x + SpawnGroupTemplate.y = Coordinate.z self:F( #SpawnGroupTemplate.units ) for UnitID, UnitData in pairs( SpawnGroup:GetUnits() ) do @@ -227,9 +227,9 @@ function UNIT:ReSpawn( SpawnVec3, Heading ) SpawnGroupTemplate.units[UnitTemplateID].unitId = nil if UnitTemplateData.name == self:Name() then self:T("Adjusting") - SpawnGroupTemplate.units[UnitTemplateID].alt = SpawnVec3.y - SpawnGroupTemplate.units[UnitTemplateID].x = SpawnVec3.x - SpawnGroupTemplate.units[UnitTemplateID].y = SpawnVec3.z + SpawnGroupTemplate.units[UnitTemplateID].alt = Coordinate.y + SpawnGroupTemplate.units[UnitTemplateID].x = Coordinate.x + SpawnGroupTemplate.units[UnitTemplateID].y = Coordinate.z SpawnGroupTemplate.units[UnitTemplateID].heading = Heading self:F( { UnitTemplateID, SpawnGroupTemplate.units[UnitTemplateID], SpawnGroupTemplate.units[UnitTemplateID] } ) else @@ -344,18 +344,18 @@ end function UNIT:GetPlayerName() self:F2( self.UnitName ) - local DCSUnit = self:GetDCSObject() + local DCSUnit = self:GetDCSObject() -- Dcs.DCSUnit#Unit if DCSUnit then local PlayerName = DCSUnit:getPlayerName() - if PlayerName == nil then - PlayerName = "" + if PlayerName == nil or PlayerName == "" then + PlayerName = "Player" .. DCSUnit:getID() end return PlayerName end - return nil + return nil end From 1ae1b9abd72b605c8cb7640dbdfaed160a9697ac Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Wed, 11 Apr 2018 09:14:12 +0200 Subject: [PATCH 046/420] Ready for another test session. --- Moose Development/Moose/Cargo/Cargo.lua | 13 +++++-- Moose Development/Moose/Cargo/CargoCrate.lua | 29 ++++++++++++-- Moose Development/Moose/Cargo/CargoGroup.lua | 20 ++++++++++ .../Moose/Cargo/CargoSlingload.lua | 24 +++++++++++- Moose Development/Moose/Cargo/CargoUnit.lua | 18 +++++++++ Moose Development/Moose/Core/Event.lua | 14 ++++++- Moose Development/Moose/Core/Set.lua | 38 +++++++++++++++---- Moose Development/Moose/Core/SpawnStatic.lua | 5 --- Moose Development/Moose/Tasking/Task.lua | 2 +- Moose Development/Moose/Tasking/TaskInfo.lua | 12 +++--- .../Moose/Tasking/Task_CARGO.lua | 21 +++++----- Moose Development/Moose/Wrapper/Object.lua | 4 +- Moose Development/Moose/Wrapper/Static.lua | 17 +++++++-- 13 files changed, 172 insertions(+), 45 deletions(-) diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index b7f52ee4e..6e327bf5a 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -268,8 +268,7 @@ do -- CARGO self.Slingloadable = false self.Moveable = false self.Containable = false - self.LoadAction = "" - + self.CargoLimit = 0 self.LoadRadius = LoadRadius or 500 @@ -369,6 +368,14 @@ do -- CARGO end + --- Get the transportation method of the Cargo. + -- @param #CARGO self + -- @return #string The transportation method of the Cargo. + function CARGO:GetTransportationMethod() + return self.TransportationMethod + end + + --- Get the coalition of the Cargo. -- @param #CARGO self -- @return Coalition @@ -802,7 +809,7 @@ do -- CARGO_REPRESENTABLE -- Cargo objects are deleted from the _DATABASE and SET_CARGO objects. self:F( { CargoName = self:GetName() } ) - _EVENTDISPATCHER:CreateEventDeleteCargo( self ) + --_EVENTDISPATCHER:CreateEventDeleteCargo( self ) return self end diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua index 104e26438..89bd95c6d 100644 --- a/Moose Development/Moose/Cargo/CargoCrate.lua +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -49,7 +49,7 @@ do -- CARGO_CRATE local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoStatic, Type, Name, nil, LoadRadius, NearRadius ) ) -- #CARGO_CRATE self:F( { Type, Name, NearRadius } ) - self.CargoObject = CargoStatic + self.CargoObject = CargoStatic -- Wrapper.Static#STATIC self:T( self.ClassName ) @@ -73,7 +73,9 @@ do -- CARGO_CRATE if self:IsDestroyed() or self:IsUnLoaded() or self:IsBoarding() then if self.CargoObject:GetName() == EventData.IniUnitName then - Destroyed = true + if not self.NoDestroy then + Destroyed = true + end end else if self:IsLoaded() then @@ -90,7 +92,7 @@ do -- CARGO_CRATE self:I( { "Cargo crate destroyed: " .. self.CargoObject:GetName() } ) self:Destroyed() end - + end @@ -145,7 +147,10 @@ do -- CARGO_CRATE -- Only destroy the CargoObject is if there is a CargoObject (packages don't have CargoObjects). if self.CargoObject then self:T("Destroying") + self.NoDestroy = true self.CargoObject:Destroy() + --local Coordinate = self.CargoObject:GetCoordinate():GetRandomCoordinateInRadius( 50, 20 ) + --self.CargoObject:ReSpawnAt( Coordinate, 0 ) end end @@ -290,6 +295,24 @@ do -- CARGO_CRATE end + + --- Get the transportation method of the Cargo. + -- @param #CARGO_CRATE self + -- @return #string The transportation method of the Cargo. + function CARGO_CRATE:GetTransportationMethod() + if self:IsLoaded() then + return "for unloading" + else + if self:IsUnLoaded() then + return "for loading" + else + if self:IsDeployed() then + return "delivered" + end + end + end + return "" + end end diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 4ad076362..32dd2a886 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -584,4 +584,24 @@ do -- CARGO_GROUP end + --- Get the transportation method of the Cargo. + -- @param #CARGO_GROUP self + -- @return #string The transportation method of the Cargo. + function CARGO_GROUP:GetTransportationMethod() + if self:IsLoaded() then + return "for unboarding" + else + if self:IsUnLoaded() then + return "for boarding" + else + if self:IsDeployed() then + return "delivered" + end + end + end + return "" + end + + + end -- CARGO_GROUP diff --git a/Moose Development/Moose/Cargo/CargoSlingload.lua b/Moose Development/Moose/Cargo/CargoSlingload.lua index 04626385b..e8ed5d5cb 100644 --- a/Moose Development/Moose/Cargo/CargoSlingload.lua +++ b/Moose Development/Moose/Cargo/CargoSlingload.lua @@ -74,7 +74,9 @@ do -- CARGO_SLINGLOAD if self:IsDestroyed() or self:IsUnLoaded() then if self.CargoObject:GetName() == EventData.IniUnitName then - Destroyed = true + if not self.NoDestroy then + Destroyed = true + end end end @@ -247,5 +249,23 @@ do -- CARGO_SLINGLOAD end - + + --- Get the transportation method of the Cargo. + -- @param #CARGO_SLINGLOAD self + -- @return #string The transportation method of the Cargo. + function CARGO_SLINGLOAD:GetTransportationMethod() + if self:IsLoaded() then + return "for sling loading" + else + if self:IsUnLoaded() then + return "for sling loading" + else + if self:IsDeployed() then + return "delivered" + end + end + end + return "" + end + end diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index 70a42b6c8..7086035bb 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -370,4 +370,22 @@ do -- CARGO_UNIT end end + --- Get the transportation method of the Cargo. + -- @param #CARGO_UNIT self + -- @return #string The transportation method of the Cargo. + function CARGO_UNIT:GetTransportationMethod() + if self:IsLoaded() then + return "for unboarding" + else + if self:IsUnLoaded() then + return "for boarding" + else + if self:IsDeployed() then + return "delivered" + end + end + end + return "" + end + end -- CARGO_UNIT diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index dd26c3bf6..b7004d42f 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -744,7 +744,7 @@ function EVENT:onEvent( Event ) local EventMeta = _EVENTMETA[Event.id] - --self:E( { EventMeta.Text, Event } ) -- Activate the see all incoming events ... + self:E( { EventMeta.Text, Event } ) -- Activate the see all incoming events ... if self and self.Events and @@ -888,7 +888,7 @@ function EVENT:onEvent( Event ) -- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called. for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do - + --if Event.IniObjectCategory ~= Object.Category.STATIC then -- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } ) --end @@ -1037,6 +1037,16 @@ function EVENT:onEvent( Event ) end end end + + -- When cargo was deleted, it may probably be because of an S_EVENT_DEAD. + -- However, in the loading logic, an S_EVENT_DEAD is also generated after a Destroy() call. + -- And this is a problem because it will remove all entries from the SET_CARGOs. + -- To prevent this from happening, the Cargo object has a flag NoDestroy. + -- When true, the SET_CARGO won't Remove the Cargo object from the set. + -- But we need to switch that flag off after the event handlers have been called. + if Event.id == EVENTS.DeleteCargo then + Event.Cargo.NoDestroy = nil + end else self:T( { EventMeta.Text, Event } ) end diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 48973c2b2..eb950115a 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -149,6 +149,7 @@ end -- @param #SET_BASE self -- @param #string ObjectName function SET_BASE:Remove( ObjectName ) + self:F( { ObjectName = ObjectName, Object = Object } ) local Object = self.Set[ObjectName] @@ -313,10 +314,6 @@ function SET_BASE:_FilterStart() end end - self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) - -- Follow alive players and clients --self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) --self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit ) @@ -935,6 +932,9 @@ function SET_GROUP:FilterStart() if _DATABASE then self:_FilterStart() + self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) + self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) + self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) end @@ -1643,6 +1643,9 @@ do -- SET_UNIT if _DATABASE then self:_FilterStart() + self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) + self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) + self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) end return self @@ -2559,6 +2562,9 @@ do -- SET_STATIC if _DATABASE then self:_FilterStart() + self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) + self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) + self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) end return self @@ -3200,6 +3206,9 @@ function SET_CLIENT:FilterStart() if _DATABASE then self:_FilterStart() + self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) + self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) + self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) end return self @@ -3599,6 +3608,9 @@ function SET_PLAYER:FilterStart() if _DATABASE then self:_FilterStart() + self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) + self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) + self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) end return self @@ -4270,10 +4282,9 @@ function SET_CARGO:FilterStart() --R2.1 if _DATABASE then self:_FilterStart() + self:HandleEvent( EVENTS.NewCargo ) + self:HandleEvent( EVENTS.DeleteCargo ) end - - self:HandleEvent( EVENTS.NewCargo ) - self:HandleEvent( EVENTS.DeleteCargo ) return self end @@ -4405,7 +4416,18 @@ function SET_CARGO:OnEventDeleteCargo( EventData ) --R2.1 if EventData.Cargo then local Cargo = _DATABASE:FindCargo( EventData.Cargo.Name ) if Cargo and Cargo.Name then - self:Remove( Cargo.Name ) + + -- When cargo was deleted, it may probably be because of an S_EVENT_DEAD. + -- However, in the loading logic, an S_EVENT_DEAD is also generated after a Destroy() call. + -- And this is a problem because it will remove all entries from the SET_CARGOs. + -- To prevent this from happening, the Cargo object has a flag NoDestroy. + -- When true, the SET_CARGO won't Remove the Cargo object from the set. + -- This flag is switched off after the event handlers have been called in the EVENT class. + self:F( { CargoNoDestroy=Cargo.NoDestroy } ) + if Cargo.NoDestroy then + else + self:Remove( Cargo.Name ) + end end end end diff --git a/Moose Development/Moose/Core/SpawnStatic.lua b/Moose Development/Moose/Core/SpawnStatic.lua index 1f5b8a97a..8019902af 100644 --- a/Moose Development/Moose/Core/SpawnStatic.lua +++ b/Moose Development/Moose/Core/SpawnStatic.lua @@ -238,15 +238,10 @@ function SPAWNSTATIC:ReSpawnAt( Coordinate, Heading ) if StaticTemplate then local CountryID = self.CountryID - local CountryName = _DATABASE.COUNTRY_NAME[CountryID] StaticTemplate.x = Coordinate.x StaticTemplate.y = Coordinate.z - StaticTemplate.units = nil - StaticTemplate.route = nil - StaticTemplate.groupId = nil - StaticTemplate.heading = Heading and ( ( Heading / 180 ) * math.pi ) or StaticTemplate.heading StaticTemplate.CountryID = nil diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index 4a4677053..e31cb0c6b 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -1502,7 +1502,7 @@ function TASK:ReportDetails( ReportGroup ) local PlayerReport = REPORT:New() for PlayerName, PlayerGroup in pairs( PlayerNames ) do - PlayerReport:Add( "Group " .. PlayerGroup:GetCallsign() .. ": " .. PlayerName ) + PlayerReport:Add( "Players group " .. PlayerGroup:GetCallsign() .. ": " .. PlayerName ) end local Players = PlayerReport:Text() diff --git a/Moose Development/Moose/Tasking/TaskInfo.lua b/Moose Development/Moose/Tasking/TaskInfo.lua index bf75bcf5a..2a3c150c3 100644 --- a/Moose Development/Moose/Tasking/TaskInfo.lua +++ b/Moose Development/Moose/Tasking/TaskInfo.lua @@ -249,17 +249,15 @@ end function TASKINFO:AddCargoSet( SetCargo, Order, Detail, Keep ) local CargoReport = REPORT:New() + CargoReport:Add( "" ) SetCargo:ForEachCargo( - --- @param Core.Cargo#CARGO Cargo + --- @param Cargo.Cargo#CARGO Cargo function( Cargo ) - local CargoType = Cargo:GetType() - local CargoName = Cargo:GetName() - local CargoCoordinate = Cargo:GetCoordinate() - CargoReport:Add( string.format( '"%s" (%s) at %s', CargoName, CargoType, CargoCoordinate:ToStringMGRS() ) ) + CargoReport:Add( string.format( ' - %s (%s) %s - status %s ', Cargo:GetName(), Cargo:GetType(), Cargo:GetTransportationMethod(), Cargo:GetCurrentState() ) ) end ) - self:AddInfo( "CargoSet", CargoReport:Text(), Order, Detail, Keep ) + self:AddInfo( "Cargo", CargoReport:Text(), Order, Detail, Keep ) return self end @@ -319,7 +317,7 @@ function TASKINFO:Report( Report, Detail, ReportGroup ) local Coordinate = Data.Data -- Core.Point#COORDINATE Text = Coordinate:ToStringWind( ReportGroup:GetUnit(1), nil, self ) end - if Key == "CargoSet" then + if Key == "Cargo" then local DataText = Data.Data -- #string Text = DataText end diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index a6ab41fc3..970da6af5 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -309,27 +309,27 @@ do -- TASK_CARGO if not TaskUnit:InAir() then if Cargo:CanBoard() == true then if Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then - Cargo:Report( "ready for boarding at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "board", TaskUnit:GetGroup() ) + Cargo:Report( "Ready for boarding.", "board", TaskUnit:GetGroup() ) local BoardMenu = MENU_GROUP:New( TaskGroup, "Board cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" ) MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, BoardMenu, self.MenuBoardCargo, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() else - Cargo:Report( "Board at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "reporting", TaskUnit:GetGroup() ) + Cargo:Report( "Board at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() .. "." ), "reporting", TaskUnit:GetGroup() ) end else if Cargo:CanLoad() == true then if Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then - Cargo:Report( "ready for loading at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "load", TaskUnit:GetGroup() ) + Cargo:Report( "Ready for loading.", "load", TaskUnit:GetGroup() ) local LoadMenu = MENU_GROUP:New( TaskGroup, "Load cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" ) MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, LoadMenu, self.MenuLoadCargo, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() else - Cargo:Report( "Load at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "reporting", TaskUnit:GetGroup() ) + Cargo:Report( "Load at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ) .. " within " .. Cargo.NearRadius .. ".", "reporting", TaskUnit:GetGroup() ) end else if Cargo:CanSlingload() == true then if Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then - Cargo:Report( "ready for slingloading at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "slingload", TaskUnit:GetGroup() ) + Cargo:Report( "Ready for slingloading.", "slingload", TaskUnit:GetGroup() ) else - Cargo:Report( "Slingload at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ), "reporting", TaskUnit:GetGroup() ) + Cargo:Report( "Slingload at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() .. "." ), "reporting", TaskUnit:GetGroup() ) end end end @@ -643,7 +643,7 @@ do -- TASK_CARGO local TaskUnitName = TaskUnit:GetName() self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) - Cargo:MessageToGroup( "Boarded ...", TaskUnit:GetGroup() ) + Cargo:MessageToGroup( "Boarded cargo " .. Cargo:GetName(), TaskUnit:GetGroup() ) self:__Load( -0.1, Cargo ) @@ -662,7 +662,7 @@ do -- TASK_CARGO Cargo:Load( TaskUnit ) end - Cargo:MessageToGroup( "Loaded ...", TaskUnit:GetGroup() ) + Cargo:MessageToGroup( "Loaded cargo " .. Cargo:GetName(), TaskUnit:GetGroup() ) TaskUnit:AddCargo( Cargo ) Task:CargoPickedUp( TaskUnit, Cargo ) @@ -736,7 +736,7 @@ do -- TASK_CARGO local TaskUnitName = TaskUnit:GetName() self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) - self.Cargo:MessageToGroup( "UnBoarded ...", TaskUnit:GetGroup() ) + self.Cargo:MessageToGroup( "UnBoarded cargo " .. self.Cargo:GetName(), TaskUnit:GetGroup() ) self:Unload( self.Cargo ) end @@ -759,6 +759,8 @@ do -- TASK_CARGO end TaskUnit:RemoveCargo( Cargo ) + Cargo:MessageToGroup( "Unloaded cargo " .. Cargo:GetName(), TaskUnit:GetGroup() ) + self:Planned() self:__SelectAction( 1 ) end @@ -970,7 +972,6 @@ do -- TASK_CARGO function TASK_CARGO:UpdateTaskInfo( DetectedItem ) if self:IsStatePlanned() or self:IsStateAssigned() then - self.TaskInfo:AddTaskName( 0, "MSOD" ) self.TaskInfo:AddCargoSet( self.SetCargo, 10, "SOD", true ) end end diff --git a/Moose Development/Moose/Wrapper/Object.lua b/Moose Development/Moose/Wrapper/Object.lua index 29901fd4b..656031e78 100644 --- a/Moose Development/Moose/Wrapper/Object.lua +++ b/Moose Development/Moose/Wrapper/Object.lua @@ -73,6 +73,7 @@ end --- Destroys the OBJECT. -- @param #OBJECT self +-- @return #boolean true if the object is destroyed. -- @return #nil The DCS Unit is not existing or alive. function OBJECT:Destroy() @@ -80,7 +81,8 @@ function OBJECT:Destroy() if DCSObject then --BASE:CreateEventCrash( timer.getTime(), DCSObject ) - DCSObject:destroy() + DCSObject:destroy( false ) + return true end BASE:E( { "Cannot Destroy", Name = self.ObjectName, Class = self:GetClassName() } ) diff --git a/Moose Development/Moose/Wrapper/Static.lua b/Moose Development/Moose/Wrapper/Static.lua index 2e5318f68..ad53a16dd 100644 --- a/Moose Development/Moose/Wrapper/Static.lua +++ b/Moose Development/Moose/Wrapper/Static.lua @@ -138,13 +138,24 @@ function STATIC:SpawnAt( Coordinate, Heading ) end ---- Respawn the @{Unit} using a (tweaked) template of the parent Group. +--- Respawn the @{Unit} at the same location with the same properties. +-- This is useful to respawn a cargo after it has been destroyed. -- @param #UNIT self --- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static. --- @param #number Heading The heading of the unit respawn. function STATIC:ReSpawn() local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName ) SpawnStatic:ReSpawn() end + + +--- Respawn the @{Unit} at a defined Coordinate with an optional heading. +-- @param #UNIT self +-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static. +-- @param #number Heading The heading of the unit respawn. +function STATIC:ReSpawnAt( Coordinate, Heading ) + + local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName ) + + SpawnStatic:ReSpawnAt( Coordinate, Heading ) +end From faa934ffce8ca95a77a421be7853149d54837a56 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Wed, 11 Apr 2018 17:00:11 +0200 Subject: [PATCH 047/420] Fixed stuff for multi player --- Moose Development/Moose/Cargo/Cargo.lua | 6 +++--- Moose Development/Moose/Cargo/CargoCrate.lua | 16 +++++++--------- Moose Development/Moose/Cargo/CargoUnit.lua | 4 ++-- Moose Development/Moose/Core/Menu.lua | 2 +- Moose Development/Moose/Tasking/Task.lua | 6 +----- Moose Development/Moose/Tasking/Task_CARGO.lua | 2 +- Moose Development/Moose/Wrapper/Positionable.lua | 2 +- 7 files changed, 16 insertions(+), 22 deletions(-) diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 6e327bf5a..199f211a2 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -610,7 +610,7 @@ do -- CARGO -- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). -- @return #boolean function CARGO:IsNear( PointVec2, NearRadius ) - self:F2( { PointVec2 = PointVec2, NearRadius = NearRadius } ) + --self:F2( { PointVec2 = PointVec2, NearRadius = NearRadius } ) if self.CargoObject:IsAlive() then --local Distance = PointVec2:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) @@ -621,12 +621,12 @@ do -- CARGO --self:F( Distance ) if Distance <= NearRadius then - self:F( { PointVec2 = PointVec2, NearRadius = NearRadius, IsNear = true } ) + --self:F( { PointVec2 = PointVec2, NearRadius = NearRadius, IsNear = true } ) return true end end - self:F( { PointVec2 = PointVec2, NearRadius = NearRadius, IsNear = false } ) + --self:F( { PointVec2 = PointVec2, NearRadius = NearRadius, IsNear = false } ) return false end diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua index 89bd95c6d..18c49a4db 100644 --- a/Moose Development/Moose/Cargo/CargoCrate.lua +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -51,8 +51,6 @@ do -- CARGO_CRATE self.CargoObject = CargoStatic -- Wrapper.Static#STATIC - self:T( self.ClassName ) - -- Cargo objects are added to the _DATABASE and SET_CARGO objects. _EVENTDISPATCHER:CreateEventNewCargo( self ) @@ -103,7 +101,7 @@ do -- CARGO_CRATE -- @param #string To -- @param Core.Point#POINT_VEC2 function CARGO_CRATE:onenterUnLoaded( From, Event, To, ToPointVec2 ) - self:F( { ToPointVec2, From, Event, To } ) + --self:F( { ToPointVec2, From, Event, To } ) local Angle = 180 local Speed = 10 @@ -140,7 +138,7 @@ do -- CARGO_CRATE -- @param #string To -- @param Wrapper.Unit#UNIT CargoCarrier function CARGO_CRATE:onenterLoaded( From, Event, To, CargoCarrier ) - self:F( { From, Event, To, CargoCarrier } ) + --self:F( { From, Event, To, CargoCarrier } ) self.CargoCarrier = CargoCarrier @@ -171,12 +169,12 @@ do -- CARGO_CRATE -- @param Core.Point#Coordinate Coordinate -- @return #boolean true if the Cargo Crate is within the report radius. function CARGO_CRATE:IsInReportRadius( Coordinate ) - self:F( { Coordinate, LoadRadius = self.LoadRadius } ) + --self:F( { Coordinate, LoadRadius = self.LoadRadius } ) local Distance = 0 if self:IsUnLoaded() then Distance = Coordinate:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) - self:T( Distance ) + --self:T( Distance ) if Distance <= self.LoadRadius then return true end @@ -191,12 +189,12 @@ do -- CARGO_CRATE -- @param Core.Point#Coordinate Coordinate -- @return #boolean true if the Cargo Crate is within the loading radius. function CARGO_CRATE:IsInLoadRadius( Coordinate ) - self:F( { Coordinate, LoadRadius = self.NearRadius } ) + --self:F( { Coordinate, LoadRadius = self.NearRadius } ) local Distance = 0 if self:IsUnLoaded() then Distance = Coordinate:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) - self:T( Distance ) + --self:T( Distance ) if Distance <= self.NearRadius then return true end @@ -212,7 +210,7 @@ do -- CARGO_CRATE -- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup. -- @return #nil There is no valid Cargo in the CargoGroup. function CARGO_CRATE:GetCoordinate() - self:F() + --self:F() return self.CargoObject:GetCoordinate() end diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index 7086035bb..318852cb1 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -285,7 +285,7 @@ do -- CARGO_UNIT -- @param Wrapper.Client#CLIENT CargoCarrier -- @param #number NearRadius function CARGO_UNIT:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { From, Event, To, CargoCarrier.UnitName, NearRadius } ) + --self:F( { From, Event, To, CargoCarrier.UnitName, NearRadius } ) if CargoCarrier and CargoCarrier:IsAlive() and self.CargoObject and self.CargoObject:IsAlive() then @@ -338,7 +338,7 @@ do -- CARGO_UNIT -- @param #string To -- @param Wrapper.Unit#UNIT CargoCarrier function CARGO_UNIT:onenterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { From, Event, To, CargoCarrier.UnitName, NearRadius } ) + --self:F( { From, Event, To, CargoCarrier.UnitName, NearRadius } ) local Speed = 90 local Angle = 180 diff --git a/Moose Development/Moose/Core/Menu.lua b/Moose Development/Moose/Core/Menu.lua index 5ee9f9fff..a435f16cb 100644 --- a/Moose Development/Moose/Core/Menu.lua +++ b/Moose Development/Moose/Core/Menu.lua @@ -246,7 +246,7 @@ do -- MENU_BASE -- @param #boolean RemoveParent If true, the parent menu is automatically removed when this menu is the last child menu of that parent @{Menu}. -- @return #MENU_BASE function MENU_BASE:SetRemoveParent( RemoveParent ) - self:F( { RemoveParent } ) + --self:F( { RemoveParent } ) self.MenuRemoveParent = RemoveParent return self end diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index e31cb0c6b..d1dde22ed 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -1624,11 +1624,7 @@ do -- Task Control Menu TaskName = TaskName or "" - if not self.TaskControlMenu then - self.TaskControlMenu = MENU_GROUP:New( TaskUnit:GetGroup(), "Assigned Task " .. TaskUnit:GetPlayerName() .. " - " .. self:GetName() .. " " .. TaskName ):SetTime( self.TaskControlMenuTime ) - else - self.TaskControlMenu:SetTime( self.TaskControlMenuTime ) - end + self.TaskControlMenu = MENU_GROUP:New( TaskUnit:GetGroup(), "Assigned Task " .. TaskUnit:GetPlayerName() .. " - " .. self:GetName() .. " " .. TaskName ):SetTime( self.TaskControlMenuTime ) return self.TaskControlMenu end diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 970da6af5..4f1473e7f 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -329,7 +329,7 @@ do -- TASK_CARGO if Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then Cargo:Report( "Ready for slingloading.", "slingload", TaskUnit:GetGroup() ) else - Cargo:Report( "Slingload at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() .. "." ), "reporting", TaskUnit:GetGroup() ) + Cargo:Report( "Slingload at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ) .. ".", "reporting", TaskUnit:GetGroup() ) end end end diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index b36a2d88b..b28bb5933 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -129,7 +129,7 @@ function POSITIONABLE:GetPointVec2() local PositionablePointVec2 = POINT_VEC2:NewFromVec3( PositionableVec3 ) - self:T( PositionablePointVec2 ) + --self:F( PositionablePointVec2 ) return PositionablePointVec2 end From 9cea486fdc31bffc610eee3bb6d20290b6a9f5f6 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Wed, 11 Apr 2018 17:09:59 +0200 Subject: [PATCH 048/420] Progress --- Moose Development/Moose/Cargo/CargoSlingload.lua | 16 +++++++--------- Moose Development/Moose/Wrapper/Client.lua | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/Moose Development/Moose/Cargo/CargoSlingload.lua b/Moose Development/Moose/Cargo/CargoSlingload.lua index e8ed5d5cb..1cfa043f1 100644 --- a/Moose Development/Moose/Cargo/CargoSlingload.lua +++ b/Moose Development/Moose/Cargo/CargoSlingload.lua @@ -51,8 +51,6 @@ do -- CARGO_SLINGLOAD self.CargoObject = CargoStatic - self:T( self.ClassName ) - -- Cargo objects are added to the _DATABASE and SET_CARGO objects. _EVENTDISPATCHER:CreateEventNewCargo( self ) @@ -124,7 +122,7 @@ do -- CARGO_SLINGLOAD -- @param Core.Point#Coordinate Coordinate -- @return #boolean true if the Cargo Crate is within the report radius. function CARGO_SLINGLOAD:IsInReportRadius( Coordinate ) - self:F( { Coordinate, LoadRadius = self.LoadRadius } ) + --self:F( { Coordinate, LoadRadius = self.LoadRadius } ) local Distance = 0 if self:IsUnLoaded() then @@ -144,7 +142,7 @@ do -- CARGO_SLINGLOAD -- @param Core.Point#Coordinate Coordinate -- @return #boolean true if the Cargo Slingload is within the loading radius. function CARGO_SLINGLOAD:IsInLoadRadius( Coordinate ) - self:F( { Coordinate } ) + --self:F( { Coordinate } ) local Distance = 0 if self:IsUnLoaded() then @@ -165,7 +163,7 @@ do -- CARGO_SLINGLOAD -- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup. -- @return #nil There is no valid Cargo in the CargoGroup. function CARGO_SLINGLOAD:GetCoordinate() - self:F() + --self:F() return self.CargoObject:GetCoordinate() end @@ -195,7 +193,7 @@ do -- CARGO_SLINGLOAD -- @param #CARGO_SLINGLOAD self -- @param Core.Point#COORDINATE Coordinate function CARGO_SLINGLOAD:RouteTo( Coordinate ) - self:F( {Coordinate = Coordinate } ) + --self:F( {Coordinate = Coordinate } ) end @@ -208,7 +206,7 @@ do -- CARGO_SLINGLOAD -- @return #boolean The Cargo is near to the Carrier. -- @return #nil The Cargo is not near to the Carrier. function CARGO_SLINGLOAD:IsNear( CargoCarrier, NearRadius ) - self:F( {NearRadius = NearRadius } ) + --self:F( {NearRadius = NearRadius } ) return self:IsNear( CargoCarrier:GetCoordinate(), NearRadius ) end @@ -218,7 +216,7 @@ do -- CARGO_SLINGLOAD -- @param #CARGO_SLINGLOAD self function CARGO_SLINGLOAD:Respawn() - self:F( { "Respawning slingload " .. self:GetName() } ) + --self:F( { "Respawning slingload " .. self:GetName() } ) -- Respawn the group... @@ -235,7 +233,7 @@ do -- CARGO_SLINGLOAD -- @param #CARGO_SLINGLOAD self function CARGO_SLINGLOAD:onafterReset() - self:F( { "Reset slingload " .. self:GetName() } ) + --self:F( { "Reset slingload " .. self:GetName() } ) -- Respawn the group... diff --git a/Moose Development/Moose/Wrapper/Client.lua b/Moose Development/Moose/Wrapper/Client.lua index dcf44d8d8..0d98f5092 100644 --- a/Moose Development/Moose/Wrapper/Client.lua +++ b/Moose Development/Moose/Wrapper/Client.lua @@ -181,8 +181,8 @@ function CLIENT:ShowBriefing() local Briefing = "" if self.ClientBriefing then Briefing = Briefing .. self.ClientBriefing + self:Message( Briefing, 60, "Briefing" ) end - self:Message( Briefing, 60, "Briefing" ) end return self From dcc42b8e62db9040331430c9236213cbe80963ca Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Wed, 11 Apr 2018 23:24:13 +0200 Subject: [PATCH 049/420] Fixes --- Moose Development/Moose/Cargo/Cargo.lua | 4 +-- Moose Development/Moose/Cargo/CargoGroup.lua | 28 +++++++++---------- .../Moose/Cargo/CargoSlingload.lua | 2 +- Moose Development/Moose/Core/Fsm.lua | 4 ++- Moose Development/Moose/Tasking/Task.lua | 13 +++++---- .../Moose/Tasking/Task_CARGO.lua | 4 +-- 6 files changed, 29 insertions(+), 26 deletions(-) diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 199f211a2..2cf5be78e 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -637,12 +637,12 @@ do -- CARGO -- @param Core.Zone#ZONE_BASE Zone -- @return #boolean **true** if cargo is in the Zone, **false** if cargo is not in the Zone. function CARGO:IsInZone( Zone ) - self:F( { Zone } ) + --self:F( { Zone } ) if self:IsLoaded() then return Zone:IsPointVec2InZone( self.CargoCarrier:GetPointVec2() ) else - self:F( { Size = self.CargoObject:GetSize(), Units = self.CargoObject:GetUnits() } ) + --self:F( { Size = self.CargoObject:GetSize(), Units = self.CargoObject:GetUnits() } ) if self.CargoObject:GetSize() ~= 0 then return Zone:IsPointVec2InZone( self.CargoObject:GetPointVec2() ) else diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 32dd2a886..ab7addf54 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -150,7 +150,7 @@ do -- CARGO_GROUP -- @param #string From -- @param #string To function CARGO_GROUP:onenterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) + --self:F( { CargoCarrier.UnitName, From, Event, To } ) local NearRadius = NearRadius or 25 @@ -175,7 +175,7 @@ do -- CARGO_GROUP -- @param #string From -- @param #string To function CARGO_GROUP:onenterLoaded( From, Event, To, CargoCarrier, ... ) - self:F( { From, Event, To, CargoCarrier, ...} ) + --self:F( { From, Event, To, CargoCarrier, ...} ) if From == "UnLoaded" then -- For each Cargo object within the CARGO_GROUP, load each cargo to the CargoCarrier. @@ -196,7 +196,7 @@ do -- CARGO_GROUP -- @param #string From -- @param #string To function CARGO_GROUP:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { CargoCarrier.UnitName, From, Event, To } ) + --self:F( { CargoCarrier.UnitName, From, Event, To } ) local NearRadius = NearRadius or 100 @@ -259,7 +259,7 @@ do -- CARGO_GROUP -- @param #string From -- @param #string To function CARGO_GROUP:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) - self:F( {From, Event, To, ToPointVec2, NearRadius } ) + --self:F( {From, Event, To, ToPointVec2, NearRadius } ) NearRadius = NearRadius or 25 @@ -293,7 +293,7 @@ do -- CARGO_GROUP -- @param #string From -- @param #string To function CARGO_GROUP:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) - self:F( { From, Event, To, ToPointVec2, NearRadius } ) + --self:F( { From, Event, To, ToPointVec2, NearRadius } ) --local NearRadius = NearRadius or 25 @@ -330,7 +330,7 @@ do -- CARGO_GROUP -- @param #string From -- @param #string To function CARGO_GROUP:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) - self:F( { From, Event, To, ToPointVec2, NearRadius } ) + --self:F( { From, Event, To, ToPointVec2, NearRadius } ) --local NearRadius = NearRadius or 25 @@ -346,7 +346,7 @@ do -- CARGO_GROUP -- @param #string From -- @param #string To function CARGO_GROUP:onenterUnLoaded( From, Event, To, ToPointVec2, ... ) - self:F( { From, Event, To, ToPointVec2 } ) + --self:F( { From, Event, To, ToPointVec2 } ) if From == "Loaded" then @@ -410,7 +410,7 @@ do -- CARGO_GROUP -- @param #CARGO_GROUP self -- @param Core.Point#COORDINATE Coordinate function CARGO_GROUP:RouteTo( Coordinate ) - self:F( {Coordinate = Coordinate } ) + --self:F( {Coordinate = Coordinate } ) -- For each Cargo within the CargoSet, route each object to the Coordinate self.CargoSet:ForEach( @@ -429,7 +429,7 @@ do -- CARGO_GROUP -- @return #boolean The Cargo is near to the Carrier. -- @return #nil The Cargo is not near to the Carrier. function CARGO_GROUP:IsNear( CargoCarrier, NearRadius ) - self:F( {NearRadius = NearRadius } ) + --self:F( {NearRadius = NearRadius } ) local Cargo = self.CargoSet:GetFirst() -- #CARGO @@ -445,7 +445,7 @@ do -- CARGO_GROUP -- @param Core.Point#Coordinate Coordinate -- @return #boolean true if the Cargo Group is within the load radius. function CARGO_GROUP:IsInLoadRadius( Coordinate ) - self:F( { Coordinate } ) + --self:F( { Coordinate } ) local Cargo = self.CargoSet:GetFirst() -- #CARGO @@ -456,7 +456,7 @@ do -- CARGO_GROUP else Distance = Coordinate:DistanceFromPointVec2( Cargo.CargoObject:GetPointVec2() ) end - self:T( Distance ) + --self:T( Distance ) if Distance <= self.LoadRadius then return true @@ -475,7 +475,7 @@ do -- CARGO_GROUP -- @param Core.Point#Coordinate Coordinate -- @return #boolean true if the Cargo Group is within the report radius. function CARGO_GROUP:IsInReportRadius( Coordinate ) - self:F( { Coordinate } ) + --self:F( { Coordinate } ) local Cargo = self.CargoSet:GetFirst() -- #CARGO @@ -483,7 +483,7 @@ do -- CARGO_GROUP local Distance = 0 if Cargo:IsUnLoaded() then Distance = Coordinate:DistanceFromPointVec2( Cargo.CargoObject:GetPointVec2() ) - self:T( Distance ) + --self:T( Distance ) if Distance <= self.LoadRadius then return true end @@ -572,7 +572,7 @@ do -- CARGO_GROUP -- @return #boolean **true** if the first element of the CargoGroup is in the Zone -- @return #boolean **false** if there is no element of the CargoGroup in the Zone. function CARGO_GROUP:IsInZone( Zone ) - self:F( { Zone } ) + --self:F( { Zone } ) local Cargo = self.CargoSet:GetFirst() -- #CARGO diff --git a/Moose Development/Moose/Cargo/CargoSlingload.lua b/Moose Development/Moose/Cargo/CargoSlingload.lua index 1cfa043f1..b1ea35246 100644 --- a/Moose Development/Moose/Cargo/CargoSlingload.lua +++ b/Moose Development/Moose/Cargo/CargoSlingload.lua @@ -127,7 +127,7 @@ do -- CARGO_SLINGLOAD local Distance = 0 if self:IsUnLoaded() then Distance = Coordinate:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) - self:T( Distance ) + --self:T( Distance ) if Distance <= self.LoadRadius then return true end diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index efcea3aee..dc2dec440 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -934,7 +934,9 @@ do -- FSM_PROCESS if self[handler] then self:F3( "Calling " .. handler ) self._EventSchedules[EventName] = nil - local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, self.Task, unpack( params ) ) end, ErrorHandler ) + if self.Controllable and self.Controllable:IsAlive() == true then + local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, self.Task, unpack( params ) ) end, ErrorHandler ) + end return Value --return self[handler]( self, self.Controllable, unpack( params ) ) end diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index d1dde22ed..564e6b4bd 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -688,7 +688,8 @@ function TASK:SetMenu( MenuTime ) --R2.1 Mission Reports and Task Reports added. self:F( { self:GetName(), MenuTime } ) --self.SetGroup:Flush() - for TaskGroupID, TaskGroupData in pairs( self.SetGroup:GetAliveSet() ) do + --for TaskGroupID, TaskGroupData in pairs( self.SetGroup:GetAliveSet() ) do + for TaskGroupID, TaskGroupData in pairs( self.SetGroup:GetSet() ) do local TaskGroup = TaskGroupData -- Wrapper.Group#GROUP if TaskGroup:IsAlive() == true and TaskGroup:GetPlayerNames() then @@ -1444,11 +1445,13 @@ function TASK:GetPlayerCount() --R2.1 Get a count of the players. local PlayerCount = 0 -- Loop each Unit active in the Task, and find Player Names. - for TaskGroupID, PlayerGroup in pairs( self:GetGroups():GetAliveSet() ) do + for TaskGroupID, PlayerGroup in pairs( self:GetGroups():GetSet() ) do local PlayerGroup = PlayerGroup -- Wrapper.Group#GROUP - if self:IsGroupAssigned( PlayerGroup ) then - local PlayerNames = PlayerGroup:GetPlayerNames() - PlayerCount = PlayerCount + #PlayerNames + if PlayerGroup:IsAlive() == true then + if self:IsGroupAssigned( PlayerGroup ) then + local PlayerNames = PlayerGroup:GetPlayerNames() + PlayerCount = PlayerCount + #PlayerNames + end end end diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 4f1473e7f..12cabd80e 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -291,8 +291,7 @@ do -- TASK_CARGO -- ):SetTime(MenuTime) -- end - self:F( { CargoUnloaded = Cargo:IsUnLoaded(), CargoLoaded = Cargo:IsLoaded(), CargoItemCount = CargoItemCount } ) - Task:E( { TaskDeployZones = Task.DeployZones, TaskName = Task:GetName() } ) + --self:F( { CargoUnloaded = Cargo:IsUnLoaded(), CargoLoaded = Cargo:IsLoaded(), CargoItemCount = CargoItemCount } ) local TaskGroup = TaskUnit:GetGroup() @@ -349,7 +348,6 @@ do -- TASK_CARGO -- Cargo in deployzones are flagged as deployed. for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do - Task:E( { DeployZone = DeployZone } ) if Cargo:IsInZone( DeployZone ) then Task:E( { CargoIsDeployed = Task.CargoDeployed and "true" or "false" } ) if Cargo:IsDeployed() == false then From fea2f55fbbea65d4351399b0727308b5f2ab2c73 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Thu, 12 Apr 2018 13:59:55 +0200 Subject: [PATCH 050/420] Lots of optimizations --- Moose Development/Moose/Actions/Act_Route.lua | 7 +- .../Moose/Cargo/CargoSlingload.lua | 2 - Moose Development/Moose/Core/Database.lua | 4 +- Moose Development/Moose/Core/Event.lua | 2 +- Moose Development/Moose/Core/Fsm.lua | 215 +++++++++++------- Moose Development/Moose/Core/Point.lua | 14 +- Moose Development/Moose/Core/Set.lua | 5 +- .../Moose/Functional/Detection.lua | 49 ++-- .../Moose/Tasking/CommandCenter.lua | 2 +- Moose Development/Moose/Tasking/Mission.lua | 4 +- Moose Development/Moose/Tasking/Task.lua | 67 +++--- .../Moose/Tasking/Task_CARGO.lua | 8 - Moose Development/Moose/Wrapper/Client.lua | 2 +- .../Moose/Wrapper/Controllable.lua | 2 +- Moose Development/Moose/Wrapper/Group.lua | 45 +++- Moose Development/Moose/Wrapper/Unit.lua | 22 +- 16 files changed, 261 insertions(+), 189 deletions(-) diff --git a/Moose Development/Moose/Actions/Act_Route.lua b/Moose Development/Moose/Actions/Act_Route.lua index 8a0d53b0f..3a4806468 100644 --- a/Moose Development/Moose/Actions/Act_Route.lua +++ b/Moose Development/Moose/Actions/Act_Route.lua @@ -244,10 +244,8 @@ do -- ACT_ROUTE -- @param #string From -- @param #string To function ACT_ROUTE:onbeforeRoute( ProcessUnit, From, Event, To ) - self:F( { "BeforeRoute 1", self.DisplayCount, self.DisplayInterval } ) if ProcessUnit:IsAlive() then - self:F( "BeforeRoute 2" ) local HasArrived = self:onfuncHasArrived( ProcessUnit ) -- Polymorphic if self.DisplayCount >= self.DisplayInterval then self:T( { HasArrived = HasArrived } ) @@ -259,8 +257,6 @@ do -- ACT_ROUTE self.DisplayCount = self.DisplayCount + 1 end - self:T( { DisplayCount = self.DisplayCount } ) - if HasArrived then self:__Arrive( 1 ) else @@ -343,7 +339,7 @@ do -- ACT_ROUTE_POINT -- @param #ACT_ROUTE_POINT self -- @param #number Range The Range to consider the arrival. Default is 10000 meters. function ACT_ROUTE_POINT:SetRange( Range ) - self:F2( { self.Range } ) + self:F2( { Range } ) self.Range = Range or 10000 end @@ -351,6 +347,7 @@ do -- ACT_ROUTE_POINT -- @param #ACT_ROUTE_POINT self -- @return #number The Range to consider the arrival. Default is 10000 meters. function ACT_ROUTE_POINT:GetRange() + self:F2( { self.Range } ) return self.Range end diff --git a/Moose Development/Moose/Cargo/CargoSlingload.lua b/Moose Development/Moose/Cargo/CargoSlingload.lua index b1ea35246..642fde94f 100644 --- a/Moose Development/Moose/Cargo/CargoSlingload.lua +++ b/Moose Development/Moose/Cargo/CargoSlingload.lua @@ -127,7 +127,6 @@ do -- CARGO_SLINGLOAD local Distance = 0 if self:IsUnLoaded() then Distance = Coordinate:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) - --self:T( Distance ) if Distance <= self.LoadRadius then return true end @@ -147,7 +146,6 @@ do -- CARGO_SLINGLOAD local Distance = 0 if self:IsUnLoaded() then Distance = Coordinate:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) - self:T( Distance ) if Distance <= self.NearRadius then return true end diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 01b1c64a0..b160fdfda 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -719,7 +719,7 @@ function DATABASE:_EventOnBirth( Event ) Event.IniGroup = self:FindGroup( Event.IniDCSGroupName ) local PlayerName = Event.IniUnit:GetPlayerName() self:E( { "PlayerName:", PlayerName } ) - if PlayerName ~= "" then + if PlayerName then self:E( { "Player Joined:", PlayerName } ) if not self.PLAYERS[PlayerName] then self:AddPlayer( Event.IniUnitName, PlayerName ) @@ -788,7 +788,7 @@ function DATABASE:_EventOnPlayerLeaveUnit( Event ) if Event.IniUnit then if Event.IniObjectCategory == 1 then local PlayerName = Event.IniUnit:GetPlayerName() - if self.PLAYERS[PlayerName] then + if PlayerName and self.PLAYERS[PlayerName] then self:E( { "Player Left:", PlayerName } ) local Settings = SETTINGS:Set( PlayerName ) Settings:RemovePlayerMenu( Event.IniUnit ) diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index b7004d42f..fafd2d2c5 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -744,7 +744,7 @@ function EVENT:onEvent( Event ) local EventMeta = _EVENTMETA[Event.id] - self:E( { EventMeta.Text, Event } ) -- Activate the see all incoming events ... + --self:E( { EventMeta.Text, Event } ) -- Activate the see all incoming events ... if self and self.Events and diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index dc2dec440..12b25179a 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -337,7 +337,7 @@ do -- FSM --- Creates a new FSM object. -- @param #FSM self -- @return #FSM - function FSM:New( FsmT ) + function FSM:New() -- Inherits from BASE self = BASE:Inherit( self, BASE:New() ) @@ -557,8 +557,9 @@ do -- FSM end - function FSM:_call_handler( handler, params, EventName ) + function FSM:_call_handler( step, trigger, params, EventName ) + local handler = step .. trigger local ErrorHandler = function( errmsg ) env.info( "Error in SCHEDULER function:" .. errmsg ) @@ -569,64 +570,99 @@ do -- FSM return errmsg end if self[handler] then - self:T2( "Calling " .. handler ) + self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] ) self._EventSchedules[EventName] = nil local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler ) return Value end end + --- @param #FSM self function FSM._handler( self, EventName, ... ) - local Can, to = self:can( EventName ) + local Can, To = self:can( EventName ) - if to == "*" then - to = self.current + if To == "*" then + To = self.current end if Can then - local from = self.current - local params = { from, EventName, to, ... } + local From = self.current + local Params = { From, EventName, To, ... } - if self.Controllable then - self:T( "FSM Transition for " .. self.Controllable.ControllableName .. " :" .. self.current .. " --> " .. EventName .. " --> " .. to ) + + if self["onleave".. From] or + self["OnLeave".. From] or + self["onbefore".. EventName] or + self["OnBefore".. EventName] or + self["onafter".. EventName] or + self["OnAfter".. EventName] or + self["onenter".. To] or + self["OnEnter".. To] + then + if self:_call_handler( "onbefore", EventName, Params, EventName ) == false then + self:T( "*** FSM *** Cancel" .. " *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** onbefore" .. EventName ) + return false + else + if self:_call_handler( "OnBefore", EventName, Params, EventName ) == false then + self:T( "*** FSM *** Cancel" .. " *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** OnBefore" .. EventName ) + return false + else + if self:_call_handler( "onleave", From, Params, EventName ) == false then + self:T( "*** FSM *** Cancel" .. " *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** onleave" .. From ) + return false + else + if self:_call_handler( "OnLeave", From, Params, EventName ) == false then + self:T( "*** FSM *** Cancel" .. " *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** OnLeave" .. From ) + return false + end + end + end + end else - self:T( "FSM Transition:" .. self.current .. " --> " .. EventName .. " --> " .. to ) - end + local ClassName = self:GetClassName() + if ClassName == "FSM" then + self:T( "*** FSM *** Transit *** " .. self.current .. " --> " .. EventName .. " --> " .. To ) + end - if ( self:_call_handler("onbefore" .. EventName, params, EventName ) == false ) - or ( self:_call_handler("OnBefore" .. EventName, params, EventName ) == false ) - or ( self:_call_handler("onleave" .. from, params, EventName ) == false ) - or ( self:_call_handler("OnLeave" .. from, params, EventName ) == false ) then - self:T( "Cancel Transition" ) - return false + if ClassName == "FSM_TASK" then + self:T( "*** FSM *** Transit *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** Task: " .. self.TaskName ) + end + + if ClassName == "FSM_CONTROLLABLE" then + self:T( "*** FSM *** Transit *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** TaskUnit: " .. self.Controllable.ControllableName .. " *** " ) + end + + if ClassName == "FSM_PROCESS" then + self:T( "*** FSM *** Transit *** " .. self.current .. " --> " .. EventName .. " --> " .. To .. " *** Task: " .. self.Task:GetName() .. ", TaskUnit: " .. self.Controllable.ControllableName .. " *** " ) + end end - self.current = to + self.current = To local execute = true - local subtable = self:_gosub( from, EventName ) + local subtable = self:_gosub( From, EventName ) for _, sub in pairs( subtable ) do --if sub.nextevent then -- self:F2( "nextevent = " .. sub.nextevent ) -- self[sub.nextevent]( self ) --end - self:T( "calling sub start event: " .. sub.StartEvent ) + self:T( "*** FSM *** Sub *** " .. sub.StartEvent ) sub.fsm.fsmparent = self sub.fsm.ReturnEvents = sub.ReturnEvents sub.fsm[sub.StartEvent]( sub.fsm ) execute = false end - local fsmparent, Event = self:_isendstate( to ) + local fsmparent, Event = self:_isendstate( To ) if fsmparent and Event then - self:F2( { "end state: ", fsmparent, Event } ) - self:_call_handler("onenter" .. to, params, EventName ) - self:_call_handler("OnEnter" .. to, params, EventName ) - self:_call_handler("onafter" .. EventName, params, EventName ) - self:_call_handler("OnAfter" .. EventName, params, EventName ) - self:_call_handler("onstatechange", params, EventName ) + self:T( "*** FSM *** End *** " .. Event ) + self:_call_handler("onenter", To, Params, EventName ) + self:_call_handler("OnEnter", To, Params, EventName ) + self:_call_handler("onafter", EventName, Params, EventName ) + self:_call_handler("OnAfter", EventName, Params, EventName ) + self:_call_handler("onstate", "change", Params, EventName ) fsmparent[Event]( fsmparent ) execute = false end @@ -634,18 +670,17 @@ do -- FSM if execute then -- only execute the call if the From state is not equal to the To state! Otherwise this function should never execute! --if from ~= to then - self:_call_handler("onenter" .. to, params, EventName ) - self:_call_handler("OnEnter" .. to, params, EventName ) + self:_call_handler("onenter", To, Params, EventName ) + self:_call_handler("OnEnter", To, Params, EventName ) --end - self:_call_handler("onafter" .. EventName, params, EventName ) - self:_call_handler("OnAfter" .. EventName, params, EventName ) + self:_call_handler("onafter", EventName, Params, EventName ) + self:_call_handler("OnAfter", EventName, Params, EventName ) - self:_call_handler("onstatechange", params, EventName ) + self:_call_handler("onstate", "change", Params, EventName ) end else - self:T( "Cannot execute transition." ) - self:T( { From = self.current, Event = EventName, To = to, Can = Can } ) + self:T( "*** FSM *** NO Transition *** " .. self.current .. " --> " .. EventName .. " --> ? " ) end return nil @@ -691,17 +726,16 @@ do -- FSM function FSM:_isendstate( Current ) local FSMParent = self.fsmparent if FSMParent and self.endstates[Current] then - self:T( { state = Current, endstates = self.endstates, endstate = self.endstates[Current] } ) + --self:T( { state = Current, endstates = self.endstates, endstate = self.endstates[Current] } ) FSMParent.current = Current local ParentFrom = FSMParent.current - self:T( ParentFrom ) - self:T( self.ReturnEvents ) + --self:T( { ParentFrom, self.ReturnEvents } ) local Event = self.ReturnEvents[Current] - self:T( { ParentFrom, Event, self.ReturnEvents } ) + --self:T( { Event } ) if Event then return FSMParent, Event else - self:T( { "Could not find parent event name for state ", ParentFrom } ) + --self:T( { "Could not find parent event name for state ", ParentFrom } ) end end @@ -773,10 +807,10 @@ do -- FSM_CONTROLLABLE -- @param #table FSMT Finite State Machine Table -- @param Wrapper.Controllable#CONTROLLABLE Controllable (optional) The CONTROLLABLE object that the FSM_CONTROLLABLE governs. -- @return #FSM_CONTROLLABLE - function FSM_CONTROLLABLE:New( FSMT, Controllable ) + function FSM_CONTROLLABLE:New( Controllable ) -- Inherits from BASE - local self = BASE:Inherit( self, FSM:New( FSMT ) ) -- Core.Fsm#FSM_CONTROLLABLE + local self = BASE:Inherit( self, FSM:New() ) -- Core.Fsm#FSM_CONTROLLABLE if Controllable then self:SetControllable( Controllable ) @@ -859,7 +893,9 @@ do -- FSM_CONTROLLABLE return self.Controllable end - function FSM_CONTROLLABLE:_call_handler( handler, params, EventName ) + function FSM_CONTROLLABLE:_call_handler( step, trigger, params, EventName ) + + local handler = step .. trigger local ErrorHandler = function( errmsg ) @@ -872,7 +908,7 @@ do -- FSM_CONTROLLABLE end if self[handler] then - self:F3( "Calling " .. handler ) + self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] .. " *** TaskUnit: " .. self.Controllable:GetName() ) self._EventSchedules[EventName] = nil local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, unpack( params ) ) end, ErrorHandler ) return Value @@ -909,9 +945,9 @@ do -- FSM_PROCESS local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- Core.Fsm#FSM_PROCESS --self:F( Controllable ) - + self:Assign( Controllable, Task ) - + return self end @@ -919,7 +955,9 @@ do -- FSM_PROCESS self:T( "No Initialisation" ) end - function FSM_PROCESS:_call_handler( handler, params, EventName ) + function FSM_PROCESS:_call_handler( step, trigger, params, EventName ) + + local handler = step .. trigger local ErrorHandler = function( errmsg ) @@ -932,7 +970,9 @@ do -- FSM_PROCESS end if self[handler] then - self:F3( "Calling " .. handler ) + if handler ~= "onstatechange" then + self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] .. " *** Task: " .. self.Task:GetName() .. ", TaskUnit: " .. self.Controllable:GetName() ) + end self._EventSchedules[EventName] = nil if self.Controllable and self.Controllable:IsAlive() == true then local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, self.Task, unpack( params ) ) end, ErrorHandler ) @@ -952,7 +992,7 @@ do -- FSM_PROCESS local NewFsm = self:New( Controllable, Task ) -- Core.Fsm#FSM_PROCESS NewFsm:Assign( Controllable, Task ) - + -- Polymorphic call to initialize the new FSM_PROCESS based on self FSM_PROCESS NewFsm:Init( self ) @@ -1043,21 +1083,21 @@ do -- FSM_PROCESS -- TODO: Need to check and fix that an FSM_PROCESS is only for a UNIT. Not for a GROUP. --- Send a message of the @{Task} to the Group of the Unit. --- @param #FSM_PROCESS self -function FSM_PROCESS:Message( Message ) - self:F( { Message = Message } ) - - local CC = self:GetCommandCenter() - local TaskGroup = self.Controllable:GetGroup() + -- @param #FSM_PROCESS self + function FSM_PROCESS:Message( Message ) + self:F( { Message = Message } ) - local PlayerName = self.Controllable:GetPlayerName() -- Only for a unit - PlayerName = PlayerName and " (" .. PlayerName .. ")" or "" -- If PlayerName is nil, then keep it nil, otherwise add brackets. - local Callsign = self.Controllable:GetCallsign() - local Prefix = Callsign and " @ " .. Callsign .. PlayerName or "" - - Message = Prefix .. ": " .. Message - CC:MessageToGroup( Message, TaskGroup ) -end + local CC = self:GetCommandCenter() + local TaskGroup = self.Controllable:GetGroup() + + local PlayerName = self.Controllable:GetPlayerName() -- Only for a unit + PlayerName = PlayerName and " (" .. PlayerName .. ")" or "" -- If PlayerName is nil, then keep it nil, otherwise add brackets. + local Callsign = self.Controllable:GetCallsign() + local Prefix = Callsign and " @ " .. Callsign .. PlayerName or "" + + Message = Prefix .. ": " .. Message + CC:MessageToGroup( Message, TaskGroup ) + end @@ -1078,14 +1118,16 @@ end return self end - function FSM_PROCESS:onenterAssigned( ProcessUnit ) - self:T( "Assign" ) +-- function FSM_PROCESS:onenterAssigned( ProcessUnit, Task, From, Event, To ) +-- +-- if From( "Planned" ) then +-- self:T( "*** FSM *** Assign *** " .. Task:GetName() .. "/" .. ProcessUnit:GetName() .. " *** " .. From .. " --> " .. Event .. " --> " .. To ) +-- self.Task:Assign() +-- end +-- end - self.Task:Assign() - end - - function FSM_PROCESS:onenterFailed( ProcessUnit ) - self:T( "Failed" ) + function FSM_PROCESS:onenterFailed( ProcessUnit, Task, From, Event, To ) + self:T( "*** FSM *** Failed *** " .. Task:GetName() .. "/" .. ProcessUnit:GetName() .. " *** " .. From .. " --> " .. Event .. " --> " .. To ) self.Task:Fail() end @@ -1097,14 +1139,17 @@ end -- @param #string Event -- @param #string From -- @param #string To - function FSM_PROCESS:onstatechange( ProcessUnit, Task, From, Event, To, Dummy ) - self:T( { ProcessUnit:GetName(), From, Event, To, Dummy, self:IsTrace() } ) + function FSM_PROCESS:onstatechange( ProcessUnit, Task, From, Event, To ) - if self:IsTrace() then - --MESSAGE:New( "@ Process " .. self:GetClassNameAndID() .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll() + if From ~= To then + self:T( "*** FSM *** Change *** " .. Task:GetName() .. "/" .. ProcessUnit:GetName() .. " *** " .. From .. " --> " .. Event .. " --> " .. To ) end - self:T( { Scores = self._Scores, To = To } ) +-- if self:IsTrace() then +-- MESSAGE:New( "@ Process " .. self:GetClassNameAndID() .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll() +-- self:F2( { Scores = self._Scores, To = To } ) +-- end + -- TODO: This needs to be reworked with a callback functions allocated within Task, and set within the mission script from the Task Objects... if self._Scores[To] then @@ -1139,22 +1184,23 @@ do -- FSM_TASK --- Creates a new FSM_TASK object. -- @param #FSM_TASK self - -- @param #table FSMT - -- @param Tasking.Task#TASK Task - -- @param Wrapper.Unit#UNIT TaskUnit + -- @param #string TaskName The name of the task. -- @return #FSM_TASK - function FSM_TASK:New( FSMT ) + function FSM_TASK:New( TaskName ) - local self = BASE:Inherit( self, FSM_CONTROLLABLE:New( FSMT ) ) -- Core.Fsm#FSM_TASK + local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- Core.Fsm#FSM_TASK self["onstatechange"] = self.OnStateChange + self.TaskName = TaskName return self end - function FSM_TASK:_call_handler( handler, params, EventName ) + function FSM_TASK:_call_handler( step, trigger, params, EventName ) + local handler = step .. trigger + if self[handler] then - self:T( "Calling " .. handler ) + self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] .. " *** Task: " .. self.TaskName ) self._EventSchedules[EventName] = nil return self[handler]( self, unpack( params ) ) end @@ -1216,9 +1262,10 @@ do -- FSM_SET return self.Controllable end - function FSM_SET:_call_handler( handler, params, EventName ) + function FSM_SET:_call_handler( step, trigger, params, EventName ) + local handler = step .. trigger if self[handler] then - self:T( "Calling " .. handler ) + self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] ) self._EventSchedules[EventName] = nil return self[handler]( self, self.Set, unpack( params ) ) end diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 0bb49c265..de1a81cfc 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1289,7 +1289,7 @@ do -- COORDINATE -- @return #string The coordinate Text in the configured coordinate system. function COORDINATE:ToStringFromRP( ReferenceCoord, ReferenceName, Controllable, Settings ) -- R2.2 - self:F( { ReferenceCoord = ReferenceCoord, ReferenceName = ReferenceName } ) + self:F2( { ReferenceCoord = ReferenceCoord, ReferenceName = ReferenceName } ) local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS @@ -1318,7 +1318,7 @@ do -- COORDINATE -- @return #string The coordinate Text in the configured coordinate system. function COORDINATE:ToStringA2G( Controllable, Settings ) -- R2.2 - self:F( { Controllable = Controllable and Controllable:GetName() } ) + self:F2( { Controllable = Controllable and Controllable:GetName() } ) local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS @@ -1353,7 +1353,7 @@ do -- COORDINATE -- @return #string The coordinate Text in the configured coordinate system. function COORDINATE:ToStringA2A( Controllable, Settings ) -- R2.2 - self:F( { Controllable = Controllable and Controllable:GetName() } ) + self:F2( { Controllable = Controllable and Controllable:GetName() } ) local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS @@ -1393,7 +1393,7 @@ do -- COORDINATE -- @return #string The coordinate Text in the configured coordinate system. function COORDINATE:ToString( Controllable, Settings, Task ) -- R2.2 - self:F( { Controllable = Controllable and Controllable:GetName() } ) + self:F2( { Controllable = Controllable and Controllable:GetName() } ) local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS @@ -1442,7 +1442,7 @@ do -- COORDINATE -- @return #string The pressure text in the configured measurement system. function COORDINATE:ToStringPressure( Controllable, Settings ) -- R2.3 - self:F( { Controllable = Controllable and Controllable:GetName() } ) + self:F2( { Controllable = Controllable and Controllable:GetName() } ) local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS @@ -1458,7 +1458,7 @@ do -- COORDINATE -- @return #string The wind text in the configured measurement system. function COORDINATE:ToStringWind( Controllable, Settings ) -- R2.3 - self:F( { Controllable = Controllable and Controllable:GetName() } ) + self:F2( { Controllable = Controllable and Controllable:GetName() } ) local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS @@ -1474,7 +1474,7 @@ do -- COORDINATE -- @return #string The temperature text in the configured measurement system. function COORDINATE:ToStringTemperature( Controllable, Settings ) -- R2.3 - self:F( { Controllable = Controllable and Controllable:GetName() } ) + self:F2( { Controllable = Controllable and Controllable:GetName() } ) local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index eb950115a..a59d82909 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -149,7 +149,7 @@ end -- @param #SET_BASE self -- @param #string ObjectName function SET_BASE:Remove( ObjectName ) - self:F( { ObjectName = ObjectName, Object = Object } ) + self:F2( { ObjectName = ObjectName } ) local Object = self.Set[ObjectName] @@ -158,7 +158,6 @@ function SET_BASE:Remove( ObjectName ) if Key == ObjectName then table.remove( self.Index, Index ) self.Set[ObjectName] = nil - self:Flush(self) break end end @@ -174,7 +173,7 @@ end -- @param Core.Base#BASE Object -- @return Core.Base#BASE The added BASE Object. function SET_BASE:Add( ObjectName, Object ) - self:F( { ObjectName = ObjectName, Object = Object } ) + self:F2( { 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 diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 98f7cb024..02122a437 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -510,7 +510,6 @@ do -- DETECTION_BASE -- @param #string Event The Event string. -- @param #string To The To State string. function DETECTION_BASE:onafterDetect(From,Event,To) - self:F( { From, Event, To } ) local DetectDelay = 0.1 self.DetectionCount = 0 @@ -533,7 +532,6 @@ do -- DETECTION_BASE -- @param #string To The To State string. -- @param Wrapper.Group#GROUP DetectionGroup The Group detecting. function DETECTION_BASE:onafterDetectionGroup( From, Event, To, DetectionGroup, DetectionTimeStamp ) - self:F( { From, Event, To } ) self.DetectionRun = self.DetectionRun + 1 @@ -541,7 +539,7 @@ do -- DETECTION_BASE if DetectionGroup:IsAlive() then - self:T( { "DetectionGroup is Alive", DetectionGroup:GetName() } ) + --self:T( { "DetectionGroup is Alive", DetectionGroup:GetName() } ) local DetectionGroupName = DetectionGroup:GetName() local DetectionUnit = DetectionGroup:GetUnit(1) @@ -557,7 +555,7 @@ do -- DETECTION_BASE self.DetectDLINK ) - self:F( DetectedTargets ) + --self:F( DetectedTargets ) for DetectionObjectID, Detection in pairs( DetectedTargets ) do local DetectedObject = Detection.object -- Dcs.DCSWrapper.Object#Object @@ -574,7 +572,7 @@ do -- DETECTION_BASE self.DetectDLINK ) - self:T2( { TargetIsDetected = TargetIsDetected, TargetIsVisible = TargetIsVisible, TargetLastTime = TargetLastTime, TargetKnowType = TargetKnowType, TargetKnowDistance = TargetKnowDistance, TargetLastPos = TargetLastPos, TargetLastVelocity = TargetLastVelocity } ) + --self:T2( { TargetIsDetected = TargetIsDetected, TargetIsVisible = TargetIsVisible, TargetLastTime = TargetLastTime, TargetKnowType = TargetKnowType, TargetKnowDistance = TargetKnowDistance, TargetLastPos = TargetLastPos, TargetLastVelocity = TargetLastVelocity } ) -- Only process if the target is visible. Detection also returns invisible units. --if Detection.visible == true then @@ -596,7 +594,7 @@ do -- DETECTION_BASE local DetectedUnitCategory = DetectedObject:getDesc().category - self:F( { "Detected Target:", DetectionGroupName, DetectedObjectName, DetectedObjectType, Distance, DetectedUnitCategory } ) + --self:F( { "Detected Target:", DetectionGroupName, DetectedObjectName, DetectedObjectType, Distance, DetectedUnitCategory } ) -- Calculate Acceptance @@ -644,7 +642,7 @@ do -- DETECTION_BASE local DistanceProbability = 1 - DistanceProbabilityReversed DistanceProbability = DistanceProbability * 30 / 300 local Probability = math.random() -- Selects a number between 0 and 1 - self:T( { Probability, DistanceProbability } ) + --self:T( { Probability, DistanceProbability } ) if Probability > DistanceProbability then DetectionAccepted = false end @@ -660,7 +658,7 @@ do -- DETECTION_BASE AlphaAngleProbability = AlphaAngleProbability * 30 / 300 local Probability = math.random() -- Selects a number between 0 and 1 - self:T( { Probability, AlphaAngleProbability } ) + --self:T( { Probability, AlphaAngleProbability } ) if Probability > AlphaAngleProbability then DetectionAccepted = false end @@ -677,7 +675,7 @@ do -- DETECTION_BASE if ZoneObject:IsPointVec2InZone( DetectedObjectVec2 ) == true then local Probability = math.random() -- Selects a number between 0 and 1 - self:T( { Probability, ZoneProbability } ) + --self:T( { Probability, ZoneProbability } ) if Probability > ZoneProbability then DetectionAccepted = false break @@ -702,7 +700,7 @@ do -- DETECTION_BASE self.DetectedObjects[DetectedObjectName].Distance = Distance self.DetectedObjects[DetectedObjectName].DetectionTimeStamp = DetectionTimeStamp - self:F( { DetectedObject = self.DetectedObjects[DetectedObjectName] } ) + --self:F( { DetectedObject = self.DetectedObjects[DetectedObjectName] } ) local DetectedUnit = UNIT:FindByName( DetectedObjectName ) @@ -716,7 +714,7 @@ do -- DETECTION_BASE --end end - self:T2( self.DetectedObjects ) + --self:T2( self.DetectedObjects ) end if HasDetectedObjects then @@ -726,7 +724,6 @@ do -- DETECTION_BASE end if self.DetectionCount > 0 and self.DetectionRun == self.DetectionCount then - self:T( "--> Create Detection Sets" ) -- First check if all DetectedObjects were detected. -- This is important. When there are DetectedObjects in the list, but were not detected, @@ -766,7 +763,6 @@ do -- DETECTION_BASE local DetectedSet = DetectedItem.Set if DetectedSet:Count() == 0 then - self:F3( { DetectedItemID = DetectedItemID } ) self:RemoveDetectedItem( DetectedItemID ) end @@ -778,8 +774,7 @@ do -- DETECTION_BASE -- @param #string UnitName The UnitName that needs to be forgotten from the DetectionItem Sets. -- @return #DETECTION_BASE function DETECTION_BASE:ForgetDetectedUnit( UnitName ) - self:F2() - + local DetectedItems = self:GetDetectedItems() for DetectedItemIndex, DetectedItem in pairs( DetectedItems ) do @@ -796,7 +791,6 @@ do -- DETECTION_BASE -- @param #DETECTION_BASE self -- @return #DETECTION_BASE function DETECTION_BASE:CreateDetectionItems() - self:F2() self:F( "Error, in DETECTION_BASE class..." ) return self @@ -1179,8 +1173,7 @@ do -- DETECTION_BASE -- @param Dcs.DCSUnit#Unit.Category Category The category of the unit. -- @return #boolean true if there are friendlies nearby function DETECTION_BASE:IsFriendliesNearBy( DetectedItem, Category ) - - self:F( { "FriendliesNearBy Test", DetectedItem.FriendliesNearBy } ) + --self:F( { "FriendliesNearBy Test", DetectedItem.FriendliesNearBy } ) return ( DetectedItem.FriendliesNearBy and DetectedItem.FriendliesNearBy[Category] ~= nil ) or false end @@ -1237,7 +1230,7 @@ do -- DETECTION_BASE --- Background worker function to determine if there are friendlies nearby ... -- @param #DETECTION_BASE self function DETECTION_BASE:ReportFriendliesNearBy( TargetData ) - self:F( { "Search Friendlies", DetectedItem = TargetData.DetectedItem } ) + --self:F( { "Search Friendlies", DetectedItem = TargetData.DetectedItem } ) local DetectedItem = TargetData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem local DetectedSet = TargetData.DetectedItem.Set @@ -1286,7 +1279,7 @@ do -- DETECTION_BASE if FoundUnitInReportSetGroup == true then -- If the recce was part of the friendlies found, then check if the recce is part of the allowed friendly unit prefixes. for PrefixID, Prefix in pairs( self.FriendlyPrefixes or {} ) do - self:F( { "Friendly Prefix:", Prefix = Prefix } ) + --self:F( { "Friendly Prefix:", Prefix = Prefix } ) -- In case a match is found (so a recce unit name is part of the friendly prefixes), then report that recce to be part of the friendlies. -- This is important if CAP planes (so planes using their own radar) to be scanning for targets as part of the EWR network. -- But CAP planes are also attackers, so they need to be considered friendlies too! @@ -1298,7 +1291,7 @@ do -- DETECTION_BASE end end - self:F( { "Friendlies near Target:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) + --self:F( { "Friendlies near Target:", FoundUnitName, FoundUnitCoalition, EnemyUnitName, EnemyCoalition, FoundUnitInReportSetGroup } ) if FoundUnitCoalition ~= EnemyCoalition and FoundUnitInReportSetGroup == false then local FriendlyUnit = UNIT:Find( FoundDCSUnit ) @@ -1313,7 +1306,7 @@ do -- DETECTION_BASE local Distance = DetectedUnitCoord:Get2DDistance( FriendlyUnit:GetCoordinate() ) DetectedItem.FriendliesDistance = DetectedItem.FriendliesDistance or {} DetectedItem.FriendliesDistance[Distance] = FriendlyUnit - self:T( { "Friendlies Found:", FriendlyUnitName = FriendlyUnitName, Distance = Distance, FriendlyUnitCategory = FriendlyUnitCategory, FriendliesCategory = self.FriendliesCategory } ) + --self:F( { "Friendlies Found:", FriendlyUnitName = FriendlyUnitName, Distance = Distance, FriendlyUnitCategory = FriendlyUnitCategory, FriendliesCategory = self.FriendliesCategory } ) return true end @@ -1355,6 +1348,9 @@ do -- DETECTION_BASE end ) end + + self:F( { Friendlies = DetectedItem.FriendliesNearBy, Players = DetectedItem.PlayersNearBy } ) + end end @@ -1886,7 +1882,6 @@ do -- DETECTION_UNITS -- @param #DETECTION_UNITS self -- @return #DETECTION_UNITS self function DETECTION_UNITS:CreateDetectionItems() - self:F2( #self.DetectedObjects ) -- Loop the current detected items, and check if each object still exists and is detected. @@ -2137,7 +2132,6 @@ do -- DETECTION_TYPES -- @param #DETECTION_TYPES self -- @return #DETECTION_TYPES self function DETECTION_TYPES:CreateDetectionItems() - self:F2( #self.DetectedObjects ) -- Loop the current detected items, and check if each object still exists and is detected. @@ -2525,10 +2519,9 @@ do -- DETECTION_AREAS -- @param #DETECTION_AREAS self -- @return #DETECTION_AREAS self function DETECTION_AREAS:CreateDetectionItems() - self:F2() - self:T( "Checking Detected Items for new Detected Units ..." ) + self:T2( "Checking Detected Items for new Detected Units ..." ) -- First go through all detected sets, and check if there are new detected units, match all existing detected units and identify undetected units. -- Regroup when needed, split groups when needed. for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do @@ -2537,8 +2530,7 @@ do -- DETECTION_AREAS if DetectedItem then - self:T( { "Detected Item ID:", DetectedItemID } ) - + self:T2( { "Detected Item ID: ", DetectedItemID } ) local DetectedSet = DetectedItem.Set @@ -2667,7 +2659,6 @@ do -- DETECTION_AREAS local DetectedItem = DetectedItemData -- #DETECTION_BASE.DetectedItem if DetectedItem then - self:T( "Detection Area #" .. DetectedItem.ID ) local DetectedSet = DetectedItem.Set if not self:IsDetectedObjectIdentified( DetectedObject ) and DetectedUnit:IsInZone( DetectedItem.Zone ) then self:IdentifyDetectedObject( DetectedObject ) diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index e521f63df..3f581ec62 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -171,7 +171,7 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) end ) - -- Handle when a player leaves a slot and goes back to spectators ... + -- Handle when a player crashes ... -- The PlayerUnit will be UnAssigned from the Task. -- When there is no Unit left running the Task, the Task goes into Abort... self:HandleEvent( EVENTS.Crash, diff --git a/Moose Development/Moose/Tasking/Mission.lua b/Moose Development/Moose/Tasking/Mission.lua index 441b463aa..0d872a85b 100644 --- a/Moose Development/Moose/Tasking/Mission.lua +++ b/Moose Development/Moose/Tasking/Mission.lua @@ -443,11 +443,11 @@ do -- Group Assignment local MissionGroupName = MissionGroup:GetName() if self.AssignedGroups[MissionGroupName] == MissionGroup then - self:T( { "Mission is assigned to:", MissionGroup:GetName() } ) + self:T2( { "Mission is assigned to:", MissionGroup:GetName() } ) return true end - self:T( { "Mission is not assigned to:", MissionGroup:GetName() } ) + self:T2( { "Mission is not assigned to:", MissionGroup:GetName() } ) return false end diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index 564e6b4bd..562b9413d 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -161,7 +161,7 @@ TASK = { -- @return #TASK self function TASK:New( Mission, SetGroupAssign, TaskName, TaskType, TaskBriefing ) - local self = BASE:Inherit( self, FSM_TASK:New() ) -- Tasking.Task#TASK + local self = BASE:Inherit( self, FSM_TASK:New( TaskName ) ) -- Tasking.Task#TASK self:SetStartState( "Planned" ) self:AddTransition( "Planned", "Assign", "Assigned" ) @@ -622,9 +622,11 @@ function TASK:MessageToGroups( Message ) local Mission = self:GetMission() local CC = Mission:GetCommandCenter() - for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetAliveSet() ) do - local TaskGroup = TaskGroup -- Wrapper.Group#GROUP - CC:MessageToGroup( Message, TaskGroup, TaskGroup:GetName() ) + for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do + TaskGroup = TaskGroup -- Wrapper.Group#GROUP + if TaskGroup:IsAlive() == true then + CC:MessageToGroup( Message, TaskGroup, TaskGroup:GetName() ) + end end end @@ -634,10 +636,11 @@ end function TASK:SendBriefingToAssignedGroups() self:F2() - for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetAliveSet() ) do - - if self:IsGroupAssigned( TaskGroup ) then - TaskGroup:Message( self.TaskBriefing, 60 ) + for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do + if TaskGroup:IsAlive() then + if self:IsGroupAssigned( TaskGroup ) then + TaskGroup:Message( self.TaskBriefing, 60 ) + end end end end @@ -648,9 +651,11 @@ end function TASK:UnAssignFromGroups() self:F2() - for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetAliveSet() ) do - if self:IsGroupAssigned(TaskGroup) then - self:UnAssignFromGroup( TaskGroup ) + for TaskGroupName, TaskGroup in pairs( self.SetGroup:GetSet() ) do + if TaskGroup:IsAlive() == true then + if self:IsGroupAssigned(TaskGroup) then + self:UnAssignFromGroup( TaskGroup ) + end end end end @@ -663,13 +668,15 @@ end function TASK:HasAliveUnits() self:F() - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetAliveSet() ) do - if self:IsStateAssigned() then - if self:IsGroupAssigned( TaskGroup ) then - for TaskUnitID, TaskUnit in pairs( TaskGroup:GetUnits() ) do - if TaskUnit:IsAlive() then - self:T( { HasAliveUnits = true } ) - return true + for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do + if TaskGroup:IsAlive() == true then + if self:IsStateAssigned() then + if self:IsGroupAssigned( TaskGroup ) then + for TaskUnitID, TaskUnit in pairs( TaskGroup:GetUnits() ) do + if TaskUnit:IsAlive() then + self:T( { HasAliveUnits = true } ) + return true + end end end end @@ -782,7 +789,7 @@ function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime ) local TaskText = string.format( "%s%s", self:GetName(), TaskPlayerString ) --, TaskThreatLevelString ) local TaskName = string.format( "%s", self:GetName() ) - for UnitName, TaskUnit in pairs( TaskGroup:GetUnits() ) do + for UnitName, TaskUnit in pairs( TaskGroup:GetPlayerUnits() ) do local TaskUnit = TaskUnit -- Wrapper.Unit#UNIT if TaskUnit then local MenuControl = self:GetTaskControlMenu( TaskUnit ) @@ -803,10 +810,12 @@ end function TASK:RemoveMenu( MenuTime ) self:F( { self:GetName(), MenuTime } ) - for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetAliveSet() ) do - local TaskGroup = TaskGroup -- Wrapper.Group#GROUP - if TaskGroup:IsAlive() == true and TaskGroup:GetPlayerNames() then - self:RefreshMenus( TaskGroup, MenuTime ) + for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do + if TaskGroup:IsAlive() == true then + local TaskGroup = TaskGroup -- Wrapper.Group#GROUP + if TaskGroup:IsAlive() == true and TaskGroup:GetPlayerNames() then + self:RefreshMenus( TaskGroup, MenuTime ) + end end end end @@ -1467,12 +1476,14 @@ function TASK:GetPlayerNames() --R2.1 Get a map of the players. local PlayerNameMap = {} -- Loop each Unit active in the Task, and find Player Names. - for TaskGroupID, PlayerGroup in pairs( self:GetGroups():GetAliveSet() ) do + for TaskGroupID, PlayerGroup in pairs( self:GetGroups():GetSet() ) do local PlayerGroup = PlayerGroup -- Wrapper.Group#GROUP - if self:IsGroupAssigned( PlayerGroup ) then - local PlayerNames = PlayerGroup:GetPlayerNames() - for PlayerNameID, PlayerName in pairs( PlayerNames ) do - PlayerNameMap[PlayerName] = PlayerGroup + if PlayerGroup:IsAlive() == true then + if self:IsGroupAssigned( PlayerGroup ) then + local PlayerNames = PlayerGroup:GetPlayerNames() + for PlayerNameID, PlayerName in pairs( PlayerNames ) do + PlayerNameMap[PlayerName] = PlayerGroup + end end end end diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 12cabd80e..4d523aba9 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -261,17 +261,9 @@ do -- TASK_CARGO function Fsm:onafterSelectAction( TaskUnit, Task ) local TaskUnitName = TaskUnit:GetName() - - self:F( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } ) - local MenuTime = Task:InitTaskControlMenu( TaskUnit ) - local MenuControl = Task:GetTaskControlMenu( TaskUnit ) - local CargoItemCount = TaskUnit:CargoItemCount() - - --Task:GetMission():GetCommandCenter():MessageToGroup( "Cargo in carrier: " .. CargoItemCount, TaskUnit:GetGroup() ) - Task.SetCargo:ForEachCargo( diff --git a/Moose Development/Moose/Wrapper/Client.lua b/Moose Development/Moose/Wrapper/Client.lua index 0d98f5092..4ccfe98d4 100644 --- a/Moose Development/Moose/Wrapper/Client.lua +++ b/Moose Development/Moose/Wrapper/Client.lua @@ -179,7 +179,7 @@ function CLIENT:ShowBriefing() if not self.ClientBriefingShown then self.ClientBriefingShown = true local Briefing = "" - if self.ClientBriefing then + if self.ClientBriefing and self.ClientBriefing ~= "" then Briefing = Briefing .. self.ClientBriefing self:Message( Briefing, 60, "Briefing" ) end diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 2d6551fd2..0b68255c5 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -2223,7 +2223,7 @@ function CONTROLLABLE:GetDetectedTargets( DetectVisual, DetectOptical, DetectRad local DetectionRWR = ( DetectRWR and DetectRWR == true ) and Controller.Detection.RWR or nil local DetectionDLINK = ( DetectDLINK and DetectDLINK == true ) and Controller.Detection.DLINK or nil - self:T( { DetectionVisual, DetectionOptical, DetectionRadar, DetectionIRST, DetectionRWR, DetectionDLINK } ) + self:T2( { DetectionVisual, DetectionOptical, DetectionRadar, DetectionIRST, DetectionRWR, DetectionDLINK } ) return self:_GetController():getDetectedTargets( DetectionVisual, DetectionOptical, DetectionRadar, DetectionIRST, DetectionRWR, DetectionDLINK ) end diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 609b1aa91..2705eb51a 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -352,13 +352,39 @@ function GROUP:GetUnits() return nil end + + +--- Returns a list of @{Unit} objects of the @{Group} that are occupied by a player. +-- @param #GROUP self +-- @return #list The list of player occupied @{Unit} objects of the @{Group}. +function GROUP:GetPlayerUnits() + self:F2( { self.GroupName } ) + local DCSGroup = self:GetDCSObject() + + if DCSGroup then + local DCSUnits = DCSGroup:getUnits() + local Units = {} + for Index, UnitData in pairs( DCSUnits ) do + local PlayerUnit = UNIT:Find( UnitData ) + if PlayerUnit:GetPlayerName() then + Units[#Units+1] = PlayerUnit + end + end + self:T3( Units ) + return Units + end + + return nil +end + + --- Returns the UNIT wrapper class with number UnitNumber. -- If the underlying DCS Unit does not exist, the method will return nil. . -- @param #GROUP self -- @param #number UnitNumber The number of the UNIT wrapper class to be returned. -- @return Wrapper.Unit#UNIT The UNIT wrapper class. function GROUP:GetUnit( UnitNumber ) - self:E( { self.GroupName, UnitNumber } ) + self:F3( { self.GroupName, UnitNumber } ) local DCSGroup = self:GetDCSObject() @@ -378,7 +404,7 @@ end -- @param #number UnitNumber The number of the DCS Unit to be returned. -- @return Dcs.DCSWrapper.Unit#Unit The DCS Unit. function GROUP:GetDCSUnit( UnitNumber ) - self:F2( { self.GroupName, UnitNumber } ) + self:F3( { self.GroupName, UnitNumber } ) local DCSGroup = self:GetDCSObject() @@ -396,7 +422,7 @@ end -- @param #GROUP self -- @return #number The DCS Group size. function GROUP:GetSize() - self:F2( { self.GroupName } ) + self:F3( { self.GroupName } ) local DCSGroup = self:GetDCSObject() if DCSGroup then @@ -419,7 +445,7 @@ end -- @param #GROUP self -- @return #number The DCS Group initial size. function GROUP:GetInitialSize() - self:F2( { self.GroupName } ) + self:F3( { self.GroupName } ) local DCSGroup = self:GetDCSObject() if DCSGroup then @@ -1382,6 +1408,8 @@ do -- Players -- @return #nil The group has no players function GROUP:GetPlayerNames() + local HasPlayers = false + local PlayerNames = {} local Units = self:GetUnits() @@ -1391,11 +1419,16 @@ do -- Players if PlayerName and PlayerName ~= "" then PlayerNames = PlayerNames or {} table.insert( PlayerNames, PlayerName ) + HasPlayers = true end end + + if HasPlayers == true then + self:F2( PlayerNames ) + return PlayerNames + end - self:F2( PlayerNames ) - return PlayerNames + return nil end end diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index fe4b8cca0..ebc041a7c 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -349,9 +349,21 @@ function UNIT:GetPlayerName() if DCSUnit then local PlayerName = DCSUnit:getPlayerName() + -- TODO - Workaround for DCS-BUG-3 if PlayerName == nil or PlayerName == "" then - PlayerName = "Player" .. DCSUnit:getID() + local PlayerCategory = DCSUnit:getDesc().category + if PlayerCategory == Unit.Category.GROUND_UNIT or PlayerCategory == Unit.Category.SHIP then + PlayerName = "Player" .. DCSUnit:getID() + end end +-- -- Good code +-- if PlayerName == nil then +-- PlayerName = nil +-- else +-- if PlayerName == "" then +-- PlayerName = "Player" .. DCSUnit:getID() +-- end +-- end return PlayerName end @@ -638,12 +650,9 @@ function UNIT:GetThreatLevel() if Descriptor then local Attributes = Descriptor.attributes - self:T( Attributes ) if self:IsGround() then - self:T( "Ground" ) - local ThreatLevels = { "Unarmed", "Infantry", @@ -680,8 +689,6 @@ function UNIT:GetThreatLevel() if self:IsAir() then - self:T( "Air" ) - local ThreatLevels = { "Unarmed", "Tanker", @@ -714,8 +721,6 @@ function UNIT:GetThreatLevel() if self:IsShip() then - self:T( "Ship" ) - --["Aircraft Carriers"] = {"Heavy armed ships",}, --["Cruisers"] = {"Heavy armed ships",}, --["Destroyers"] = {"Heavy armed ships",}, @@ -753,7 +758,6 @@ function UNIT:GetThreatLevel() end end - self:T2( ThreatLevel ) return ThreatLevel, ThreatText end From 3e6017c0d5d9650b709ecaef3bed58b0f4b232f5 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Thu, 12 Apr 2018 20:03:44 +0200 Subject: [PATCH 051/420] Task Assignment --- .../Moose/Actions/Act_Assign.lua | 61 +++++++++---------- Moose Development/Moose/Core/Fsm.lua | 14 +++++ .../Moose/Tasking/CommandCenter.lua | 49 ++++++++++++--- Moose Development/Moose/Tasking/Mission.lua | 30 ++++++--- Moose Development/Moose/Tasking/Task.lua | 55 +++++++++-------- .../Moose/Tasking/Task_CARGO.lua | 6 +- .../Moose/Wrapper/Positionable.lua | 10 +-- 7 files changed, 144 insertions(+), 81 deletions(-) diff --git a/Moose Development/Moose/Actions/Act_Assign.lua b/Moose Development/Moose/Actions/Act_Assign.lua index 930810ee2..6b2652a59 100644 --- a/Moose Development/Moose/Actions/Act_Assign.lua +++ b/Moose Development/Moose/Actions/Act_Assign.lua @@ -155,8 +155,7 @@ do -- ACT_ASSIGN_ACCEPT -- @param #string Event -- @param #string From -- @param #string To - function ACT_ASSIGN_ACCEPT:onafterStart( ProcessUnit, From, Event, To ) - self:F( { ProcessUnit, From, Event, To } ) + function ACT_ASSIGN_ACCEPT:onafterStart( ProcessUnit, Task, From, Event, To ) self:__Assign( 1 ) end @@ -167,11 +166,8 @@ do -- ACT_ASSIGN_ACCEPT -- @param #string Event -- @param #string From -- @param #string To - function ACT_ASSIGN_ACCEPT:onenterAssigned( ProcessUnit, From, Event, To ) - self:F( { ProcessUnit, From, Event, To } ) + function ACT_ASSIGN_ACCEPT:onenterAssigned( ProcessUnit, Task, From, Event, To ) - local ProcessGroup = ProcessUnit:GetGroup() - self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() ) end @@ -192,36 +188,26 @@ do -- ACT_ASSIGN_MENU_ACCEPT --- Init. -- @param #ACT_ASSIGN_MENU_ACCEPT self - -- @param #string TaskName -- @param #string TaskBriefing -- @return #ACT_ASSIGN_MENU_ACCEPT self - function ACT_ASSIGN_MENU_ACCEPT:New( TaskName, TaskBriefing ) + function ACT_ASSIGN_MENU_ACCEPT:New( TaskBriefing ) -- Inherits from BASE local self = BASE:Inherit( self, ACT_ASSIGN:New() ) -- #ACT_ASSIGN_MENU_ACCEPT - self.TaskName = TaskName self.TaskBriefing = TaskBriefing return self end - function ACT_ASSIGN_MENU_ACCEPT:Init( FsmAssign ) - - self.TaskName = FsmAssign.TaskName - self.TaskBriefing = FsmAssign.TaskBriefing - end - - + --- Creates a new task assignment state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator. -- @param #ACT_ASSIGN_MENU_ACCEPT self - -- @param #string TaskName -- @param #string TaskBriefing -- @return #ACT_ASSIGN_MENU_ACCEPT self - function ACT_ASSIGN_MENU_ACCEPT:Init( TaskName, TaskBriefing ) + function ACT_ASSIGN_MENU_ACCEPT:Init( TaskBriefing ) self.TaskBriefing = TaskBriefing - self.TaskName = TaskName return self end @@ -232,30 +218,31 @@ do -- ACT_ASSIGN_MENU_ACCEPT -- @param #string Event -- @param #string From -- @param #string To - function ACT_ASSIGN_MENU_ACCEPT:onafterStart( ProcessUnit, From, Event, To ) - self:F( { ProcessUnit, From, Event, To } ) + function ACT_ASSIGN_MENU_ACCEPT:onafterStart( ProcessUnit, Task, From, Event, To ) - self:GetCommandCenter():MessageTypeToGroup( "Access the radio menu to accept the task. You have 30 seconds or the assignment will be cancelled.", ProcessUnit:GetGroup(), MESSAGE.Type.Information ) + self:GetCommandCenter():MessageToGroup( "Task " .. self.Task:GetName() .. " has been assigned to you and your group!\nRead the briefing and use the Radio Menu (F10) to accept the task.\nYou have 2 minutes to accept, or the task assignment will be cancelled!", ProcessUnit:GetGroup(), 120 ) local ProcessGroup = ProcessUnit:GetGroup() + + self.Menu = MENU_GROUP:New( ProcessGroup, "Accept task " .. self.Task:GetName() ) + self.MenuAcceptTask = MENU_GROUP_COMMAND:New( ProcessGroup, "Accept task " .. self.Task:GetName(), self.Menu, self.MenuAssign, self ) + self.MenuRejectTask = MENU_GROUP_COMMAND:New( ProcessGroup, "Reject task " .. self.Task:GetName(), self.Menu, self.MenuReject, self ) - self.Menu = MENU_GROUP:New( ProcessGroup, "Task " .. self.TaskName .. " acceptance" ) - self.MenuAcceptTask = MENU_GROUP_COMMAND:New( ProcessGroup, "Accept task " .. self.TaskName, self.Menu, self.MenuAssign, self ) - self.MenuRejectTask = MENU_GROUP_COMMAND:New( ProcessGroup, "Reject task " .. self.TaskName, self.Menu, self.MenuReject, self ) + self:__Reject( 120, ProcessUnit ) end --- Menu function. -- @param #ACT_ASSIGN_MENU_ACCEPT self - function ACT_ASSIGN_MENU_ACCEPT:MenuAssign() + function ACT_ASSIGN_MENU_ACCEPT:MenuAssign( ProcessUnit, Task, From, Event, To ) - self:__Assign( 1 ) + self:__Assign( -1 ) end --- Menu function. -- @param #ACT_ASSIGN_MENU_ACCEPT self - function ACT_ASSIGN_MENU_ACCEPT:MenuReject() + function ACT_ASSIGN_MENU_ACCEPT:MenuReject( ProcessUnit, Task, From, Event, To ) - self:__Reject( 1 ) + self:__Reject( -1 ) end --- StateMachine callback function @@ -264,8 +251,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT -- @param #string Event -- @param #string From -- @param #string To - function ACT_ASSIGN_MENU_ACCEPT:onafterAssign( ProcessUnit, From, Event, To ) - self:F( { ProcessUnit.UnitNameFrom, Event, To } ) + function ACT_ASSIGN_MENU_ACCEPT:onafterAssign( ProcessUnit, Task, From, Event, To ) self.Menu:Remove() end @@ -282,7 +268,18 @@ do -- ACT_ASSIGN_MENU_ACCEPT self.Menu:Remove() --TODO: need to resolve this problem ... it has to do with the events ... --self.Task:UnAssignFromUnit( ProcessUnit )needs to become a callback funtion call upon the event - ProcessUnit:Destroy() + self.Task:Abort() + end + + --- StateMachine callback function + -- @param #ACT_ASSIGN_ACCEPT self + -- @param Wrapper.Unit#UNIT ProcessUnit + -- @param #string Event + -- @param #string From + -- @param #string To + function ACT_ASSIGN_MENU_ACCEPT:onenterAssigned( ProcessUnit, Task, From, Event, To ) + + self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() ) end end -- ACT_ASSIGN_MENU_ACCEPT diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index 12b25179a..4e959004f 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -441,6 +441,8 @@ do -- FSM -- @return #table function FSM:GetProcesses() + self:F( { Processes = self._Processes } ) + return self._Processes or {} end @@ -455,6 +457,18 @@ do -- FSM error( "Sub-Process from state " .. From .. " with event " .. Event .. " not found!" ) end + function FSM:SetProcess( From, Event, Fsm ) + + for ProcessID, Process in pairs( self:GetProcesses() ) do + if Process.From == From and Process.Event == Event then + Process.fsm = Fsm + return true + end + end + + error( "Sub-Process from state " .. From .. " with event " .. Event .. " not found!" ) + end + --- Adds an End state. function FSM:AddEndState( State ) diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index 3f581ec62..217721d53 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -86,7 +86,7 @@ COMMANDCENTER = { -- @return #COMMANDCENTER function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) - local self = BASE:Inherit( self, BASE:New() ) + local self = BASE:Inherit( self, BASE:New() ) -- #COMMANDCENTER self.CommandCenterPositionable = CommandCenterPositionable self.CommandCenterName = CommandCenterName or CommandCenterPositionable:GetName() @@ -241,7 +241,7 @@ end -- @return #list function COMMANDCENTER:GetMissions() - return self.Missions + return self.Missions or {} end --- Add a MISSION to be governed by the HQ command center. @@ -330,7 +330,7 @@ end --- Sets the menu structure of the Missions governed by the HQ command center. -- @param #COMMANDCENTER self function COMMANDCENTER:SetMenu() - self:F() + self:F2() local MenuTime = timer.getTime() for MissionID, Mission in pairs( self:GetMissions() or {} ) do @@ -339,7 +339,7 @@ function COMMANDCENTER:SetMenu() end for MissionID, Mission in pairs( self:GetMissions() or {} ) do - local Mission = Mission -- Tasking.Mission#MISSION + Mission = Mission -- Tasking.Mission#MISSION Mission:RemoveMenu( MenuTime ) end @@ -348,10 +348,43 @@ end --- Gets the commandcenter menu structure governed by the HQ command center. -- @param #COMMANDCENTER self -- @return Core.Menu#MENU_COALITION -function COMMANDCENTER:GetMenu() - return self.CommandCenterMenu +function COMMANDCENTER:GetMenu( TaskGroup ) + + self.CommandCenterMenus = self.CommandCenterMenus or {} + if not self.CommandCenterMenus[TaskGroup] then + local CommandCenterText = self:GetText() + local CommandCenterMenu = MENU_GROUP:New( TaskGroup, CommandCenterText ) + self.CommandCenterMenus[TaskGroup] = CommandCenterMenu + local AssignTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Assign Task", CommandCenterMenu, self.AssignRandomTask, self, TaskGroup ) + end + return self.CommandCenterMenus[TaskGroup] end + +--- Assigns a random task to a TaskGroup. +-- @param #COMMANDCENTER self +-- @return #COMMANDCENTER +function COMMANDCENTER:AssignRandomTask( TaskGroup ) + + local Tasks = {} + + for MissionID, Mission in pairs( self:GetMissions() ) do + local Mission = Mission -- Tasking.Mission#MISSION + local MissionTasks = Mission:GetGroupTasks( TaskGroup ) + for MissionTaskName, MissionTask in pairs( MissionTasks or {} ) do + Tasks[#Tasks+1] = MissionTask + end + end + + local Task = Tasks[ math.random( 1, #Tasks ) ] -- Tasking.Task#TASK + + Task:SetAssignMethod( ACT_ASSIGN_MENU_ACCEPT:New( Task.TaskBriefing ) ) + + Task:AssignToGroup( TaskGroup ) + +end + + --- Checks of the COMMANDCENTER has a GROUP. -- @param #COMMANDCENTER self -- @param Wrapper.Group#GROUP @@ -407,7 +440,7 @@ function COMMANDCENTER:MessageToCoalition( Message ) local CCCoalition = self:GetPositionable():GetCoalition() --TODO: Fix coalition bug! - self:GetPositionable():MessageToCoalition( Message, 15, CCCoalition ) + self:GetPositionable():MessageToCoalition( Message, 15, CCCoalition, self:GetShortText() ) end @@ -421,7 +454,7 @@ function COMMANDCENTER:MessageTypeToCoalition( Message, MessageType ) local CCCoalition = self:GetPositionable():GetCoalition() --TODO: Fix coalition bug! - self:GetPositionable():MessageTypeToCoalition( Message, MessageType, CCCoalition ) + self:GetPositionable():MessageTypeToCoalition( Message, MessageType, CCCoalition, self:GetShortText() ) end diff --git a/Moose Development/Moose/Tasking/Mission.lua b/Moose Development/Moose/Tasking/Mission.lua index 0d872a85b..ca2723b5e 100644 --- a/Moose Development/Moose/Tasking/Mission.lua +++ b/Moose Development/Moose/Tasking/Mission.lua @@ -524,18 +524,13 @@ end function MISSION:GetMenu( TaskGroup ) -- R2.1 -- Changed Menu Structure local CommandCenter = self:GetCommandCenter() - local CommandCenterMenu = CommandCenter:GetMenu() + local CommandCenterMenu = CommandCenter:GetMenu( TaskGroup ) - --local MissionMenu = CommandCenterMenu:GetMenu( MissionName ) - self.MissionGroupMenu = self.MissionGroupMenu or {} self.MissionGroupMenu[TaskGroup] = self.MissionGroupMenu[TaskGroup] or {} local GroupMenu = self.MissionGroupMenu[TaskGroup] - local CommandCenterText = CommandCenter:GetText() - CommandCenterMenu = MENU_GROUP:New( TaskGroup, CommandCenterText ) - local MissionText = self:GetText() self.MissionMenu = MENU_GROUP:New( TaskGroup, MissionText, CommandCenterMenu ) @@ -564,7 +559,7 @@ end -- @param #string TaskName The Name of the @{Task} within the @{Mission}. -- @return Tasking.Task#TASK The Task -- @return #nil Returns nil if no task was found. -function MISSION:GetTask( TaskName ) +function MISSION:GetTask( TaskName ) self:F( { TaskName } ) return self.Tasks[TaskName] @@ -1005,9 +1000,28 @@ end -- env.info( "Task 2 Completion = " .. Tasks[2]:GetGoalPercentage() .. "%" ) function MISSION:GetTasks() - return self.Tasks + return self.Tasks or {} end +--- Get the relevant tasks of a TaskGroup. +-- @param #MISSION +-- @param Wrapper.Group#GROUP TaskGroup +-- @return #list +function MISSION:GetGroupTasks( TaskGroup ) + + local Tasks = {} + + for TaskID, Task in pairs( self:GetTasks() ) do + local Task = Task -- Tasking.Task#TASK + if Task:HasGroup( TaskGroup ) then + Tasks[#Tasks+1] = Task + end + end + + return Tasks +end + + --- Reports the briefing. -- @param #MISSION self -- @param Wrapper.Group#GROUP ReportGroup The group to which the report needs to be sent. diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index 562b9413d..946ad9bca 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -169,10 +169,16 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType, TaskBriefing ) self:AddTransition( "Assigned", "Success", "Success" ) self:AddTransition( "Assigned", "Hold", "Hold" ) self:AddTransition( "Assigned", "Fail", "Failed" ) - self:AddTransition( "Assigned", "Abort", "Aborted" ) + self:AddTransition( { "Planned", "Assigned" }, "Abort", "Aborted" ) self:AddTransition( "Assigned", "Cancel", "Cancelled" ) self:AddTransition( "Assigned", "Goal", "*" ) + self.Fsm = {} + + local Fsm = self:GetUnitProcess() + Fsm:SetStartState( "Planned" ) + Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "SelectAction", Rejected = "Reject" } ) + --- Goal Handler OnBefore for TASK -- @function [parent=#TASK] OnBeforeGoal -- @param #TASK self @@ -216,7 +222,6 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType, TaskBriefing ) self:F( "New TASK " .. TaskName ) self.Processes = {} - self.Fsm = {} self.Mission = Mission self.CommandCenter = Mission:GetCommandCenter() @@ -229,7 +234,6 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType, TaskBriefing ) self:SetBriefing( TaskBriefing ) - self.FsmTemplate = self.FsmTemplate or FSM_PROCESS:New() self.TaskInfo = TASKINFO:New( self ) @@ -246,7 +250,8 @@ function TASK:GetUnitProcess( TaskUnit ) if TaskUnit then return self:GetStateMachine( TaskUnit ) else - return self.FsmTemplate + self.FsmTemplate = self.FsmTemplate or FSM_PROCESS:New() + return self.FsmTemplate end end @@ -500,6 +505,16 @@ end do -- Group Assignment + --- @param #TASK self + -- @param Actions.Act_Assign#ACT_ASSIGN AcceptClass + function TASK:SetAssignMethod( AcceptClass ) + + local ProcessTemplate = self:GetUnitProcess() + + ProcessTemplate:SetProcess( "Planned", "Accept", AcceptClass ) -- Actions.Act_Assign#ACT_ASSIGN + end + + --- Assign the @{Task} to a @{Group}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup @@ -739,21 +754,14 @@ function TASK:SetPlannedMenuForGroup( TaskGroup, MenuTime ) local Mission = self:GetMission() local MissionName = Mission:GetName() - local CommandCenter = Mission:GetCommandCenter() - local CommandCenterMenu = CommandCenter:GetMenu() + local MissionMenu = Mission:GetMenu( TaskGroup ) local TaskType = self:GetType() local TaskPlayerCount = self:GetPlayerCount() local TaskPlayerString = string.format( " (%dp)", TaskPlayerCount ) --- local TaskText = string.format( "%s%s", self:GetName(), TaskPlayerString ) --, TaskThreatLevelString ) local TaskText = string.format( "%s", self:GetName() ) local TaskName = string.format( "%s", self:GetName() ) - local MissionMenu = Mission:GetMenu( TaskGroup ) - --local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ):SetTime( MenuTime ) - - --local MissionMenu = Mission:GetMenu( TaskGroup ) - self.MenuPlanned = self.MenuPlanned or {} self.MenuPlanned[TaskGroup] = MENU_GROUP_DELAYED:New( TaskGroup, "Join Planned Task", MissionMenu, Mission.MenuReportTasksPerStatus, Mission, TaskGroup, "Planned" ):SetTime( MenuTime ):SetTag( "Tasking" ) local TaskTypeMenu = MENU_GROUP_DELAYED:New( TaskGroup, TaskType, self.MenuPlanned[TaskGroup] ):SetTime( MenuTime ):SetTag( "Tasking" ) @@ -778,11 +786,6 @@ end function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime ) self:F( { TaskGroup:GetName(), MenuTime } ) - local Mission = self:GetMission() - local MissionName = Mission:GetName() - local CommandCenter = Mission:GetCommandCenter() - local CommandCenterMenu = CommandCenter:GetMenu() - local TaskType = self:GetType() local TaskPlayerCount = self:GetPlayerCount() local TaskPlayerString = string.format( " (%dp)", TaskPlayerCount ) @@ -831,9 +834,6 @@ function TASK:RefreshMenus( TaskGroup, MenuTime ) local Mission = self:GetMission() local MissionName = Mission:GetName() - local CommandCenter = Mission:GetCommandCenter() - local CommandCenterMenu = CommandCenter:GetMenu() - local MissionMenu = Mission:GetMenu( TaskGroup ) local TaskName = self:GetName() @@ -865,7 +865,6 @@ function TASK:RemoveAssignedMenuForGroup( TaskGroup ) local Mission = self:GetMission() local MissionName = Mission:GetName() - local MissionMenu = Mission:GetMenu( TaskGroup ) if MissionMenu then @@ -1223,12 +1222,16 @@ function TASK:onenterAssigned( From, Event, To, PlayerUnit, PlayerName ) --- This test is required, because the state transition will be fired also when the state does not change in case of an event. if From ~= "Assigned" then - self:F( { From, Event, To, PlayerUnit:GetName(), PlayerName } ) - self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " is assigned." ) - + local PlayerNames = self:GetPlayerNames() + local PlayerText = REPORT:New() + for PlayerName, TaskName in pairs( PlayerNames ) do + PlayerText:Add( PlayerName ) + end + + self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " is assigned to players " .. PlayerText:Text(",") .. ". Good Luck!" ) + -- Set the total Progress to be achieved. - self:SetGoalTotal() -- Polymorphic to set the initial goal total! if self.Dispatcher then @@ -1244,7 +1247,7 @@ function TASK:onenterAssigned( From, Event, To, PlayerUnit, PlayerName ) self:SetMenu() self:F( { "--> Task Assigned", TaskName = self:GetName(), Mission = self:GetMission():GetName() } ) - self:F( { "--> Task Player Names", PlayerNames = self:GetPlayerNames() } ) + self:F( { "--> Task Player Names", PlayerNames = PlayerNames } ) end end diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 4d523aba9..0d2a5d869 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -215,9 +215,9 @@ do -- TASK_CARGO local Fsm = self:GetUnitProcess() - Fsm:SetStartState( "Planned" ) - - Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "SelectAction", Rejected = "Reject" } ) +-- Fsm:SetStartState( "Planned" ) +-- +-- Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "SelectAction", Rejected = "Reject" } ) Fsm:AddTransition( { "Planned", "Assigned", "Cancelled", "WaitingForCommand", "ArrivedAtPickup", "ArrivedAtDeploy", "Boarded", "UnBoarded", "Loaded", "UnLoaded", "Landed", "Boarding" }, "SelectAction", "*" ) diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index b28bb5933..2a6605bb9 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -538,10 +538,11 @@ end -- @param #string Message The message text -- @param Dcs.DCSTYpes#Duration Duration The duration of the message. -- @param Dcs.DCScoalition#coalition MessageCoalition The Coalition receiving the message. -function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition ) +-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. +function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition, Name ) self:F2( { Message, Duration } ) - local Name = "" + local Name = Name or "" local DCSObject = self:GetDCSObject() if DCSObject then @@ -558,10 +559,11 @@ end -- @param #string Message The message text -- @param Core.Message#MESSAGE.Type MessageType The message type that determines the duration. -- @param Dcs.DCScoalition#coalition MessageCoalition The Coalition receiving the message. -function POSITIONABLE:MessageTypeToCoalition( Message, MessageType, MessageCoalition ) +-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. +function POSITIONABLE:MessageTypeToCoalition( Message, MessageType, MessageCoalition, Name ) self:F2( { Message, MessageType } ) - local Name = "" + local Name = Name or "" local DCSObject = self:GetDCSObject() if DCSObject then From 25ae0c3d1570396653b3563d029b3ced7b95e281 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Fri, 13 Apr 2018 09:56:00 +0200 Subject: [PATCH 052/420] Finish this feature. --- .../Moose/Actions/Act_Assign.lua | 31 +++--- Moose Development/Moose/Core/Menu.lua | 8 +- .../Moose/Tasking/CommandCenter.lua | 102 +++++++++++++++++- Moose Development/Moose/Tasking/Mission.lua | 24 +++-- Moose Development/Moose/Tasking/Task.lua | 93 ++++++++++++---- Moose Development/Moose/Wrapper/Group.lua | 20 ++++ Moose Development/Moose/Wrapper/Unit.lua | 2 +- 7 files changed, 228 insertions(+), 52 deletions(-) diff --git a/Moose Development/Moose/Actions/Act_Assign.lua b/Moose Development/Moose/Actions/Act_Assign.lua index 6b2652a59..0bb843ab1 100644 --- a/Moose Development/Moose/Actions/Act_Assign.lua +++ b/Moose Development/Moose/Actions/Act_Assign.lua @@ -220,29 +220,29 @@ do -- ACT_ASSIGN_MENU_ACCEPT -- @param #string To function ACT_ASSIGN_MENU_ACCEPT:onafterStart( ProcessUnit, Task, From, Event, To ) - self:GetCommandCenter():MessageToGroup( "Task " .. self.Task:GetName() .. " has been assigned to you and your group!\nRead the briefing and use the Radio Menu (F10) to accept the task.\nYou have 2 minutes to accept, or the task assignment will be cancelled!", ProcessUnit:GetGroup(), 120 ) + self:GetCommandCenter():MessageToGroup( "Task " .. self.Task:GetName() .. " has been assigned to you and your group!\nRead the briefing and use the Radio Menu (F10) / Task ... CONFIRMATION menu to accept or reject the task.\nYou have 2 minutes to accept, or the task assignment will be cancelled!", ProcessUnit:GetGroup(), 120 ) - local ProcessGroup = ProcessUnit:GetGroup() + local TaskGroup = ProcessUnit:GetGroup() - self.Menu = MENU_GROUP:New( ProcessGroup, "Accept task " .. self.Task:GetName() ) - self.MenuAcceptTask = MENU_GROUP_COMMAND:New( ProcessGroup, "Accept task " .. self.Task:GetName(), self.Menu, self.MenuAssign, self ) - self.MenuRejectTask = MENU_GROUP_COMMAND:New( ProcessGroup, "Reject task " .. self.Task:GetName(), self.Menu, self.MenuReject, self ) + self.Menu = MENU_GROUP:New( TaskGroup, "Task " .. self.Task:GetName() .. " CONFIRMATION" ) + self.MenuAcceptTask = MENU_GROUP_COMMAND:New( TaskGroup, "Accept task " .. self.Task:GetName(), self.Menu, self.MenuAssign, self, TaskGroup ) + self.MenuRejectTask = MENU_GROUP_COMMAND:New( TaskGroup, "Reject task " .. self.Task:GetName(), self.Menu, self.MenuReject, self, TaskGroup ) - self:__Reject( 120, ProcessUnit ) + self:__Reject( 120, TaskGroup ) end --- Menu function. -- @param #ACT_ASSIGN_MENU_ACCEPT self - function ACT_ASSIGN_MENU_ACCEPT:MenuAssign( ProcessUnit, Task, From, Event, To ) + function ACT_ASSIGN_MENU_ACCEPT:MenuAssign( TaskGroup ) - self:__Assign( -1 ) + self:__Assign( -1, TaskGroup ) end --- Menu function. -- @param #ACT_ASSIGN_MENU_ACCEPT self - function ACT_ASSIGN_MENU_ACCEPT:MenuReject( ProcessUnit, Task, From, Event, To ) + function ACT_ASSIGN_MENU_ACCEPT:MenuReject( TaskGroup ) - self:__Reject( -1 ) + self:__Reject( -1, TaskGroup ) end --- StateMachine callback function @@ -251,7 +251,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT -- @param #string Event -- @param #string From -- @param #string To - function ACT_ASSIGN_MENU_ACCEPT:onafterAssign( ProcessUnit, Task, From, Event, To ) + function ACT_ASSIGN_MENU_ACCEPT:onafterAssign( ProcessUnit, Task, From, Event, To, TaskGroup ) self.Menu:Remove() end @@ -262,13 +262,13 @@ do -- ACT_ASSIGN_MENU_ACCEPT -- @param #string Event -- @param #string From -- @param #string To - function ACT_ASSIGN_MENU_ACCEPT:onafterReject( ProcessUnit, From, Event, To ) - self:F( { ProcessUnit.UnitName, From, Event, To } ) + function ACT_ASSIGN_MENU_ACCEPT:onafterReject( ProcessUnit, Task, From, Event, To, TaskGroup ) + self:F( { TaskGroup = TaskGroup } ) self.Menu:Remove() --TODO: need to resolve this problem ... it has to do with the events ... --self.Task:UnAssignFromUnit( ProcessUnit )needs to become a callback funtion call upon the event - self.Task:Abort() + self.Task:RejectGroup( TaskGroup ) end --- StateMachine callback function @@ -277,8 +277,9 @@ do -- ACT_ASSIGN_MENU_ACCEPT -- @param #string Event -- @param #string From -- @param #string To - function ACT_ASSIGN_MENU_ACCEPT:onenterAssigned( ProcessUnit, Task, From, Event, To ) + function ACT_ASSIGN_MENU_ACCEPT:onenterAssigned( ProcessUnit, Task, From, Event, To, TaskGroup ) + --self.Task:AssignToGroup( TaskGroup ) self.Task:Assign( ProcessUnit, ProcessUnit:GetPlayerName() ) end diff --git a/Moose Development/Moose/Core/Menu.lua b/Moose Development/Moose/Core/Menu.lua index a435f16cb..65c0025cd 100644 --- a/Moose Development/Moose/Core/Menu.lua +++ b/Moose Development/Moose/Core/Menu.lua @@ -917,8 +917,8 @@ do self:RemoveSubMenus( MenuTime, MenuTag ) if not MenuTime or self.MenuTime ~= MenuTime then if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then - self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } ) if self.MenuPath ~= nil then + self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } ) missionCommands.removeItemForGroup( self.GroupID, self.MenuPath ) end MENU_INDEX:ClearGroupMenu( self.Group, Path ) @@ -1009,8 +1009,8 @@ do if GroupMenu == self then if not MenuTime or self.MenuTime ~= MenuTime then if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then - self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } ) if self.MenuPath ~= nil then + self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } ) missionCommands.removeItemForGroup( self.GroupID, self.MenuPath ) end MENU_INDEX:ClearGroupMenu( self.Group, Path ) @@ -1150,8 +1150,8 @@ do self:RemoveSubMenus( MenuTime, MenuTag ) if not MenuTime or self.MenuTime ~= MenuTime then if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then - self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } ) if self.MenuPath ~= nil then + self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } ) missionCommands.removeItemForGroup( self.GroupID, self.MenuPath ) end MENU_INDEX:ClearGroupMenu( self.Group, Path ) @@ -1261,8 +1261,8 @@ do if GroupMenu == self then if not MenuTime or self.MenuTime ~= MenuTime then if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then - self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } ) if self.MenuPath ~= nil then + self:F( { Group = self.GroupID, Text = self.MenuText, Path = self.MenuPath } ) missionCommands.removeItemForGroup( self.GroupID, self.MenuPath ) end MENU_INDEX:ClearGroupMenu( self.Group, Path ) diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index 217721d53..1e4452bec 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -91,6 +91,8 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) self.CommandCenterPositionable = CommandCenterPositionable self.CommandCenterName = CommandCenterName or CommandCenterPositionable:GetName() self.CommandCenterCoalition = CommandCenterPositionable:GetCoalition() + + self.AutoAssignTasks = false self.Missions = {} @@ -350,13 +352,23 @@ end -- @return Core.Menu#MENU_COALITION function COMMANDCENTER:GetMenu( TaskGroup ) + local MenuTime = timer.getTime() + self.CommandCenterMenus = self.CommandCenterMenus or {} - if not self.CommandCenterMenus[TaskGroup] then - local CommandCenterText = self:GetText() - local CommandCenterMenu = MENU_GROUP:New( TaskGroup, CommandCenterText ) - self.CommandCenterMenus[TaskGroup] = CommandCenterMenu - local AssignTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Assign Task", CommandCenterMenu, self.AssignRandomTask, self, TaskGroup ) + local CommandCenterMenu + + local CommandCenterText = self:GetText() + CommandCenterMenu = MENU_GROUP:New( TaskGroup, CommandCenterText ):SetTime(MenuTime) + self.CommandCenterMenus[TaskGroup] = CommandCenterMenu + + if self.AutoAssignTasks == false then + local AutoAssignTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Assign Task On", CommandCenterMenu, self.SetAutoAssignTasks, self, true ):SetTime(MenuTime):SetTag("AutoTask") + local AssignTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Assign Task", CommandCenterMenu, self.AssignRandomTask, self, TaskGroup ):SetTime(MenuTime):SetTag("AutoTask") + else + local AutoAssignTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Assign Task Off", CommandCenterMenu, self.SetAutoAssignTasks, self, false ):SetTime(MenuTime):SetTag("AutoTask") end + CommandCenterMenu:Remove( MenuTime, "AutoTask" ) + return self.CommandCenterMenus[TaskGroup] end @@ -385,6 +397,86 @@ function COMMANDCENTER:AssignRandomTask( TaskGroup ) end +--- Automatically assigns tasks to all TaskGroups. +-- @param #COMMANDCENTER self +-- @param #boolean AutoAssign true for ON and false or nil for OFF. +-- @return #COMMANDCENTER +function COMMANDCENTER:SetAutoAssignTasks( AutoAssign ) + + self.AutoAssignTasks = AutoAssign or false + + local GroupSet = self:AddGroups() + + for GroupID, TaskGroup in pairs( GroupSet:GetSet() ) do + local TaskGroup = TaskGroup -- Wrapper.Group#GROUP + self:GetMenu( TaskGroup ) + end + + if self.AutoAssignTasks == true then + self:ScheduleRepeat( 10, 30, 0, nil, self.AssignTasks, self ) + else + self:ScheduleStop( self.AssignTasks ) + end + +end + + +--- Automatically assigns tasks to all TaskGroups. +-- @param #COMMANDCENTER self +function COMMANDCENTER:AssignTasks() + + local GroupSet = self:AddGroups() + + for GroupID, TaskGroup in pairs( GroupSet:GetSet() ) do + local TaskGroup = TaskGroup -- Wrapper.Group#GROUP + + if self:IsGroupAssigned( TaskGroup ) then + else + -- Only groups with planes or helicopters will receive automatic tasks. + -- TODO Workaround DCS-BUG-3 - https://github.com/FlightControl-Master/MOOSE/issues/696 + if TaskGroup:IsAir() then + self:AssignRandomTask( TaskGroup ) + end + end + end +end + + +--- Get all the Groups active within the command center. +-- @param #COMMANDCENTER self +-- @return Core.Set#SET_GROUP +function COMMANDCENTER:AddGroups() + + local GroupSet = SET_GROUP:New() + + for MissionID, Mission in pairs( self.Missions ) do + local Mission = Mission -- Tasking.Mission#MISSION + GroupSet = Mission:AddGroups( GroupSet ) + end + + return GroupSet +end + + +--- Checks of the TaskGroup has a Task. +-- @param #COMMANDCENTER self +-- @return #boolean +function COMMANDCENTER:IsGroupAssigned( TaskGroup ) + + local Assigned = false + + for MissionID, Mission in pairs( self.Missions ) do + local Mission = Mission -- Tasking.Mission#MISSION + if Mission:IsGroupAssigned( TaskGroup ) then + Assigned = true + break + end + end + + return Assigned +end + + --- Checks of the COMMANDCENTER has a GROUP. -- @param #COMMANDCENTER self -- @param Wrapper.Group#GROUP diff --git a/Moose Development/Moose/Tasking/Mission.lua b/Moose Development/Moose/Tasking/Mission.lua index ca2723b5e..86ba1d410 100644 --- a/Moose Development/Moose/Tasking/Mission.lua +++ b/Moose Development/Moose/Tasking/Mission.lua @@ -377,24 +377,30 @@ function MISSION:GetScoring() return self.Scoring end ---- Get the groups for which TASKS are given in the mission +--- Gets the groups for which TASKS are given in the mission -- @param #MISSION self +-- @param Core.Set#SET_GROUP GroupSet -- @return Core.Set#SET_GROUP function MISSION:GetGroups() - local SetGroup = SET_GROUP:New() + return self:AddGroups() + +end + +--- Adds the groups for which TASKS are given in the mission +-- @param #MISSION self +-- @param Core.Set#SET_GROUP GroupSet +-- @return Core.Set#SET_GROUP +function MISSION:AddGroups( GroupSet ) + + GroupSet = GroupSet or SET_GROUP:New() for TaskID, Task in pairs( self:GetTasks() ) do local Task = Task -- Tasking.Task#TASK - local GroupSet = Task:GetGroups() - GroupSet:ForEachGroup( - function( TaskGroup ) - SetGroup:Add( TaskGroup, TaskGroup ) - end - ) + GroupSet = Task:AddGroups( GroupSet ) end - return SetGroup + return GroupSet end diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index 946ad9bca..4203dc08a 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -215,6 +215,7 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType, TaskBriefing ) self:AddTransition( "*", "PlayerCrashed", "*" ) self:AddTransition( "*", "PlayerAborted", "*" ) + self:AddTransition( "*", "PlayerRejected", "*" ) self:AddTransition( "*", "PlayerDead", "*" ) self:AddTransition( { "Failed", "Aborted", "Cancelled" }, "Replan", "Planned" ) self:AddTransition( "*", "TimeOut", "Cancelled" ) @@ -300,34 +301,61 @@ function TASK:JoinUnit( PlayerUnit, PlayerGroup ) return PlayerUnitAdded end ---- Abort a PlayerUnit from a Task. --- If the Unit was not part of the Task, false is returned. --- If the Unit is part of the Task, true is returned. +--- A group rejecting a planned task. -- @param #TASK self --- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player aborting the Task. +-- @param Wrapper.Group#GROUP PlayerGroup The group rejecting the task. -- @return #TASK -function TASK:AbortGroup( PlayerGroup ) - self:F( { PlayerGroup = PlayerGroup } ) +function TASK:RejectGroup( PlayerGroup ) local PlayerGroups = self:GetGroups() -- Is the PlayerGroup part of the PlayerGroups? if PlayerGroups:IsIncludeObject( PlayerGroup ) then - -- Check if the PlayerGroup is already assigned to the Task. If yes, the PlayerGroup is aborted from the Task. + -- Check if the PlayerGroup is already assigned or is planned to be assigned to the Task. + -- If yes, the PlayerGroup is aborted from the Task. -- If the PlayerUnit was the last unit of the PlayerGroup, the menu needs to be removed from the Group. - if self:IsStateAssigned() then + if self:IsStatePlanned() then + + local IsGroupAssigned = self:IsGroupAssigned( PlayerGroup ) + if IsGroupAssigned then + local PlayerName = PlayerGroup:GetUnit(1):GetPlayerName() + self:GetMission():GetCommandCenter():MessageToGroup( "Task " .. self:GetName() .. " has been rejected! We will select another task.", PlayerGroup ) + self:UnAssignFromGroup( PlayerGroup ) + + self:PlayerRejected( PlayerGroup:GetUnit(1) ) + end + + end + end + + return self +end + + +--- A group aborting the task. +-- @param #TASK self +-- @param Wrapper.Group#GROUP PlayerGroup The group aborting the task. +-- @return #TASK +function TASK:AbortGroup( PlayerGroup ) + + local PlayerGroups = self:GetGroups() + + -- Is the PlayerGroup part of the PlayerGroups? + if PlayerGroups:IsIncludeObject( PlayerGroup ) then + + -- Check if the PlayerGroup is already assigned or is planned to be assigned to the Task. + -- If yes, the PlayerGroup is aborted from the Task. + -- If the PlayerUnit was the last unit of the PlayerGroup, the menu needs to be removed from the Group. + if self:IsStateAssigned() then + local IsGroupAssigned = self:IsGroupAssigned( PlayerGroup ) - self:F( { IsGroupAssigned = IsGroupAssigned } ) if IsGroupAssigned then local PlayerName = PlayerGroup:GetUnit(1):GetPlayerName() - --self:MessageToGroups( PlayerName .. " aborted Task " .. self:GetName() ) self:UnAssignFromGroup( PlayerGroup ) - --self:Abort() -- Now check if the task needs to go to hold... -- It will go to hold, if there are no players in the mission... - PlayerGroups:Flush( self ) local IsRemaining = false for GroupName, AssignedGroup in pairs( PlayerGroups:GetSet() or {} ) do @@ -352,11 +380,10 @@ function TASK:AbortGroup( PlayerGroup ) return self end ---- A PlayerUnit crashed in a Task. Abort the Player. --- If the Unit was not part of the Task, false is returned. --- If the Unit is part of the Task, true is returned. + +--- A group crashing and thus aborting from the task. -- @param #TASK self --- @param Wrapper.Unit#UNIT PlayerUnit The CLIENT or UNIT of the Player aborting the Task. +-- @param Wrapper.Group#GROUP PlayerGroup The group aborting the task. -- @return #TASK function TASK:CrashGroup( PlayerGroup ) self:F( { PlayerGroup = PlayerGroup } ) @@ -418,9 +445,29 @@ end -- @param #TASK self -- @return Core.Set#SET_GROUP function TASK:GetGroups() + return self.SetGroup end + +--- Gets the SET_GROUP assigned to the TASK. +-- @param #TASK self +-- @param Core.Set#SET_GROUP GroupSet +-- @return Core.Set#SET_GROUP +function TASK:AddGroups( GroupSet ) + + GroupSet = GroupSet or SET_GROUP:New() + + self.SetGroup:ForEachGroup( + --- @param Wrapper.Group#GROUP GroupSet + function( GroupItem ) + GroupSet:Add( GroupItem:GetName(), GroupItem) + end + ) + + return GroupSet +end + do -- Group Assignment --- Returns if the @{Task} is assigned to the Group. @@ -797,7 +844,9 @@ function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime ) if TaskUnit then local MenuControl = self:GetTaskControlMenu( TaskUnit ) local TaskControl = MENU_GROUP:New( TaskGroup, "Control Task", MenuControl ):SetTime( MenuTime ):SetTag( "Tasking" ) - local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Abort Task" ), TaskControl, self.MenuTaskAbort, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ) + if self:IsStateAssigned() then + local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Abort Task" ), TaskControl, self.MenuTaskAbort, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ) + end local MarkMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Mark Task Location on Map" ), TaskControl, self.MenuMarkToGroup, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ) local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Report Task Details" ), TaskControl, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ) end @@ -1289,6 +1338,7 @@ function TASK:onenterAborted( From, Event, To ) end + --- FSM function for a TASK -- @param #TASK self -- @param #string From @@ -1641,7 +1691,14 @@ do -- Task Control Menu TaskName = TaskName or "" - self.TaskControlMenu = MENU_GROUP:New( TaskUnit:GetGroup(), "Assigned Task " .. TaskUnit:GetPlayerName() .. " - " .. self:GetName() .. " " .. TaskName ):SetTime( self.TaskControlMenuTime ) + local TaskGroup = TaskUnit:GetGroup() + local TaskPlayerCount = TaskGroup:GetPlayerCount() + + if TaskPlayerCount <= 1 then + self.TaskControlMenu = MENU_GROUP:New( TaskUnit:GetGroup(), "Task " .. self:GetName() .. " control" ):SetTime( self.TaskControlMenuTime ) + else + self.TaskControlMenu = MENU_GROUP:New( TaskUnit:GetGroup(), "Task " .. self:GetName() .. " control for " .. TaskUnit:GetPlayerName() ):SetTime( self.TaskControlMenuTime ) + end return self.TaskControlMenu end diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 2705eb51a..be59a7723 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1430,6 +1430,26 @@ do -- Players return nil end + + + --- Get the active player count in the group. + -- @param #GROUP self + -- @return #number The amount of players. + function GROUP:GetPlayerCount() + + local PlayerCount = 0 + + local Units = self:GetUnits() + for UnitID, UnitData in pairs( Units or {} ) do + local Unit = UnitData -- Wrapper.Unit#UNIT + local PlayerName = Unit:GetPlayerName() + if PlayerName and PlayerName ~= "" then + PlayerCount = PlayerCount + 1 + end + end + + return PlayerCount + end end diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index ebc041a7c..2734279bd 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -349,7 +349,7 @@ function UNIT:GetPlayerName() if DCSUnit then local PlayerName = DCSUnit:getPlayerName() - -- TODO - Workaround for DCS-BUG-3 + -- TODO Workaround DCS-BUG-3 - https://github.com/FlightControl-Master/MOOSE/issues/696 if PlayerName == nil or PlayerName == "" then local PlayerCategory = DCSUnit:getDesc().category if PlayerCategory == Unit.Category.GROUND_UNIT or PlayerCategory == Unit.Category.SHIP then From 47dd73a37751d1fec11338da87ba467777ccb5db Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Fri, 13 Apr 2018 12:22:39 +0200 Subject: [PATCH 053/420] Fixes --- Moose Development/Moose/Functional/Scoring.lua | 2 +- Moose Development/Moose/Tasking/Task.lua | 3 ++- Moose Development/Moose/Tasking/Task_A2A.lua | 11 +++++++++-- Moose Development/Moose/Tasking/Task_A2G.lua | 15 ++++++++++++--- Moose Development/Moose/Tasking/Task_CARGO.lua | 11 +++++++++++ .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 3 ++- .../Moose/Tasking/Task_Cargo_Transport.lua | 2 +- 7 files changed, 38 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Functional/Scoring.lua b/Moose Development/Moose/Functional/Scoring.lua index cab7839ae..2ca3fd71a 100644 --- a/Moose Development/Moose/Functional/Scoring.lua +++ b/Moose Development/Moose/Functional/Scoring.lua @@ -858,7 +858,7 @@ function SCORING:OnEventBirth( Event ) if Event.IniUnit then if Event.IniObjectCategory == 1 then local PlayerName = Event.IniUnit:GetPlayerName() - if PlayerName ~= "" then + if PlayerName then self:_AddPlayerFromUnit( Event.IniUnit ) self:SetScoringMenu( Event.IniGroup ) end diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index 4203dc08a..d854431a5 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -177,7 +177,8 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType, TaskBriefing ) local Fsm = self:GetUnitProcess() Fsm:SetStartState( "Planned" ) - Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "SelectAction", Rejected = "Reject" } ) + Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "Assigned", Rejected = "Reject" } ) + Fsm:AddTransition( "Assigned", "Assigned", "*" ) --- Goal Handler OnBefore for TASK -- @function [parent=#TASK] OnBeforeGoal diff --git a/Moose Development/Moose/Tasking/Task_A2A.lua b/Moose Development/Moose/Tasking/Task_A2A.lua index 4db992c40..0ebc3551a 100644 --- a/Moose Development/Moose/Tasking/Task_A2A.lua +++ b/Moose Development/Moose/Tasking/Task_A2A.lua @@ -62,8 +62,6 @@ do -- TASK_A2A local Fsm = self:GetUnitProcess() - Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "RouteToRendezVous", Rejected = "Reject" } ) - Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" ) Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } ) Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } ) @@ -84,6 +82,15 @@ do -- TASK_A2A Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) Fsm:AddTransition( "Failed", "Fail", "Failed" ) + + ---- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param #TASK_CARGO Task + function Fsm:OnLeaveAssigned( TaskUnit, Task ) + self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + + self:SelectAction() + end --- Test -- @param #FSM_PROCESS self diff --git a/Moose Development/Moose/Tasking/Task_A2G.lua b/Moose Development/Moose/Tasking/Task_A2G.lua index 6ecb4ce78..6b3f28986 100644 --- a/Moose Development/Moose/Tasking/Task_A2G.lua +++ b/Moose Development/Moose/Tasking/Task_A2G.lua @@ -61,9 +61,6 @@ do -- TASK_A2G local Fsm = self:GetUnitProcess() - - Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "RouteToRendezVous", Rejected = "Reject" } ) - Fsm:AddTransition( "Assigned", "RouteToRendezVous", "RoutingToRendezVous" ) Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousPoint", ACT_ROUTE_POINT:New(), { Arrived = "ArriveAtRendezVous" } ) Fsm:AddProcess ( "RoutingToRendezVous", "RouteToRendezVousZone", ACT_ROUTE_ZONE:New(), { Arrived = "ArriveAtRendezVous" } ) @@ -84,6 +81,18 @@ do -- TASK_A2G Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) Fsm:AddTransition( "Failed", "Fail", "Failed" ) + + + --- Test + -- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param Tasking.Task_A2G#TASK_A2G Task + function Fsm:onafterAssigned( TaskUnit, Task ) + self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + -- Determine the first Unit from the self.RendezVousSetUnit + + self:RouteToRendezVous() + end --- Test -- @param #FSM_PROCESS self diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 0d2a5d869..47ff9a6ee 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -252,6 +252,17 @@ do -- TASK_CARGO Fsm:AddTransition( "Deployed", "Success", "Success" ) Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) Fsm:AddTransition( "Failed", "Fail", "Failed" ) + + + ---- @param #FSM_PROCESS self + -- @param Wrapper.Unit#UNIT TaskUnit + -- @param #TASK_CARGO Task + function Fsm:OnAfterAssigned( TaskUnit, Task ) + self:F( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } ) + + self:SelectAction() + end + --- diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index bdac4b931..7b3967d5e 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -439,6 +439,7 @@ do -- TASK_CARGO_DISPATCHER function TASK_CARGO_DISPATCHER:AddTransportTask( TaskName, SetCargo, Briefing ) self.TransportCount = self.TransportCount + 1 + local TaskName = string.format( ( TaskName or "Transport" ) .. ".%03d", self.TransportCount ) self.Transport[TaskName] = {} @@ -446,7 +447,7 @@ do -- TASK_CARGO_DISPATCHER self.Transport[TaskName].Briefing = Briefing self.Transport[TaskName].Task = nil - return self + return TaskName end diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua index 611eefa49..21e296860 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua @@ -10,7 +10,7 @@ do -- TASK_CARGO_TRANSPORT --- The TASK_CARGO_TRANSPORT class -- @type TASK_CARGO_TRANSPORT - -- @extends Tasking.Task_Cargo#TASK_CARGO + -- @extends Tasking.Task_CARGO#TASK_CARGO TASK_CARGO_TRANSPORT = { ClassName = "TASK_CARGO_TRANSPORT", } From b33dd94fa0d8d887254e223edb6579cc5d8ae5fd Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Fri, 13 Apr 2018 13:57:47 +0200 Subject: [PATCH 054/420] Fixed a bug in Spawn regarding spawning with templates. --- Moose Development/Moose/Core/Spawn.lua | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 1e4640a62..5797ce1d0 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1880,7 +1880,10 @@ function SPAWN:_GetTemplate( SpawnTemplatePrefix ) local SpawnTemplate = nil - SpawnTemplate = routines.utils.deepCopy( _DATABASE.Templates.Groups[SpawnTemplatePrefix].Template ) + local Template = _DATABASE.Templates.Groups[SpawnTemplatePrefix].Template + self:F( { Template = Template } ) + + SpawnTemplate = UTILS.DeepCopy( _DATABASE.Templates.Groups[SpawnTemplatePrefix].Template ) if SpawnTemplate == nil then error( 'No Template returned for SpawnTemplatePrefix = ' .. SpawnTemplatePrefix ) @@ -1902,11 +1905,12 @@ end function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) --R2.2 self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix } ) - if not self.SpawnTemplate then - self.SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix ) - end +-- if not self.SpawnTemplate then +-- self.SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix ) +-- end - local SpawnTemplate = self.SpawnTemplate + local SpawnTemplate = self:_GetTemplate( SpawnTemplatePrefix ) + --local SpawnTemplate = self.SpawnTemplate SpawnTemplate.name = self:SpawnGroupName( SpawnIndex ) SpawnTemplate.groupId = nil @@ -1999,7 +2003,7 @@ function SPAWN:_RandomizeTemplate( SpawnIndex ) if self.SpawnRandomizeTemplate then self.SpawnGroups[SpawnIndex].SpawnTemplatePrefix = self.SpawnTemplatePrefixTable[ math.random( 1, #self.SpawnTemplatePrefixTable ) ] self.SpawnGroups[SpawnIndex].SpawnTemplate = self:_Prepare( self.SpawnGroups[SpawnIndex].SpawnTemplatePrefix, SpawnIndex ) - self.SpawnGroups[SpawnIndex].SpawnTemplate.route = routines.utils.deepCopy( self.SpawnTemplate.route ) + self.SpawnGroups[SpawnIndex].SpawnTemplate.route = UTILS.DeepCopy( self.SpawnTemplate.route ) self.SpawnGroups[SpawnIndex].SpawnTemplate.x = self.SpawnTemplate.x self.SpawnGroups[SpawnIndex].SpawnTemplate.y = self.SpawnTemplate.y self.SpawnGroups[SpawnIndex].SpawnTemplate.start_time = self.SpawnTemplate.start_time From f01b2c9149c83039685d40ee01b6042cd3e938ae Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Fri, 13 Apr 2018 20:35:38 +0200 Subject: [PATCH 055/420] Finish Cargo/AI_Cargo_APC --- .../{AI_Cargo_Troops.lua => AI_Cargo_APC.lua} | 80 +++---- .../Moose/AI/AI_Cargo_Helicopter.lua | 209 ++++++++++++++++++ Moose Setup/Moose.files | 3 +- 3 files changed, 251 insertions(+), 41 deletions(-) rename Moose Development/Moose/AI/{AI_Cargo_Troops.lua => AI_Cargo_APC.lua} (81%) create mode 100644 Moose Development/Moose/AI/AI_Cargo_Helicopter.lua diff --git a/Moose Development/Moose/AI/AI_Cargo_Troops.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua similarity index 81% rename from Moose Development/Moose/AI/AI_Cargo_Troops.lua rename to Moose Development/Moose/AI/AI_Cargo_APC.lua index 2051fa22d..2c34615cd 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Troops.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -6,9 +6,9 @@ -- -- === -- --- @module AI_Cargo_Troops +-- @module AI_Cargo_APC ---- @type AI_CARGO_TROOPS +--- @type AI_CARGO_APC -- @extends Core.Fsm#FSM_CONTROLLABLE @@ -16,21 +16,21 @@ -- -- === -- --- @field #AI_CARGO_TROOPS -AI_CARGO_TROOPS = { - ClassName = "AI_CARGO_TROOPS", +-- @field #AI_CARGO_APC +AI_CARGO_APC = { + ClassName = "AI_CARGO_APC", Coordinate = nil -- Core.Point#COORDINATE, } ---- Creates a new AI_CARGO_TROOPS object. --- @param #AI_CARGO_TROOPS self +--- Creates a new AI_CARGO_APC object. +-- @param #AI_CARGO_APC self -- @param Wrapper.Unit#UNIT CargoCarrier -- @param Cargo.CargoGroup#CARGO_GROUP CargoGroup -- @param #number CombatRadius --- @return #AI_CARGO_TROOPS -function AI_CARGO_TROOPS:New( CargoCarrier, CargoGroup, CombatRadius ) +-- @return #AI_CARGO_APC +function AI_CARGO_APC:New( CargoCarrier, CargoGroup, CombatRadius ) - local self = BASE:Inherit( self, FSM_CONTROLLABLE:New( ) ) -- #AI_CARGO_TROOPS + local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_CARGO_APC self.CargoGroup = CargoGroup -- Cargo.CargoGroup#CARGO_GROUP self.CombatRadius = CombatRadius @@ -59,20 +59,20 @@ end --- Set the Carrier. --- @param #AI_CARGO_TROOPS self +-- @param #AI_CARGO_APC self -- @param Wrapper.Unit#UNIT CargoCarrier --- @return #AI_CARGO_TROOPS -function AI_CARGO_TROOPS:SetCarrier( CargoCarrier ) +-- @return #AI_CARGO_APC +function AI_CARGO_APC:SetCarrier( CargoCarrier ) self.CargoCarrier = CargoCarrier -- Wrapper.Unit#UNIT - self.CargoCarrier:SetState( self.CargoCarrier, "AI_CARGO_TROOPS", self ) + self.CargoCarrier:SetState( self.CargoCarrier, "AI_CARGO_APC", self ) CargoCarrier:HandleEvent( EVENTS.Dead ) CargoCarrier:HandleEvent( EVENTS.Hit ) function CargoCarrier:OnEventDead( EventData ) self:F({"dead"}) - local AICargoTroops = self:GetState( self, "AI_CARGO_TROOPS" ) + local AICargoTroops = self:GetState( self, "AI_CARGO_APC" ) self:F({AICargoTroops=AICargoTroops}) if AICargoTroops then self:F({}) @@ -85,7 +85,7 @@ function AI_CARGO_TROOPS:SetCarrier( CargoCarrier ) function CargoCarrier:OnEventHit( EventData ) self:F({"hit"}) - local AICargoTroops = self:GetState( self, "AI_CARGO_TROOPS" ) + local AICargoTroops = self:GetState( self, "AI_CARGO_APC" ) if AICargoTroops then self:F( { OnHitLoaded = AICargoTroops:Is( "Loaded" ) } ) if AICargoTroops:Is( "Loaded" ) or AICargoTroops:Is( "Boarding" ) then @@ -107,18 +107,18 @@ end --- Find a free Carrier within a range. --- @param #AI_CARGO_TROOPS self +-- @param #AI_CARGO_APC self -- @param Core.Point#COORDINATE Coordinate -- @param #number Radius -- @return Wrapper.Unit#UNIT NewCarrier -function AI_CARGO_TROOPS:FindCarrier( Coordinate, Radius ) +function AI_CARGO_APC:FindCarrier( Coordinate, Radius ) local CoordinateZone = ZONE_RADIUS:New( "Zone" , Coordinate:GetVec2(), Radius ) CoordinateZone:Scan( { Object.Category.UNIT } ) for _, DCSUnit in pairs( CoordinateZone:GetScannedUnits() ) do local NearUnit = UNIT:Find( DCSUnit ) self:F({NearUnit=NearUnit}) - if not NearUnit:GetState( NearUnit, "AI_CARGO_TROOPS" ) then + if not NearUnit:GetState( NearUnit, "AI_CARGO_APC" ) then local Attributes = NearUnit:GetDesc() self:F({Desc=Attributes}) if NearUnit:HasAttribute( "Trucks" ) then @@ -133,12 +133,12 @@ end --- Follow Infantry to the Carrier. --- @param #AI_CARGO_TROOPS self --- @param #AI_CARGO_TROOPS Me +-- @param #AI_CARGO_APC self +-- @param #AI_CARGO_APC Me -- @param Wrapper.Unit#UNIT CargoCarrier -- @param Wrapper.Group#GROUP InfantryGroup --- @return #AI_CARGO_TROOPS -function AI_CARGO_TROOPS:FollowToCarrier( Me, CargoCarrier, InfantryGroup ) +-- @return #AI_CARGO_APC +function AI_CARGO_APC:FollowToCarrier( Me, CargoCarrier, InfantryGroup ) self:F( { self = self:GetClassNameAndID(), InfantryGroup = InfantryGroup:GetName() } ) @@ -172,7 +172,7 @@ function AI_CARGO_TROOPS:FollowToCarrier( Me, CargoCarrier, InfantryGroup ) self:F({ToGround=ToGround}) table.insert( Waypoints, ToGround ) - local TaskRoute = InfantryGroup:TaskFunction( "AI_CARGO_TROOPS.FollowToCarrier", Me, CargoCarrier, InfantryGroup ) + local TaskRoute = InfantryGroup:TaskFunction( "AI_CARGO_APC.FollowToCarrier", Me, CargoCarrier, InfantryGroup ) self:F({Waypoints = Waypoints}) local Waypoint = Waypoints[#Waypoints] @@ -185,9 +185,9 @@ function AI_CARGO_TROOPS:FollowToCarrier( Me, CargoCarrier, InfantryGroup ) end ---- @param #AI_CARGO_TROOPS self +--- @param #AI_CARGO_APC self -- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_TROOPS:onafterMonitor( CargoCarrier, From, Event, To ) +function AI_CARGO_APC:onafterMonitor( CargoCarrier, From, Event, To ) self:F( { CargoCarrier, From, Event, To } ) if CargoCarrier and CargoCarrier:IsAlive() then @@ -235,9 +235,9 @@ function AI_CARGO_TROOPS:onafterMonitor( CargoCarrier, From, Event, To ) end ---- @param #AI_CARGO_TROOPS self +--- @param #AI_CARGO_APC self -- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_TROOPS:onafterLoad( CargoCarrier, From, Event, To ) +function AI_CARGO_APC:onafterLoad( CargoCarrier, From, Event, To ) self:F( { CargoCarrier, From, Event, To } ) if CargoCarrier and CargoCarrier:IsAlive() then @@ -248,9 +248,9 @@ function AI_CARGO_TROOPS:onafterLoad( CargoCarrier, From, Event, To ) end ---- @param #AI_CARGO_TROOPS self +--- @param #AI_CARGO_APC self -- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_TROOPS:onafterBoard( CargoCarrier, From, Event, To ) +function AI_CARGO_APC:onafterBoard( CargoCarrier, From, Event, To ) self:F( { CargoCarrier, From, Event, To } ) if CargoCarrier and CargoCarrier:IsAlive() then @@ -264,9 +264,9 @@ function AI_CARGO_TROOPS:onafterBoard( CargoCarrier, From, Event, To ) end ---- @param #AI_CARGO_TROOPS self +--- @param #AI_CARGO_APC self -- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_TROOPS:onafterLoaded( CargoCarrier, From, Event, To ) +function AI_CARGO_APC:onafterLoaded( CargoCarrier, From, Event, To ) self:F( { CargoCarrier, From, Event, To } ) if CargoCarrier and CargoCarrier:IsAlive() then @@ -276,9 +276,9 @@ function AI_CARGO_TROOPS:onafterLoaded( CargoCarrier, From, Event, To ) end ---- @param #AI_CARGO_TROOPS self +--- @param #AI_CARGO_APC self -- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_TROOPS:onafterUnload( CargoCarrier, From, Event, To ) +function AI_CARGO_APC:onafterUnload( CargoCarrier, From, Event, To ) self:F( { CargoCarrier, From, Event, To } ) if CargoCarrier and CargoCarrier:IsAlive() then @@ -289,9 +289,9 @@ function AI_CARGO_TROOPS:onafterUnload( CargoCarrier, From, Event, To ) end ---- @param #AI_CARGO_TROOPS self +--- @param #AI_CARGO_APC self -- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_TROOPS:onafterUnboard( CargoCarrier, From, Event, To ) +function AI_CARGO_APC:onafterUnboard( CargoCarrier, From, Event, To ) self:F( { CargoCarrier, From, Event, To } ) if CargoCarrier and CargoCarrier:IsAlive() then @@ -304,9 +304,9 @@ function AI_CARGO_TROOPS:onafterUnboard( CargoCarrier, From, Event, To ) end ---- @param #AI_CARGO_TROOPS self +--- @param #AI_CARGO_APC self -- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_TROOPS:onafterUnloaded( CargoCarrier, From, Event, To ) +function AI_CARGO_APC:onafterUnloaded( CargoCarrier, From, Event, To ) self:F( { CargoCarrier, From, Event, To } ) if CargoCarrier and CargoCarrier:IsAlive() then @@ -318,9 +318,9 @@ function AI_CARGO_TROOPS:onafterUnloaded( CargoCarrier, From, Event, To ) end ---- @param #AI_CARGO_TROOPS self +--- @param #AI_CARGO_APC self -- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_TROOPS:onafterFollow( CargoCarrier, From, Event, To ) +function AI_CARGO_APC:onafterFollow( CargoCarrier, From, Event, To ) self:F( { CargoCarrier, From, Event, To } ) self:F( "Follow" ) diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua new file mode 100644 index 000000000..bf6f3dfe6 --- /dev/null +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -0,0 +1,209 @@ +--- **AI** -- (R2.3) - Models the intelligent transportation of infantry (cargo). +-- +-- === +-- +-- ### Author: **FlightControl** +-- +-- === +-- +-- @module AI_Cargo_Helicopter + +--- @type AI_CARGO_HELICOPTER +-- @extends Core.Fsm#FSM_CONTROLLABLE + + +--- # AI\_CARGO\_TROOPS class, extends @{Core.Base@BASE} +-- +-- === +-- +-- @field #AI_CARGO_HELICOPTER +AI_CARGO_HELICOPTER = { + ClassName = "AI_CARGO_HELICOPTER", + Coordinate = nil -- Core.Point#COORDINATE, +} + +--- Creates a new AI_CARGO_HELICOPTER object. +-- @param #AI_CARGO_HELICOPTER self +-- @param Wrapper.Unit#UNIT CargoCarrier +-- @param Cargo.CargoGroup#CARGO_GROUP CargoGroup +-- @param #number CombatRadius +-- @return #AI_CARGO_HELICOPTER +function AI_CARGO_HELICOPTER:New( CargoCarrier, CargoGroup ) + + local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_CARGO_HELICOPTER + + self.CargoGroup = CargoGroup -- Cargo.CargoGroup#CARGO_GROUP + + self:SetStartState( "UnLoaded" ) + + self:AddTransition( "*", "Load", "Boarding" ) + self:AddTransition( "Boarding", "Board", "Boarding" ) + self:AddTransition( "Boarding", "Loaded", "Loaded" ) + self:AddTransition( "Loaded", "Unload", "Unboarding" ) + self:AddTransition( "Unboarding", "Unboard", "Unboarding" ) + self:AddTransition( "Unboarding", "Unloaded", "Unloaded" ) + + self:AddTransition( "*", "Landed", "*" ) + + self:AddTransition( "*", "Destroyed", "Destroyed" ) + + self:__Monitor( 1 ) + + self:SetCarrier( CargoCarrier ) + + return self +end + + +--- Set the Carrier. +-- @param #AI_CARGO_HELICOPTER self +-- @param Wrapper.Unit#UNIT CargoCarrier +-- @return #AI_CARGO_HELICOPTER +function AI_CARGO_HELICOPTER:SetCarrier( CargoCarrier ) + + self.CargoCarrier = CargoCarrier -- Wrapper.Unit#UNIT + self.CargoCarrier:SetState( self.CargoCarrier, "AI_CARGO_HELICOPTER", self ) + + CargoCarrier:HandleEvent( EVENTS.Dead ) + CargoCarrier:HandleEvent( EVENTS.Hit ) + CargoCarrier:HandleEvent( EVENTS.Land ) + + function CargoCarrier:OnEventDead( EventData ) + local AICargoTroops = self:GetState( self, "AI_CARGO_HELICOPTER" ) + self:F({AICargoTroops=AICargoTroops}) + if AICargoTroops then + self:F({}) + if not AICargoTroops:Is( "Loaded" ) then + -- There are enemies within combat range. Unload the CargoCarrier. + AICargoTroops:Destroyed() + end + end + end + + + function CargoCarrier:OnEventHit( EventData ) + local AICargoTroops = self:GetState( self, "AI_CARGO_HELICOPTER" ) + if AICargoTroops then + self:F( { OnHitLoaded = AICargoTroops:Is( "Loaded" ) } ) + if AICargoTroops:Is( "Loaded" ) or AICargoTroops:Is( "Boarding" ) then + -- There are enemies within combat range. Unload the CargoCarrier. + AICargoTroops:Unload() + end + end + end + + + function CargoCarrier:OnEventLand( EventData ) + self:Landed() + end + + self.Zone = ZONE_UNIT:New( self.CargoCarrier:GetName() .. "-Zone", self.CargoCarrier, 500 ) + self.Coalition = self.CargoCarrier:GetCoalition() + + self:SetControllable( CargoCarrier ) + + self:Guard() + + return self +end + + +--- Find a free Carrier within a range. +-- @param #AI_CARGO_HELICOPTER self +-- @param Core.Point#COORDINATE Coordinate +-- @param #number Radius +-- @return Wrapper.Unit#UNIT NewCarrier +function AI_CARGO_HELICOPTER:FindCarrier( Coordinate, Radius ) + + local CoordinateZone = ZONE_RADIUS:New( "Zone" , Coordinate:GetVec2(), Radius ) + CoordinateZone:Scan( { Object.Category.UNIT } ) + for _, DCSUnit in pairs( CoordinateZone:GetScannedUnits() ) do + local NearUnit = UNIT:Find( DCSUnit ) + self:F({NearUnit=NearUnit}) + if not NearUnit:GetState( NearUnit, "AI_CARGO_HELICOPTER" ) then + local Attributes = NearUnit:GetDesc() + self:F({Desc=Attributes}) + if NearUnit:HasAttribute( "Trucks" ) then + self:SetCarrier( NearUnit ) + break + end + end + end + +end + + +--- @param #AI_CARGO_HELICOPTER self +-- @param Wrapper.Unit#UNIT CargoCarrier +function AI_CARGO_HELICOPTER:onafterLoad( CargoCarrier, From, Event, To ) + + if CargoCarrier and CargoCarrier:IsAlive() then + self:__Board( 10 ) + self.CargoGroup:Board( CargoCarrier, 10 ) + end + +end + +--- @param #AI_CARGO_HELICOPTER self +-- @param Wrapper.Unit#UNIT CargoCarrier +function AI_CARGO_HELICOPTER:onafterBoard( CargoCarrier, From, Event, To ) + + if CargoCarrier and CargoCarrier:IsAlive() then + self:F({ IsLoaded = self.CargoGroup:IsLoaded() } ) + if not self.CargoGroup:IsLoaded() then + self:__Board( 10 ) + else + self:__Loaded( 1 ) + end + end + +end + +--- @param #AI_CARGO_HELICOPTER self +-- @param Wrapper.Unit#UNIT CargoCarrier +function AI_CARGO_HELICOPTER:onafterLoaded( CargoCarrier, From, Event, To ) + + if CargoCarrier and CargoCarrier:IsAlive() then + CargoCarrier:RouteResume() + end + +end + + +--- @param #AI_CARGO_HELICOPTER self +-- @param Wrapper.Unit#UNIT CargoCarrier +function AI_CARGO_HELICOPTER:onafterUnload( CargoCarrier, From, Event, To ) + + if CargoCarrier and CargoCarrier:IsAlive() then + CargoCarrier:RouteStop() + self.CargoGroup:UnBoard( ) + self:__Unboard( 10 ) + end + +end + +--- @param #AI_CARGO_HELICOPTER self +-- @param Wrapper.Unit#UNIT CargoCarrier +function AI_CARGO_HELICOPTER:onafterUnboard( CargoCarrier, From, Event, To ) + + if CargoCarrier and CargoCarrier:IsAlive() then + if not self.CargoGroup:IsUnLoaded() then + self:__Unboard( 10 ) + else + self:__Unloaded( 1 ) + end + end + +end + +--- @param #AI_CARGO_HELICOPTER self +-- @param Wrapper.Unit#UNIT CargoCarrier +function AI_CARGO_HELICOPTER:onafterUnloaded( CargoCarrier, From, Event, To ) + + if CargoCarrier and CargoCarrier:IsAlive() then + self.CargoCarrier = CargoCarrier + end + +end + + diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index c206b5416..2f3d08e7c 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -66,7 +66,8 @@ AI/AI_Cap.lua AI/AI_Cas.lua AI/AI_Bai.lua AI/AI_Formation.lua -AI/AI_Cargo_Troops.lua +AI/AI_Cargo_APC.lua +AI/AI_Cargo_Helicopter.lua Actions/Act_Assign.lua Actions/Act_Route.lua From 5988ceec05a0a9c8d178dedf4504889b20b1cad0 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Fri, 13 Apr 2018 22:31:19 +0200 Subject: [PATCH 056/420] A new AI Cargo Helicopter class. --- .../Moose/AI/AI_Cargo_Helicopter.lua | 308 +++++++++++++++--- 1 file changed, 255 insertions(+), 53 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index bf6f3dfe6..05afee6a4 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -24,19 +24,22 @@ AI_CARGO_HELICOPTER = { --- Creates a new AI_CARGO_HELICOPTER object. -- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Unit#UNIT CargoCarrier --- @param Cargo.CargoGroup#CARGO_GROUP CargoGroup +-- @param Wrapper.Unit#UNIT Helicopter +-- @param Core.Set#SET_CARGO CargoSet -- @param #number CombatRadius -- @return #AI_CARGO_HELICOPTER -function AI_CARGO_HELICOPTER:New( CargoCarrier, CargoGroup ) +function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_CARGO_HELICOPTER - self.CargoGroup = CargoGroup -- Cargo.CargoGroup#CARGO_GROUP + self.CargoSet = CargoSet -- Cargo.CargoGroup#CARGO_GROUP - self:SetStartState( "UnLoaded" ) + self:SetStartState( "Unloaded" ) - self:AddTransition( "*", "Load", "Boarding" ) + self:AddTransition( "Unloaded", "Pickup", "*" ) + self:AddTransition( "Loaded", "Deploy", "*" ) + + self:AddTransition( "Unloaded", "Load", "Boarding" ) self:AddTransition( "Boarding", "Board", "Boarding" ) self:AddTransition( "Boarding", "Loaded", "Loaded" ) self:AddTransition( "Loaded", "Unload", "Unboarding" ) @@ -47,9 +50,65 @@ function AI_CARGO_HELICOPTER:New( CargoCarrier, CargoGroup ) self:AddTransition( "*", "Destroyed", "Destroyed" ) - self:__Monitor( 1 ) + --- Pickup Handler OnBefore for AI_CARGO_HELICOPTER + -- @function [parent=#AI_CARGO_HELICOPTER] OnBeforePickup + -- @param #AI_CARGO_HELICOPTER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Core.Point#COORDINATE Coordinate + -- @return #boolean + + --- Pickup Handler OnAfter for AI_CARGO_HELICOPTER + -- @function [parent=#AI_CARGO_HELICOPTER] OnAfterPickup + -- @param #AI_CARGO_HELICOPTER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Core.Point#COORDINATE Coordinate + + --- Pickup Trigger for AI_CARGO_HELICOPTER + -- @function [parent=#AI_CARGO_HELICOPTER] Pickup + -- @param #AI_CARGO_HELICOPTER self + -- @param Core.Point#COORDINATE Coordinate + + --- Pickup Asynchronous Trigger for AI_CARGO_HELICOPTER + -- @function [parent=#AI_CARGO_HELICOPTER] __Pickup + -- @param #AI_CARGO_HELICOPTER self + -- @param #number Delay + -- @param Core.Point#COORDINATE Coordinate + + --- Deploy Handler OnBefore for AI_CARGO_HELICOPTER + -- @function [parent=#AI_CARGO_HELICOPTER] OnBeforeDeploy + -- @param #AI_CARGO_HELICOPTER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Core.Point#COORDINATE Coordinate + -- @return #boolean + + --- Deploy Handler OnAfter for AI_CARGO_HELICOPTER + -- @function [parent=#AI_CARGO_HELICOPTER] OnAfterDeploy + -- @param #AI_CARGO_HELICOPTER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Core.Point#COORDINATE Coordinate + + --- Deploy Trigger for AI_CARGO_HELICOPTER + -- @function [parent=#AI_CARGO_HELICOPTER] Deploy + -- @param #AI_CARGO_HELICOPTER self + -- @param Core.Point#COORDINATE Coordinate + + --- Deploy Asynchronous Trigger for AI_CARGO_HELICOPTER + -- @function [parent=#AI_CARGO_HELICOPTER] __Deploy + -- @param #AI_CARGO_HELICOPTER self + -- @param Core.Point#COORDINATE Coordinate + -- @param #number Delay - self:SetCarrier( CargoCarrier ) + + + self:SetCarrier( Helicopter ) return self end @@ -57,52 +116,54 @@ end --- Set the Carrier. -- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Unit#UNIT CargoCarrier +-- @param Wrapper.Unit#UNIT Helicopter -- @return #AI_CARGO_HELICOPTER -function AI_CARGO_HELICOPTER:SetCarrier( CargoCarrier ) +function AI_CARGO_HELICOPTER:SetCarrier( Helicopter ) - self.CargoCarrier = CargoCarrier -- Wrapper.Unit#UNIT - self.CargoCarrier:SetState( self.CargoCarrier, "AI_CARGO_HELICOPTER", self ) + local AICargo = self - CargoCarrier:HandleEvent( EVENTS.Dead ) - CargoCarrier:HandleEvent( EVENTS.Hit ) - CargoCarrier:HandleEvent( EVENTS.Land ) + self.Helicopter = Helicopter -- Wrapper.Unit#UNIT + self.Helicopter:SetState( self.Helicopter, "AI_CARGO_HELICOPTER", self ) + + self.RoutePickup = false + self.RouteDeploy = false + + Helicopter:HandleEvent( EVENTS.Dead ) + Helicopter:HandleEvent( EVENTS.Hit ) + Helicopter:HandleEvent( EVENTS.Land ) - function CargoCarrier:OnEventDead( EventData ) + function Helicopter:OnEventDead( EventData ) local AICargoTroops = self:GetState( self, "AI_CARGO_HELICOPTER" ) self:F({AICargoTroops=AICargoTroops}) if AICargoTroops then self:F({}) if not AICargoTroops:Is( "Loaded" ) then - -- There are enemies within combat range. Unload the CargoCarrier. + -- There are enemies within combat range. Unload the Helicopter. AICargoTroops:Destroyed() end end end - function CargoCarrier:OnEventHit( EventData ) + function Helicopter:OnEventHit( EventData ) local AICargoTroops = self:GetState( self, "AI_CARGO_HELICOPTER" ) if AICargoTroops then self:F( { OnHitLoaded = AICargoTroops:Is( "Loaded" ) } ) if AICargoTroops:Is( "Loaded" ) or AICargoTroops:Is( "Boarding" ) then - -- There are enemies within combat range. Unload the CargoCarrier. + -- There are enemies within combat range. Unload the Helicopter. AICargoTroops:Unload() end end end - function CargoCarrier:OnEventLand( EventData ) - self:Landed() + function Helicopter:OnEventLand( EventData ) + AICargo:Landed() end - self.Zone = ZONE_UNIT:New( self.CargoCarrier:GetName() .. "-Zone", self.CargoCarrier, 500 ) - self.Coalition = self.CargoCarrier:GetCoalition() + self.Coalition = self.Helicopter:GetCoalition() - self:SetControllable( CargoCarrier ) - - self:Guard() + self:SetControllable( Helicopter ) return self end @@ -132,25 +193,168 @@ function AI_CARGO_HELICOPTER:FindCarrier( Coordinate, Radius ) end +--- @param #AI_CARGO_HELICOPTER self +-- @param Wrapper.Unit#UNIT Helicopter +-- @param From +-- @param Event +-- @param To +-- @param Core.Point#COORDINATE Coordinate +-- @param #number Speed +function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To ) + + if Helicopter and Helicopter:IsAlive() then + + if self.RoutePickup == true then + self:Load( Helicopter:GetPointVec2() ) + self.RoutePickup = false + end + + if self.RouteDeploy == true then + self:Unload() + self.RouteDeploy = false + end + + end + +end + + --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_HELICOPTER:onafterLoad( CargoCarrier, From, Event, To ) +-- @param Wrapper.Unit#UNIT Helicopter +-- @param From +-- @param Event +-- @param To +-- @param Core.Point#COORDINATE Coordinate +-- @param #number Speed +function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordinate, Speed ) - if CargoCarrier and CargoCarrier:IsAlive() then - self:__Board( 10 ) - self.CargoGroup:Board( CargoCarrier, 10 ) + if Helicopter and Helicopter:IsAlive() then + + self.RoutePickup = true + + local Route = {} + + --- Calculate the target route point. + local CoordinateFrom = Helicopter:GetCoordinate() + local CoordinateTo = Coordinate + + --- Create a route point of type air. + local WaypointFrom = CoordinateFrom:WaypointAir( + "RADIO", + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + Speed, + true + ) + + --- Create a route point of type air. + local WaypointTo = CoordinateTo:WaypointAir( + "RADIO", + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + Speed, + true + ) + + Route[#Route+1] = WaypointFrom + Route[#Route+1] = WaypointTo + + --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... + Helicopter:WayPointInitialize( Route ) + + local Tasks = {} + + Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() ) + Route[#Route].task = Helicopter:TaskCombo( Tasks ) + + -- Now route the helicopter + Helicopter:Route( Route, 0.5 ) + end + +end + + +--- @param #AI_CARGO_HELICOPTER self +-- @param Wrapper.Unit#UNIT Helicopter +-- @param From +-- @param Event +-- @param To +-- @param Core.Point#COORDINATE Coordinate +-- @param #number Speed +function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordinate, Speed ) + + if Helicopter and Helicopter:IsAlive() then + + self.RouteDeploy = true + + local Route = {} + + --- Calculate the target route point. + local CoordinateFrom = Helicopter:GetCoordinate() + local CoordinateTo = Coordinate + + --- Create a route point of type air. + local WaypointFrom = CoordinateFrom:WaypointAir( + "RADIO", + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + Speed, + true + ) + + --- Create a route point of type air. + local WaypointTo = CoordinateTo:WaypointAir( + "RADIO", + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + Speed, + true + ) + + Route[#Route+1] = WaypointFrom + Route[#Route+1] = WaypointTo + + --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... + Helicopter:WayPointInitialize( Route ) + + local Tasks = {} + + Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() ) + Route[#Route].task = Helicopter:TaskCombo( Tasks ) + + -- Now route the helicopter + Helicopter:Route( Route, 0.5 ) + end + +end + + +--- @param #AI_CARGO_HELICOPTER self +-- @param Wrapper.Unit#UNIT Helicopter +function AI_CARGO_HELICOPTER:onafterLoad( Helicopter, From, Event, To, Coordinate ) + + if Helicopter and Helicopter:IsAlive() then + + for _, Cargo in pairs( self.CargoSet:GetSet() ) do + if Cargo:IsInLoadRadius( Coordinate ) then + self:__Board( 5 ) + Cargo:Board( Helicopter, 25 ) + self.Cargo = Cargo + break + end + end end end --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_HELICOPTER:onafterBoard( CargoCarrier, From, Event, To ) +-- @param Wrapper.Unit#UNIT Helicopter +function AI_CARGO_HELICOPTER:onafterBoard( Helicopter, From, Event, To ) - if CargoCarrier and CargoCarrier:IsAlive() then - self:F({ IsLoaded = self.CargoGroup:IsLoaded() } ) - if not self.CargoGroup:IsLoaded() then + if Helicopter and Helicopter:IsAlive() then + self:F({ IsLoaded = self.Cargo:IsLoaded() } ) + if not self.Cargo:IsLoaded() then self:__Board( 10 ) else self:__Loaded( 1 ) @@ -160,34 +364,32 @@ function AI_CARGO_HELICOPTER:onafterBoard( CargoCarrier, From, Event, To ) end --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_HELICOPTER:onafterLoaded( CargoCarrier, From, Event, To ) +-- @param Wrapper.Unit#UNIT Helicopter +function AI_CARGO_HELICOPTER:onafterLoaded( Helicopter, From, Event, To ) - if CargoCarrier and CargoCarrier:IsAlive() then - CargoCarrier:RouteResume() + if Helicopter and Helicopter:IsAlive() then end end --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_HELICOPTER:onafterUnload( CargoCarrier, From, Event, To ) +-- @param Wrapper.Unit#UNIT Helicopter +function AI_CARGO_HELICOPTER:onafterUnload( Helicopter, From, Event, To ) - if CargoCarrier and CargoCarrier:IsAlive() then - CargoCarrier:RouteStop() - self.CargoGroup:UnBoard( ) + if Helicopter and Helicopter:IsAlive() then + self.Cargo:UnBoard() self:__Unboard( 10 ) end end --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_HELICOPTER:onafterUnboard( CargoCarrier, From, Event, To ) +-- @param Wrapper.Unit#UNIT Helicopter +function AI_CARGO_HELICOPTER:onafterUnboard( Helicopter, From, Event, To ) - if CargoCarrier and CargoCarrier:IsAlive() then - if not self.CargoGroup:IsUnLoaded() then + if Helicopter and Helicopter:IsAlive() then + if not self.Cargo:IsUnLoaded() then self:__Unboard( 10 ) else self:__Unloaded( 1 ) @@ -197,11 +399,11 @@ function AI_CARGO_HELICOPTER:onafterUnboard( CargoCarrier, From, Event, To ) end --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_HELICOPTER:onafterUnloaded( CargoCarrier, From, Event, To ) +-- @param Wrapper.Unit#UNIT Helicopter +function AI_CARGO_HELICOPTER:onafterUnloaded( Helicopter, From, Event, To ) - if CargoCarrier and CargoCarrier:IsAlive() then - self.CargoCarrier = CargoCarrier + if Helicopter and Helicopter:IsAlive() then + self.Helicopter = Helicopter end end From 1444a613c5fb9f620e4bb9a922be5be16f091884 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sat, 14 Apr 2018 12:02:48 +0200 Subject: [PATCH 057/420] New AI_Cargo_Airplane file --- .../Moose/AI/AI_Cargo_Airplane.lua | 338 ++++++++++++++++++ Moose Setup/Moose.files | 1 + 2 files changed, 339 insertions(+) create mode 100644 Moose Development/Moose/AI/AI_Cargo_Airplane.lua diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua new file mode 100644 index 000000000..0d8411544 --- /dev/null +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -0,0 +1,338 @@ +--- **AI** -- (R2.3) - Models the intelligent transportation of infantry (cargo). +-- +-- === +-- +-- ### Author: **FlightControl** +-- +-- === +-- +-- @module AI_Cargo_Airplane + +--- @type AI_CARGO_AIRPLANE +-- @extends Core.Fsm#FSM_CONTROLLABLE + + +--- # AI\_CARGO\_AIRPLANE class, extends @{Core.Base@BASE} +-- +-- === +-- +-- @field #AI_CARGO_AIRPLANE +AI_CARGO_AIRPLANE = { + ClassName = "AI_CARGO_AIRPLANE", + Coordinate = nil -- Core.Point#COORDINATE, +} + +--- Creates a new AI_CARGO_AIRPLANE object. +-- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Unit#UNIT Airplane +-- @param Core.Set#SET_CARGO CargoSet +-- @param #number CombatRadius +-- @return #AI_CARGO_AIRPLANE +function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) + + local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_CARGO_AIRPLANE + + self.CargoSet = CargoSet -- Cargo.CargoGroup#CARGO_GROUP + + self:SetStartState( "Unloaded" ) + + self:AddTransition( "Unloaded", "Pickup", "*" ) + self:AddTransition( "Loaded", "Deploy", "*" ) + + self:AddTransition( "Unloaded", "Load", "Boarding" ) + self:AddTransition( "Boarding", "Board", "Boarding" ) + self:AddTransition( "Boarding", "Loaded", "Loaded" ) + self:AddTransition( "Loaded", "Unload", "Unboarding" ) + self:AddTransition( "Unboarding", "Unboard", "Unboarding" ) + self:AddTransition( "Unboarding", "Unloaded", "Unloaded" ) + + self:AddTransition( "*", "Landed", "*" ) + + self:AddTransition( "*", "Destroyed", "Destroyed" ) + + --- Pickup Handler OnBefore for AI_CARGO_AIRPLANE + -- @function [parent=#AI_CARGO_AIRPLANE] OnBeforePickup + -- @param #AI_CARGO_AIRPLANE self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Wrapper.Airbase#AIRBASE Airbase + -- @return #boolean + + --- Pickup Handler OnAfter for AI_CARGO_AIRPLANE + -- @function [parent=#AI_CARGO_AIRPLANE] OnAfterPickup + -- @param #AI_CARGO_AIRPLANE self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Wrapper.Airbase#AIRBASE Airbase + + --- Pickup Trigger for AI_CARGO_AIRPLANE + -- @function [parent=#AI_CARGO_AIRPLANE] Pickup + -- @param #AI_CARGO_AIRPLANE self + -- @param Wrapper.Airbase#AIRBASE Airbase + + --- Pickup Asynchronous Trigger for AI_CARGO_AIRPLANE + -- @function [parent=#AI_CARGO_AIRPLANE] __Pickup + -- @param #AI_CARGO_AIRPLANE self + -- @param #number Delay + -- @param Wrapper.Airbase#AIRBASE Airbase + + --- Deploy Handler OnBefore for AI_CARGO_AIRPLANE + -- @function [parent=#AI_CARGO_AIRPLANE] OnBeforeDeploy + -- @param #AI_CARGO_AIRPLANE self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Wrapper.Airbase#AIRBASE Airbase + -- @return #boolean + + --- Deploy Handler OnAfter for AI_CARGO_AIRPLANE + -- @function [parent=#AI_CARGO_AIRPLANE] OnAfterDeploy + -- @param #AI_CARGO_AIRPLANE self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Wrapper.Airbase#AIRBASE Airbase + + --- Deploy Trigger for AI_CARGO_AIRPLANE + -- @function [parent=#AI_CARGO_AIRPLANE] Deploy + -- @param #AI_CARGO_AIRPLANE self + -- @param Wrapper.Airbase#AIRBASE Airbase + + --- Deploy Asynchronous Trigger for AI_CARGO_AIRPLANE + -- @function [parent=#AI_CARGO_AIRPLANE] __Deploy + -- @param #AI_CARGO_AIRPLANE self + -- @param Wrapper.Airbase#AIRBASE Airbase + -- @param #number Delay + + + self:SetCarrier( Airplane ) + + return self +end + + +--- Set the Carrier. +-- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Unit#UNIT Airplane +-- @return #AI_CARGO_AIRPLANE +function AI_CARGO_AIRPLANE:SetCarrier( Airplane ) + + local AICargo = self + + self.Airplane = Airplane -- Wrapper.Unit#UNIT + self.Airplane:SetState( self.Airplane, "AI_CARGO_AIRPLANE", self ) + + self.RoutePickup = false + self.RouteDeploy = false + + Airplane:HandleEvent( EVENTS.Dead ) + Airplane:HandleEvent( EVENTS.Hit ) + Airplane:HandleEvent( EVENTS.EngineShutdown ) + + function Airplane:OnEventDead( EventData ) + local AICargoTroops = self:GetState( self, "AI_CARGO_AIRPLANE" ) + self:F({AICargoTroops=AICargoTroops}) + if AICargoTroops then + self:F({}) + if not AICargoTroops:Is( "Loaded" ) then + -- There are enemies within combat range. Unload the Airplane. + AICargoTroops:Destroyed() + end + end + end + + + function Airplane:OnEventHit( EventData ) + local AICargoTroops = self:GetState( self, "AI_CARGO_AIRPLANE" ) + if AICargoTroops then + self:F( { OnHitLoaded = AICargoTroops:Is( "Loaded" ) } ) + if AICargoTroops:Is( "Loaded" ) or AICargoTroops:Is( "Boarding" ) then + -- There are enemies within combat range. Unload the Airplane. + AICargoTroops:Unload() + end + end + end + + + function Airplane:OnEventEngineShutdown( EventData ) + AICargo:Landed() + end + + self.Coalition = self.Airplane:GetCoalition() + + self:SetControllable( Airplane ) + + return self +end + + +--- Find a free Carrier within a range. +-- @param #AI_CARGO_AIRPLANE self + -- @param Wrapper.Airbase#AIRBASE Airbase +-- @param #number Radius +-- @return Wrapper.Unit#UNIT NewCarrier +function AI_CARGO_AIRPLANE:FindCarrier( Coordinate, Radius ) + + local CoordinateZone = ZONE_RADIUS:New( "Zone" , Coordinate:GetVec2(), Radius ) + CoordinateZone:Scan( { Object.Category.UNIT } ) + for _, DCSUnit in pairs( CoordinateZone:GetScannedUnits() ) do + local NearUnit = UNIT:Find( DCSUnit ) + self:F({NearUnit=NearUnit}) + if not NearUnit:GetState( NearUnit, "AI_CARGO_AIRPLANE" ) then + local Attributes = NearUnit:GetDesc() + self:F({Desc=Attributes}) + if NearUnit:HasAttribute( "Trucks" ) then + self:SetCarrier( NearUnit ) + break + end + end + end + +end + +--- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Unit#UNIT Airplane +-- @param From +-- @param Event +-- @param To + -- @param Wrapper.Airbase#AIRBASE Airbase +-- @param #number Speed +function AI_CARGO_AIRPLANE:onafterLanded( Airplane, From, Event, To ) + + if Airplane and Airplane:IsAlive() then + + if self.RoutePickup == true then + self:Load( Airplane:GetPointVec2() ) + self.RoutePickup = false + end + + if self.RouteDeploy == true then + self:Unload() + self.RouteDeploy = false + end + + end + +end + + + +--- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Group#GROUP Airplane +-- @param From +-- @param Event +-- @param To +-- @param Wrapper.Airbase#AIRBASE Airbase +-- @param #number Speed +function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Airbase, Speed ) + + if Airplane and Airplane:IsAlive() then + + self.RoutePickup = true + + Airplane:RouteRTB( Airbase, Speed) + end + +end + + +--- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Unit#UNIT Airplane +-- @param From +-- @param Event +-- @param To + -- @param Wrapper.Airbase#AIRBASE Airbase +-- @param #number Speed +function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Coordinate, Speed ) + + if Airplane and Airplane:IsAlive() then + + self.RouteDeploy = true + + Airplane:RouteRTB( Airbase, Speed) + end + +end + + +--- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Unit#UNIT Airplane +function AI_CARGO_AIRPLANE:onafterLoad( Airplane, From, Event, To, Coordinate ) + + if Airplane and Airplane:IsAlive() then + + for _, Cargo in pairs( self.CargoSet:GetSet() ) do + if Cargo:IsInLoadRadius( Coordinate ) then + self:__Board( 5 ) + Cargo:Board( Airplane, 25 ) + self.Cargo = Cargo + break + end + end + end + +end + +--- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Unit#UNIT Airplane +function AI_CARGO_AIRPLANE:onafterBoard( Airplane, From, Event, To ) + + if Airplane and Airplane:IsAlive() then + self:F({ IsLoaded = self.Cargo:IsLoaded() } ) + if not self.Cargo:IsLoaded() then + self:__Board( 10 ) + else + self:__Loaded( 1 ) + end + end + +end + +--- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Unit#UNIT Airplane +function AI_CARGO_AIRPLANE:onafterLoaded( Airplane, From, Event, To ) + + if Airplane and Airplane:IsAlive() then + end + +end + + +--- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Unit#UNIT Airplane +function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To ) + + if Airplane and Airplane:IsAlive() then + self.Cargo:UnBoard() + self:__Unboard( 10 ) + end + +end + +--- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Unit#UNIT Airplane +function AI_CARGO_AIRPLANE:onafterUnboard( Airplane, From, Event, To ) + + if Airplane and Airplane:IsAlive() then + if not self.Cargo:IsUnLoaded() then + self:__Unboard( 10 ) + else + self:__Unloaded( 1 ) + end + end + +end + +--- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Unit#UNIT Airplane +function AI_CARGO_AIRPLANE:onafterUnloaded( Airplane, From, Event, To ) + + if Airplane and Airplane:IsAlive() then + self.Airplane = Airplane + end + +end + + diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index 2f3d08e7c..f909972b7 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -68,6 +68,7 @@ AI/AI_Bai.lua AI/AI_Formation.lua AI/AI_Cargo_APC.lua AI/AI_Cargo_Helicopter.lua +AI/AI_Cargo_Airplane.lua Actions/Act_Assign.lua Actions/Act_Route.lua From b9eab34d6ae25aeef23dcbbbb8eebe24c437d034 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sat, 14 Apr 2018 13:45:00 +0200 Subject: [PATCH 058/420] WIP on AI_Cargo_Airplane --- .../Moose/AI/AI_Cargo_Airplane.lua | 40 +++++--- Moose Development/Moose/Core/Database.lua | 2 +- Moose Development/Moose/Wrapper/Group.lua | 96 ++++++++++++++++++- 3 files changed, 121 insertions(+), 17 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 0d8411544..679900301 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -24,7 +24,7 @@ AI_CARGO_AIRPLANE = { --- Creates a new AI_CARGO_AIRPLANE object. -- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Unit#UNIT Airplane +-- @param Wrapper.Group#GROUP Airplane -- @param Core.Set#SET_CARGO CargoSet -- @param #number CombatRadius -- @return #AI_CARGO_AIRPLANE @@ -115,13 +115,13 @@ end --- Set the Carrier. -- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Unit#UNIT Airplane +-- @param Wrapper.Group#GROUP Airplane -- @return #AI_CARGO_AIRPLANE function AI_CARGO_AIRPLANE:SetCarrier( Airplane ) local AICargo = self - self.Airplane = Airplane -- Wrapper.Unit#UNIT + self.Airplane = Airplane -- Wrapper.Group#GROUP self.Airplane:SetState( self.Airplane, "AI_CARGO_AIRPLANE", self ) self.RoutePickup = false @@ -172,7 +172,7 @@ end -- @param #AI_CARGO_AIRPLANE self -- @param Wrapper.Airbase#AIRBASE Airbase -- @param #number Radius --- @return Wrapper.Unit#UNIT NewCarrier +-- @return Wrapper.Group#GROUP NewCarrier function AI_CARGO_AIRPLANE:FindCarrier( Coordinate, Radius ) local CoordinateZone = ZONE_RADIUS:New( "Zone" , Coordinate:GetVec2(), Radius ) @@ -193,7 +193,7 @@ function AI_CARGO_AIRPLANE:FindCarrier( Coordinate, Radius ) end --- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Unit#UNIT Airplane +-- @param Wrapper.Group#GROUP Airplane -- @param From -- @param Event -- @param To @@ -228,9 +228,14 @@ end -- @param #number Speed function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Airbase, Speed ) + if self.Airbase then + Airplane:RespawnAtAirbase( self.Airbase ) + end + if Airplane and Airplane:IsAlive() then self.RoutePickup = true + self.Airbase = Airbase Airplane:RouteRTB( Airbase, Speed) end @@ -239,26 +244,31 @@ end --- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Unit#UNIT Airplane +-- @param Wrapper.Group#GROUP Airplane -- @param From -- @param Event -- @param To - -- @param Wrapper.Airbase#AIRBASE Airbase +-- @param Wrapper.Airbase#AIRBASE Airbase -- @param #number Speed -function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Coordinate, Speed ) +function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Airbase, Speed ) + if self.Airbase then + Airplane:RespawnAtAirbase( self.Airbase ) + end + if Airplane and Airplane:IsAlive() then self.RouteDeploy = true + self.Airbase = Airbase - Airplane:RouteRTB( Airbase, Speed) + Airplane:RouteRTB( Airbase, Speed ) end end --- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Unit#UNIT Airplane +-- @param Wrapper.Group#GROUP Airplane function AI_CARGO_AIRPLANE:onafterLoad( Airplane, From, Event, To, Coordinate ) if Airplane and Airplane:IsAlive() then @@ -276,7 +286,7 @@ function AI_CARGO_AIRPLANE:onafterLoad( Airplane, From, Event, To, Coordinate ) end --- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Unit#UNIT Airplane +-- @param Wrapper.Group#GROUP Airplane function AI_CARGO_AIRPLANE:onafterBoard( Airplane, From, Event, To ) if Airplane and Airplane:IsAlive() then @@ -291,7 +301,7 @@ function AI_CARGO_AIRPLANE:onafterBoard( Airplane, From, Event, To ) end --- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Unit#UNIT Airplane +-- @param Wrapper.Group#GROUP Airplane function AI_CARGO_AIRPLANE:onafterLoaded( Airplane, From, Event, To ) if Airplane and Airplane:IsAlive() then @@ -301,7 +311,7 @@ end --- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Unit#UNIT Airplane +-- @param Wrapper.Group#GROUP Airplane function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To ) if Airplane and Airplane:IsAlive() then @@ -312,7 +322,7 @@ function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To ) end --- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Unit#UNIT Airplane +-- @param Wrapper.Group#GROUP Airplane function AI_CARGO_AIRPLANE:onafterUnboard( Airplane, From, Event, To ) if Airplane and Airplane:IsAlive() then @@ -326,7 +336,7 @@ function AI_CARGO_AIRPLANE:onafterUnboard( Airplane, From, Event, To ) end --- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Unit#UNIT Airplane +-- @param Wrapper.Group#GROUP Airplane function AI_CARGO_AIRPLANE:onafterUnloaded( Airplane, From, Event, To ) if Airplane and Airplane:IsAlive() then diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index b160fdfda..6eb78eccc 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -688,7 +688,7 @@ function DATABASE:_RegisterAirbases() local DCSAirbaseName = DCSAirbase:getName() - self:E( { "Register Airbase:", DCSAirbaseName } ) + self:E( { "Register Airbase:", DCSAirbaseName, DCSAirbase:getID() } ) self:AddAirbase( DCSAirbaseName ) end end diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index be59a7723..af83d6553 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1141,7 +1141,7 @@ function GROUP:Respawn( Template, Reset ) else for UnitID, TemplateUnitData in pairs( Template.units ) do self:F( "Reset" ) - local GroupUnitVec3 = { x = TemplateUnitData.x, y = TemplateUnitData.alt, z = TemplateUnitData.z } + local GroupUnitVec3 = { x = TemplateUnitData.x, y = TemplateUnitData.alt, z = TemplateUnitData.y } if Zone then if self.InitRespawnRandomizePositionZone then GroupUnitVec3 = Zone:GetRandomVec3() @@ -1174,7 +1174,101 @@ function GROUP:Respawn( Template, Reset ) end +--- @param Wrapper.Group#GROUP self +function GROUP:RespawnAtAirbase( AirbaseRespawn, Takeoff, TakeoffAltitude ) -- R2.4 + self:F( { AirbaseRespawn, Takeoff, TakeoffAltitude } ) + local PointVec3 = AirbaseRespawn:GetPointVec3() + + Takeoff = Takeoff or SPAWN.Takeoff.Hot + + local SpawnTemplate = self:GetTemplate() + + if SpawnTemplate then + + local SpawnPoint = SpawnTemplate.route.points[1] + + -- These are only for ships. + SpawnPoint.linkUnit = nil + SpawnPoint.helipadId = nil + SpawnPoint.airdromeId = nil + + local AirbaseID = AirbaseRespawn:GetID() + local AirbaseCategory = AirbaseRespawn:GetDesc().category + self:F( { AirbaseCategory = AirbaseCategory, Ship = Airbase.Category.SHIP, Helipad = Airbase.Category.HELIPAD, Airdrome = Airbase.Category.AIRDROME } ) + + if AirbaseCategory == Airbase.Category.SHIP then + SpawnPoint.linkUnit = AirbaseID + SpawnPoint.helipadId = AirbaseID + elseif AirbaseCategory == Airbase.Category.HELIPAD then + SpawnPoint.linkUnit = AirbaseID + SpawnPoint.helipadId = AirbaseID + elseif AirbaseCategory == Airbase.Category.AIRDROME then + SpawnPoint.airdromeId = AirbaseID + end + + SpawnPoint.alt = 0 + + SpawnPoint.type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type + SpawnPoint.action = GROUPTEMPLATE.Takeoff[Takeoff][2] -- action + + + -- Translate the position of the Group Template to the Vec3. + for UnitID = 1, #SpawnTemplate.units do + self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y ) + + -- These cause a lot of confusion. + local UnitTemplate = SpawnTemplate.units[UnitID] + + UnitTemplate.parking = 15 + UnitTemplate.parking_id = "30" + UnitTemplate.alt = 0 + + local SX = UnitTemplate.x + local SY = UnitTemplate.y + local BX = SpawnPoint.x + local BY = SpawnPoint.y + local TX = PointVec3.x + ( SX - BX ) + local TY = PointVec3.z + ( SY - BY ) + + UnitTemplate.x = TX + UnitTemplate.y = TY + + if Takeoff == GROUP.Takeoff.Air then + UnitTemplate.alt = PointVec3.y + ( TakeoffAltitude or 200 ) + --else + -- UnitTemplate.alt = PointVec3.y + 10 + end + self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. UnitTemplate.x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. UnitTemplate.y ) + end + + SpawnPoint.x = PointVec3.x + SpawnPoint.y = PointVec3.z + + if Takeoff == GROUP.Takeoff.Air then + SpawnPoint.alt = PointVec3.y + ( TakeoffAltitude or 200 ) + --else + -- SpawnPoint.alt = PointVec3.y + 10 + end + + SpawnTemplate.x = PointVec3.x + SpawnTemplate.y = PointVec3.z + + local GroupSpawned = self:Respawn( SpawnTemplate ) + + -- When spawned in the air, we need to generate a Takeoff Event + + if Takeoff == GROUP.Takeoff.Air then + for UnitID, UnitSpawned in pairs( GroupSpawned:GetUnits() ) do + SCHEDULER:New( nil, BASE.CreateEventTakeoff, { GroupSpawned, timer.getTime(), UnitSpawned:GetDCSObject() } , 1 ) + end + end + + return GroupSpawned + end + + return nil +end --- Return the mission template of the group. From 3757eb06d9411267330ac24cec6ab8904b4dea9e Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sun, 15 Apr 2018 04:41:52 +0200 Subject: [PATCH 059/420] Fixes for AI_CARGO_AIRPLANE --- .../Moose/AI/AI_Cargo_Airplane.lua | 128 ++++++++++++++++-- Moose Development/Moose/Core/Base.lua | 16 +++ Moose Development/Moose/Wrapper/Group.lua | 33 +++-- 3 files changed, 154 insertions(+), 23 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 679900301..a55bf0d10 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -228,16 +228,10 @@ end -- @param #number Speed function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Airbase, Speed ) - if self.Airbase then - Airplane:RespawnAtAirbase( self.Airbase ) - end - if Airplane and Airplane:IsAlive() then - + self:Route( Airplane, Airbase, Speed ) self.RoutePickup = true self.Airbase = Airbase - - Airplane:RouteRTB( Airbase, Speed) end end @@ -252,16 +246,10 @@ end -- @param #number Speed function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Airbase, Speed ) - if self.Airbase then - Airplane:RespawnAtAirbase( self.Airbase ) - end - if Airplane and Airplane:IsAlive() then - + self:Route( Airplane, Airbase, Speed ) self.RouteDeploy = true self.Airbase = Airbase - - Airplane:RouteRTB( Airbase, Speed ) end end @@ -346,3 +334,115 @@ function AI_CARGO_AIRPLANE:onafterUnloaded( Airplane, From, Event, To ) end +--- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Group#GROUP Airplane +-- @param Wrapper.Airbase#AIRBASE Airbase +-- @param #number Speed +function AI_CARGO_AIRPLANE:Route( Airplane, Airbase, Speed ) + + if Airplane and Airplane:IsAlive() then + + local PointVec3 = Airplane:GetPointVec3() + + local Takeoff = SPAWN.Takeoff.Hot + + local Template = Airplane:GetTemplate() + + if Template then + + local Points = {} + + if self.Airbase then + + local FromWaypoint = Template.route.points[1] + + -- These are only for ships. + FromWaypoint.linkUnit = nil + FromWaypoint.helipadId = nil + FromWaypoint.airdromeId = nil + + local AirbaseID = self.Airbase:GetID() + local AirbaseCategory = self.Airbase:GetDesc().category + + FromWaypoint.airdromeId = AirbaseID + + FromWaypoint.alt = 0 + + FromWaypoint.type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type + FromWaypoint.action = GROUPTEMPLATE.Takeoff[Takeoff][2] -- action + + + -- Translate the position of the Group Template to the Vec3. + for UnitID = 1, #Template.units do + self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. Template.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. Template.units[UnitID].y ) + + -- These cause a lot of confusion. + local UnitTemplate = Template.units[UnitID] + + UnitTemplate.parking = 15 + UnitTemplate.parking_id = "1" + UnitTemplate.alt = 0 + + local SX = UnitTemplate.x + local SY = UnitTemplate.y + local BX = FromWaypoint.x + local BY = FromWaypoint.y + local TX = PointVec3.x + ( SX - BX ) + local TY = PointVec3.z + ( SY - BY ) + + UnitTemplate.x = TX + UnitTemplate.y = TY + + self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. UnitTemplate.x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. UnitTemplate.y ) + end + + FromWaypoint.x = PointVec3.x + FromWaypoint.y = PointVec3.z + + Points[#Points+1] = FromWaypoint + else + + local GroupPoint = Airplane:GetVec2() + local GroupVelocity = Airplane:GetUnit(1):GetDesc().speedMax + + local FromWaypoint = {} + FromWaypoint.x = GroupPoint.x + FromWaypoint.y = GroupPoint.y + FromWaypoint.type = "Turning Point" + FromWaypoint.action = "Turning Point" + FromWaypoint.speed = GroupVelocity + + Points[#Points+1] = FromWaypoint + end + + local AirbasePointVec2 = Airbase:GetPointVec2() + local ToWaypoint = AirbasePointVec2:WaypointAir( + POINT_VEC3.RoutePointAltType.BARO, + "Land", + "Landing", + Speed or Airplane:GetUnit(1):GetDesc().speedMax + ) + + ToWaypoint["airdromeId"] = Airbase:GetID() + ToWaypoint["speed_locked"] = true, + + self:F( ToWaypoint ) + + Points[#Points+1] = ToWaypoint + + Template.x = PointVec3.x + Template.y = PointVec3.z + + self:T3( Points ) + Template.route.points = Points + + --self:Respawn( Template ) + + local GroupSpawned = Airplane:Respawn( Template ) + + return GroupSpawned + end + + end + +end diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index f642d79b1..555786ab4 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -645,6 +645,22 @@ function BASE:CreateEventCrash( EventTime, Initiator ) world.onEvent( Event ) end +--- Creation of a Dead Event. +-- @param #BASE self +-- @param Dcs.DCSTypes#Time EventTime The time stamp of the event. +-- @param Dcs.DCSWrapper.Object#Object Initiator The initiating object of the event. +function BASE:CreateEventDead( EventTime, Initiator ) + self:F( { EventTime, Initiator } ) + + local Event = { + id = world.event.S_EVENT_DEAD, + time = EventTime, + initiator = Initiator, + } + + world.onEvent( Event ) +end + --- Creation of a Takeoff Event. -- @param #BASE self -- @param Dcs.DCSTypes#Time EventTime The time stamp of the event. diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index af83d6553..c595a5dbb 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -236,19 +236,36 @@ function GROUP:IsAlive() end --- Destroys the DCS Group and all of its DCS Units. --- Note that this destroy method also raises a destroy event at run-time. --- So all event listeners will catch the destroy event of this DCS Group. +-- Note that this destroy method also can raise a destroy event at run-time. +-- So all event listeners will catch the destroy event of this group for each unit in the group. +-- To raise these events, provide the `GenerateEvent` parameter. -- @param #GROUP self --- @param #boolean GenerateEvent +-- @param #boolean GenerateEvent true if you want to generate a crash or dead event for each unit. +-- @usage +-- -- Air unit example: destroy the Helicopter and generate a S_EVENT_CRASH for each unit in the Helicopter group. +-- Helicopter = GROUP:FindByName( "Helicopter" ) +-- Helicopter:Destroy( true ) +-- @usage +-- -- Ground unit example: destroy the Tanks and generate a S_EVENT_DEAD for each unit in the Tanks group. +-- Tanks = GROUP:FindByName( "Tanks" ) +-- Tanks:Destroy( true ) +-- @usage +-- -- Ship unit example: destroy the Ship silently. +-- Ship = GROUP:FindByName( "Ship" ) +-- Ship:Destroy( true ) function GROUP:Destroy( GenerateEvent ) self:F2( self.GroupName ) local DCSGroup = self:GetDCSObject() if DCSGroup then - if not GenerateEvent then + if GenerateEvent and GenerateEvent == true then for Index, UnitData in pairs( DCSGroup:getUnits() ) do - self:CreateEventCrash( timer.getTime(), UnitData ) + if self:IsAir() then + self:CreateEventCrash( timer.getTime(), UnitData ) + else + self:CreateEventDead( timer.getTime(), UnitData ) + end end end USERFLAG:New( self:GetName() ):Set( 100 ) @@ -1394,7 +1411,7 @@ do -- Route methods -- @param #number Speed (optional) The Speed, if no Speed is given, the maximum Speed of the first unit is selected. -- @return #GROUP function GROUP:RouteRTB( RTBAirbase, Speed ) - self:F2( { RTBAirbase, Speed } ) + self:F( { RTBAirbase:GetName(), Speed } ) local DCSGroup = self:GetDCSObject() @@ -1435,9 +1452,7 @@ do -- Route methods Template.route.points = Points self:Respawn( Template ) - self:Route( Points ) - - self:Respawn(Template) + --self:Route( Points ) else self:ClearTasks() end From af050629aab4f401c186c4038fa429c33b84624a Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Tue, 17 Apr 2018 06:25:40 +0200 Subject: [PATCH 060/420] Made it. Now cargo can be defined in the Mission Editor as #CARGO --- Moose Development/Moose/Cargo/Cargo.lua | 2 +- Moose Development/Moose/Core/Database.lua | 107 +++++++++++++++++----- Moose Development/Moose/Moose.lua | 2 + 3 files changed, 87 insertions(+), 24 deletions(-) diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 2cf5be78e..446d58da4 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -610,7 +610,7 @@ do -- CARGO -- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). -- @return #boolean function CARGO:IsNear( PointVec2, NearRadius ) - --self:F2( { PointVec2 = PointVec2, NearRadius = NearRadius } ) + --self:F( { PointVec2 = PointVec2, NearRadius = NearRadius } ) if self.CargoObject:IsAlive() then --local Distance = PointVec2:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 6eb78eccc..262175514 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -242,35 +242,96 @@ function DATABASE:FindAirbase( AirbaseName ) return AirbaseFound end ---- Adds a Cargo based on the Cargo Name in the DATABASE. --- @param #DATABASE self --- @param #string CargoName The name of the airbase -function DATABASE:AddCargo( Cargo ) - if not self.CARGOS[Cargo.Name] then - self.CARGOS[Cargo.Name] = Cargo + +do -- cargo + + --- Adds a Cargo based on the Cargo Name in the DATABASE. + -- @param #DATABASE self + -- @param #string CargoName The name of the airbase + function DATABASE:AddCargo( Cargo ) + + if not self.CARGOS[Cargo.Name] then + self.CARGOS[Cargo.Name] = Cargo + end end -end + + + --- Deletes a Cargo from the DATABASE based on the Cargo Name. + -- @param #DATABASE self + -- @param #string CargoName The name of the airbase + function DATABASE:DeleteCargo( CargoName ) + + self.CARGOS[CargoName] = nil + end + + --- Finds an CARGO based on the CargoName. + -- @param #DATABASE self + -- @param #string CargoName + -- @return Wrapper.Cargo#CARGO The found CARGO. + function DATABASE:FindCargo( CargoName ) + + local CargoFound = self.CARGOS[CargoName] + return CargoFound + end + + --- Checks if the Template name has a ~CARGO tag. + -- If yes, the group is a cargo. + -- @param #DATABASE self + -- @param #string TemplateName + -- @return #boolean + function DATABASE:IsCargo( TemplateName ) + TemplateName = env.getValueDictByKey( TemplateName ) + + local Cargo = TemplateName:match( "#(CARGO)" ) ---- Deletes a Cargo from the DATABASE based on the Cargo Name. --- @param #DATABASE self --- @param #string CargoName The name of the airbase -function DATABASE:DeleteCargo( CargoName ) + return Cargo and Cargo == "CARGO" + end - self.CARGOS[CargoName] = nil -end - ---- Finds an CARGO based on the CargoName. --- @param #DATABASE self --- @param #string CargoName --- @return Wrapper.Cargo#CARGO The found CARGO. -function DATABASE:FindCargo( CargoName ) - - local CargoFound = self.CARGOS[CargoName] - return CargoFound -end + --- Private method that registers new Static Templates within the DATABASE Object. + -- @param #DATABASE self + -- @return #DATABASE self + function DATABASE:RegisterCargos() + + for CargoGroupName, CargoGroup in pairs( self.GROUPS ) do + if self:IsCargo( CargoGroupName ) then + local CargoInfo = CargoGroupName:match("~CARGO(.*)") + local CargoParam = CargoInfo and CargoInfo:match( "%((.*)%)") + local CargoName = CargoGroupName:match("(.*)~CARGO") + local Type = CargoParam and CargoParam:match( "T=([%a%d ]+),?") + local Name = CargoParam and CargoParam:match( "N=([%a%d]+),?") + local LoadRadius = CargoParam and CargoParam:match( "RR=([%a%d]+),?") + local NearRadius = CargoParam and CargoParam:match( "NR=([%a%d]+),?") + + CARGO_GROUP:New( CargoGroup, Type, Name or CargoName, LoadRadius, NearRadius ) + end + end + + for CargoStaticName, CargoStatic in pairs( self.STATICS ) do + if self:IsCargo( CargoStaticName ) then + local CargoInfo = CargoStaticName:match("~CARGO(.*)") + local CargoParam = CargoInfo and CargoInfo:match( "%((.*)%)") + local CargoName = CargoStaticName:match("(.*)~CARGO") + local Type = CargoParam and CargoParam:match( "T=([%a%d ]+),?") + local Category = CargoParam and CargoParam:match( "C=([%a%d ]+),?") + local Name = CargoParam and CargoParam:match( "N=([%a%d]+),?") + local LoadRadius = CargoParam and tonumber( CargoParam:match( "RR=([%a%d]+),?") ) + local NearRadius = CargoParam and tonumber( CargoParam:match( "NR=([%a%d]+),?") ) + + if Category == "SLING" then + CARGO_SLINGLOAD:New( CargoStatic, Type, Name or CargoName, LoadRadius, NearRadius ) + else + if Category == "CRATE" then + CARGO_CRATE:New( CargoStatic, Type, Name or CargoName, LoadRadius, NearRadius ) + end + end + end + end + + end +end -- cargo --- Finds a CLIENT based on the ClientName. -- @param #DATABASE self diff --git a/Moose Development/Moose/Moose.lua b/Moose Development/Moose/Moose.lua index 5e1e394cc..6a748922a 100644 --- a/Moose Development/Moose/Moose.lua +++ b/Moose Development/Moose/Moose.lua @@ -13,3 +13,5 @@ _DATABASE = DATABASE:New() -- Core.Database#DATABASE _SETTINGS = SETTINGS:Set() _SETTINGS:SetPlayerMenuOn() +_DATABASE:RegisterCargos() + From 22976468736a8a102b454ae38825a363c84cf74e Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Fri, 20 Apr 2018 00:13:30 +0200 Subject: [PATCH 061/420] Range v1.1.1 Added strafe pit/bombing targets info --- Moose Development/Moose/Functional/Range.lua | 79 ++++++++++++++++++-- 1 file changed, 74 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 9748e89d6..937dc9002 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -279,7 +279,7 @@ RANGE.id="RANGE | " --- Range script version. -- @field #number version -RANGE.version="1.1.0" +RANGE.version="1.1.1" --TODO list: --TODO: Add custom weapons, which can be specified by the user. @@ -1537,6 +1537,73 @@ function RANGE:_DisplayRangeInfo(_unitname) end end +--- Display bombing target locations to player. +-- @param #RANGE self +-- @param #string _unitname Name of the player unit. +function RANGE:_DisplayBombTargets(_unitname) + self:F(_unitname) + + -- Get player unit and player name. + local _unit, _playername = self:_GetPlayerUnitAndName(_unitname) + + -- Check if we have a player. + if _unit and _playername then + + -- Player settings. + local _settings=_DATABASE:GetPlayerSettings(_playername) or _SETTINGS --Core.Settings#SETTINGS + + -- Message text. + local _text="Bomb Target Locations:" + + for _,_bombtarget in pairs(self.bombingTargets) do + local _target=_bombtarget.target --Wrapper.Positionable#POSITIONABLE + if _target and _target:IsAlive() then + + -- Core.Point#COORDINATE + local coord=_target:GetCoordinate() --Core.Point#COORDINATE + local mycoord=coord:ToStringA2G(_unit, _settings) + _text=_text..string.format("\n- %s: %s",_bombtarget.name, mycoord) + end + end + + self:_DisplayMessageToGroup(_unit,_text, nil, true) + end +end + +--- Display pit location and heading to player. +-- @param #RANGE self +-- @param #string _unitname Name of the player unit. +function RANGE:_DisplayStrafePits(_unitname) + self:F(_unitname) + + -- Get player unit and player name. + local _unit, _playername = self:_GetPlayerUnitAndName(_unitname) + + -- Check if we have a player. + if _unit and _playername then + + -- Player settings. + local _settings=_DATABASE:GetPlayerSettings(_playername) or _SETTINGS --Core.Settings#SETTINGS + + -- Message text. + local _text="Strafe Target Locations:" + + for _,_strafepit in pairs(self.strafeTargets) do + local _target=_strafepit --Wrapper.Positionable#POSITIONABLE + + -- Pit parameters. + local coord=_strafepit.coordinate --Core.Point#COORDINATE + local heading=_strafepit.heading + + local mycoord=coord:ToStringA2G(_unit, _settings) + _text=_text..string.format("\n- %s: %s - heading %03d",_strafepit.name, mycoord, heading) + end + + self:_DisplayMessageToGroup(_unit,_text, nil, true) + end +end + + --- Report weather conditions at range. Temperature, QFE pressure and wind data. -- @param #RANGE self -- @param #string _unitname Name of the player unit. @@ -1792,11 +1859,11 @@ function RANGE:_AddF10Commands(_unitName) local _statsPath = missionCommands.addSubMenuForGroup(_gid, "Statistics", _rangePath) local _markPath = missionCommands.addSubMenuForGroup(_gid, "Mark Targets", _rangePath) local _settingsPath = missionCommands.addSubMenuForGroup(_gid, "My Settings", _rangePath) + local _infoPath = missionCommands.addSubMenuForGroup(_gid, "Range Info", _rangePath) -- F10/On the Range//My Settings/ local _mysmokePath = missionCommands.addSubMenuForGroup(_gid, "Smoke Color", _settingsPath) local _myflarePath = missionCommands.addSubMenuForGroup(_gid, "Flare Color", _settingsPath) - --TODO: Convert to MOOSE menu. -- F10/On the Range//Mark Targets/ missionCommands.addCommandForGroup(_gid, "Mark On Map", _markPath, self._MarkTargetsOnMap, self, _unitName) missionCommands.addCommandForGroup(_gid, "Illuminate Range", _markPath, self._IlluminateBombTargets, self, _unitName) @@ -1824,9 +1891,11 @@ function RANGE:_AddF10Commands(_unitName) missionCommands.addCommandForGroup(_gid, "Smoke Delay On/Off", _settingsPath, self._SmokeBombDelayOnOff, self, _unitName) missionCommands.addCommandForGroup(_gid, "Smoke Impact On/Off", _settingsPath, self._SmokeBombImpactOnOff, self, _unitName) missionCommands.addCommandForGroup(_gid, "Flare Hits On/Off", _settingsPath, self._FlareDirectHitsOnOff, self, _unitName) - -- F10/On the Range// - missionCommands.addCommandForGroup(_gid, "Range Information", _rangePath, self._DisplayRangeInfo, self, _unitName) - missionCommands.addCommandForGroup(_gid, "Weather Report", _rangePath, self._DisplayRangeWeather, self, _unitName) + -- F10/On the Range//Range Information + missionCommands.addCommandForGroup(_gid, "General Info", _infoPath, self._DisplayRangeInfo, self, _unitName) + missionCommands.addCommandForGroup(_gid, "Weather Report", _infoPath, self._DisplayRangeWeather, self, _unitName) + missionCommands.addCommandForGroup(_gid, "Bombing Targets", _infoPath, self._DisplayBombTargets, self, _unitName) + missionCommands.addCommandForGroup(_gid, "Strafe Pits", _infoPath, self._DisplayStrafePits, self, _unitName) end else self:T(RANGE.id.."Could not find group or group ID in AddF10Menu() function. Unit name: ".._unitName) From 462564cd01448282d8fc594fa31c4c5a0623840a Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sun, 22 Apr 2018 23:55:21 +0200 Subject: [PATCH 062/420] PseudoATC and RANGE Range: corrected heading PseudoATC: lots of changes --- .../Moose/Functional/PseudoATC.lua | 279 +++++++++++++----- Moose Development/Moose/Functional/Range.lua | 7 + 2 files changed, 212 insertions(+), 74 deletions(-) diff --git a/Moose Development/Moose/Functional/PseudoATC.lua b/Moose Development/Moose/Functional/PseudoATC.lua index 9e93073d6..6295fc646 100644 --- a/Moose Development/Moose/Functional/PseudoATC.lua +++ b/Moose Development/Moose/Functional/PseudoATC.lua @@ -13,7 +13,7 @@ -- -- * Report QFE or QNH pressures at nearby airbases. -- * Report wind direction and strength at airbases. --- * Report temperature at airbases +-- * Report temperature at airbases. -- * Report absolute bearing and range to nearest airports. -- * Report current altitude AGL of own aircraft. -- * Upon request, ATC reports altitude until touchdown. @@ -43,7 +43,7 @@ -- ### Contributions: **Sven van de Velde ([FlightControl](https://forums.eagle.ru/member.php?u=89536))** -- -- ==== --- @module PeusoATC +-- @module PseudoATC ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- PSEUDOATC class @@ -68,11 +68,12 @@ -- @field #PSEUDOATC PSEUDOATC={ ClassName = "PSEUDOATC", - Debug=true, + Debug=false, player={}, maxairport=9, mdur=30, mrefresh=120, + eventsmoose=true, } ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -93,7 +94,7 @@ PSEUDOATC.id="PseudoATC | " --- PSEUDOATC version. -- @field #list PSEUDOATC.version={ - version = "0.5.0", + version = "0.6.0", print = true, } @@ -107,7 +108,7 @@ function PSEUDOATC:Start() local self=BASE:Inherit(self, BASE:New()) -- #PSEUDOATC -- Debug info - env.info(PSEUDOATC.id..string.format("Creating PseudoATC object. PseudoATC version %s", PSEUDOATC.version.version)) + self:E(PSEUDOATC.id..string.format("Creating PseudoATC object. PseudoATC version %s", PSEUDOATC.version.version)) -- Handle events. if self.eventsmoose then @@ -192,11 +193,13 @@ end -- @param #PSEUDOATC self -- @param Core.Event#EVENTDATA EventData function PSEUDOATC:_OnBirth(EventData) - env.info(PSEUDOATC.id.."PlayerEntered event caught my MOOSE.") + self:F({EventData=EventData}) + -- Get unit and player. local _unitName=EventData.IniUnitName local _unit, _playername=self:_GetPlayerUnitAndName(_unitName) + -- Check if a player entered. if _unit and _playername then self:PlayerEntered(_unit) end @@ -207,10 +210,13 @@ end -- @param #PSEUDOATC self -- @param Core.Event#EVENTDATA EventData function PSEUDOATC:_PlayerLeft(EventData) + self:F({EventData=EventData}) + -- Get unit and player. local _unitName=EventData.IniUnitName local _unit, _playername=self:_GetPlayerUnitAndName(_unitName) + -- Check if a player left. if _unit and _playername then self:PlayerLeft(_unit) end @@ -220,19 +226,22 @@ end -- @param #PSEUDOATC self -- @param Core.Event#EVENTDATA EventData function PSEUDOATC:_PlayerLanded(EventData) + self:F({EventData=EventData}) + -- Get unit, player and place. local _unitName=EventData.IniUnitName local _unit, _playername=self:_GetPlayerUnitAndName(_unitName) local _base=EventData.Place local _baseName=EventData.PlaceName - if _unit and _playername and _base then + -- Call landed function. + if _unit and _playername and _base then self:PlayerLanded(_unit, _baseName) end end ----------------------------------------------------------------------------------------------------------------------------------------- --- Menu Functions +-- Event Functions --- Function called when a player enters a unit. -- @param #PSEUDOATC self @@ -240,6 +249,7 @@ end function PSEUDOATC:PlayerEntered(unit) self:F2({unit=unit}) + -- Get player info. local group=unit:GetGroup() --Wrapper.Group#GROUP local GID=group:GetID() local GroupName=group:GetName() @@ -259,10 +269,8 @@ function PSEUDOATC:PlayerEntered(unit) -- Info message. local text=string.format("Player %s entered unit %s of group %s. ID = %d", PlayerName, UnitName, GroupName, GID) - if self.Debug then - MESSAGE:New(text, 30):ToGroup(group) - env.info(PSEUDOATC.id..text) - end + self:T(PSEUDOATC.id..text) + MESSAGE:New(text, 30):ToAllIf(self.Debug) -- Create main F10 menu, i.e. "F10/Pseudo ATC" self.player[GID].menu_main=missionCommands.addSubMenuForGroup(GID, "Pseudo ATC") @@ -279,7 +287,6 @@ function PSEUDOATC:PlayerEntered(unit) -- Start scheduler to refresh the F10 menues. self.player[GID].scheduler, self.player[GID].schedulerid=SCHEDULER:New(nil, self.MenuRefresh, {self, GID}, self.mrefresh, self.mrefresh) - self:T2(self.player[GID]) end --- Function called when a player has landed. @@ -298,11 +305,9 @@ function PSEUDOATC:PlayerLanded(unit, place) local CallSign=self.player[id].callsign -- Debug message. - if self.Debug then - local text=string.format("Player %s (%s) from group %s with ID %d landed at %s", PlayerName, UnitName, GroupName, place) - MESSAGE:New(text,30):ToAll() - env.info(PSEUDOATC.id..text) - end + local text=string.format("Player %s (%s) from group %s with ID %d landed at %s", PlayerName, UnitName, GroupName, place) + self:T(PSEUDOATC.id..text) + MESSAGE:New(text, 30):ToAllIf(self.Debug) -- Stop altitude reporting timer if its activated. self:AltidudeStopTimer(id) @@ -326,25 +331,20 @@ function PSEUDOATC:PlayerLeft(unit) local id=group:GetID() -- Debug message. - if self.Debug then - local text=string.format("Player %s (%s) callsign %s of group %s just left.", self.player[id].playername, self.player[id].unitname, self.player[id].callsign, self.player[id].groupname) - MESSAGE:New(text,30):ToAll() - env.info(PSEUDOATC.id..text) - end + local text=string.format("Player %s (%s) callsign %s of group %s just left.", self.player[id].playername, self.player[id].unitname, self.player[id].callsign, self.player[id].groupname) + self:T(PSEUDOATC.id..text) + MESSAGE:New(text, 30):ToAllIf(self.Debug) -- Stop scheduler for menu updates if self.player[id].schedulerid then self.player[id].scheduler:Stop(self.player[id].schedulerid) - self.player[id].scheduler=nil - self.player[id].schedulerid=nil end - -- Remove main menu + -- Remove main menu. missionCommands.removeItem(self.player[id].menu_main) -- Remove player array. self.player[id]=nil - end ----------------------------------------------------------------------------------------------------------------------------------------- @@ -354,13 +354,12 @@ end -- @param #PSEUDOATC self. -- @param #number id Group id of player unit. function PSEUDOATC:MenuRefresh(id) - self:F(id) + self:F({id=id}) - if self.Debug then - local text=string.format("Refreshing menues for player %s in group %s.", self.player[id].playername, self.player[id].groupname) - env.info(PSEUDOATC.id..text) - MESSAGE:New(text,30):ToAll() - end + -- Debug message. + local text=string.format("Refreshing menues for player %s in group %s.", self.player[id].playername, self.player[id].groupname) + self:T(PSEUDOATC.id..text) + MESSAGE:New(text,30):ToAllIf(self.Debug) -- Clear menu. self:MenuClear(id) @@ -382,33 +381,27 @@ end function PSEUDOATC:MenuClear(id) self:F(id) - if self.Debug then - local text=string.format("Clearing menues for player %s in group %s.", self.player[id].playername, self.player[id].groupname) - env.info(PSEUDOATC.id..text) - MESSAGE:New(text,30):ToAll() - end - - BASE:E(self.player[id].menu_airports) + -- Debug message. + local text=string.format("Clearing menues for player %s in group %s.", self.player[id].playername, self.player[id].groupname) + self:T(PSEUDOATC.id..text) + MESSAGE:New(text,30):ToAllIf(self.Debug) + if self.player[id].menu_airports then for name,item in pairs(self.player[id].menu_airports) do - - if self.Debug then - env.info(PSEUDOATC.id..string.format("Deleting menu item %s for ID %d", name, id)) - BASE:E(item) - end + -- Debug message. + self:E(PSEUDOATC.id..string.format("Deleting menu item %s for ID %d", name, id)) + + -- Remove menu item. missionCommands.removeItemForGroup(id, self.player[id].menu_airports[name]) - --missionCommands.removeItemForGroup(id, item) end else - if self.Debug then - local text=string.format("no airports to clear menues") - env.info(PSEUDOATC.id..text) - end + self:T2(PSEUDOATC.id.."No airports to clear menus.") end + -- Remove if self.player[id].menu_aircraft then missionCommands.removeItemForGroup(id, self.player[id].menu_aircraft.main) end @@ -443,15 +436,15 @@ function PSEUDOATC:MenuAirports(id) self.player[id].menu_airports[name]=submenu -- Create menu reporting commands + missionCommands.addCommandForGroup(id, "Weather Report", submenu, self.ReportWeather, self, id, pos, name) missionCommands.addCommandForGroup(id, "Request QFE", submenu, self.ReportPressure, self, id, "QFE", pos, name) missionCommands.addCommandForGroup(id, "Request QNH", submenu, self.ReportPressure, self, id, "QNH", pos, name) missionCommands.addCommandForGroup(id, "Request Wind", submenu, self.ReportWind, self, id, pos, name) missionCommands.addCommandForGroup(id, "Request Temperature", submenu, self.ReportTemperature, self, id, pos, name) missionCommands.addCommandForGroup(id, "Request BR", submenu, self.ReportBR, self, id, pos, name) - if self.Debug then - env.info(string.format(PSEUDOATC.id.."Creating airport menu item %s for ID %d", name, id)) - end + -- Debug message. + self:T(string.format(PSEUDOATC.id.."Creating airport menu item %s for ID %d", name, id)) end end @@ -469,9 +462,7 @@ function PSEUDOATC:MenuAircraft(id) local name=string.format("My Aircraft (%s)", callsign) -- Debug info. - if self.Debug then - env.info(PSEUDOATC.id..string.format("Creating menu item %s for ID %d", name,id)) - end + self:T(PSEUDOATC.id..string.format("Creating menu item %s for ID %d", name,id)) -- F10/PseudoATC/My Aircraft (callsign) self.player[id].menu_aircraft.main = missionCommands.addSubMenuForGroup(id, name, self.player[id].menu_main) @@ -502,6 +493,7 @@ function PSEUDOATC:MenuAircraft(id) self.player[id].menu_aircraft_waypoints.pname=submenu -- Menu commands for each waypoint "F10/PseudoATC/My Aircraft (callsign)/Waypoints/Waypoint X/" + missionCommands.addCommandForGroup(id, "Weather Report", submenu, self.ReportWeather, self, id, pos, pname) missionCommands.addCommandForGroup(id, "Request QFE", submenu, self.ReportPressure, self, id, "QFE", pos, pname) missionCommands.addCommandForGroup(id, "Request QNH", submenu, self.ReportPressure, self, id, "QNH", pos, pname) missionCommands.addCommandForGroup(id, "Request Wind", submenu, self.ReportWind, self, id, pos, pname) @@ -517,6 +509,75 @@ end ----------------------------------------------------------------------------------------------------------------------------------------- -- Reporting Functions +--- Weather Report. Report pressure QFE/QNH, temperature, wind at certain location +-- @param #PSEUDOATC self +-- @param #number id Group id to which the report is delivered. +-- @param Core.Point#COORDINATE position Coordinates at which the pressure is measured. +-- @param #string location Name of the location at which the pressure is measured. +function PSEUDOATC:ReportWeather(id, position, location) + self:F({id=id, position=position, location=location}) + + -- Player unit system settings. + local settings=_DATABASE:GetPlayerSettings(self.player[id].playername) or _SETTINGS --Core.Settings#SETTINGS + + local text=string.format("Local weather at %s:\n", location) + + -- Get pressure in hPa. + local Pqnh=position:GetPressure(0) -- Get pressure at sea level. + local Pqfe=position:GetPressure() -- Get pressure at (land) height of position. + + -- Unit conversion. + local _Pqnh=string.format("%.2f inHg", Pqnh * PSEUDOATC.unit.hPa2inHg) + local _Pqfe=string.format("%.2f inHg", Pqfe * PSEUDOATC.unit.hPa2inHg) + if settings:IsMetric() then + _Pqnh=string.format("%.1f mmHg", Pqnh * PSEUDOATC.unit.hPa2mmHg) + _Pqfe=string.format("%.1f mmHg", Pqfe * PSEUDOATC.unit.hPa2mmHg) + end + + -- Message text. + text=text..string.format("QFE %.1f hPa = %s.\n", Pqfe, _Pqfe) + text=text..string.format("QNH %.1f hPa = %s.\n", Pqnh, _Pqnh) + + --- convert celsius to fahrenheit + local function celsius2fahrenheit(degC) + return degC*1.8+32 + end + + -- Get temperature at position in degrees Celsius. + local T=position:GetTemperature() + + -- Correct unit system. + local _T=string.format('%d°F', celsius2fahrenheit(T)) + if settings:IsMetric() then + _T=string.format('%d°C', T) + end + + -- Message text. + local text=text..string.format("Temperature %s\n", _T) + + -- Get wind direction and speed. + local Dir,Vel=position:GetWind() + + -- Get Beaufort wind scale. + local Bn,Bd=UTILS.BeaufortScale(Vel) + + -- Formatted wind direction. + local Ds = string.format('%03d°', Dir) + + -- Velocity in player units. + local Vs=string.format('%.1f m/s', Vel) + if settings:IsImperial() then + Vs=string.format("%.1f knots", Vel*1.94384) + end + + -- Message text. + local text=text..string.format("Wind from %s at %s (%s).", Ds, Vs, Bd) + + -- Send message + self:_DisplayMessageToGroup(self.player[id].unit, text, self.mdur, true) + +end + --- Report pressure. -- @param #PSEUDOATC self -- @param #number id Group id to which the report is delivered. @@ -534,12 +595,20 @@ function PSEUDOATC:ReportPressure(id, Qcode, position, location) P=position:GetPressure() -- Get pressure at (land) height of position. end + -- Settings. + local settings=_DATABASE:GetPlayerSettings(self.player[id].playername) or _SETTINGS --Core.Settings#SETTINGS + -- Unit conversion. local P_inHg=P * PSEUDOATC.unit.hPa2inHg local P_mmHg=P * PSEUDOATC.unit.hPa2mmHg + + local P_set=string.format("%.2f inHg", P_inHg) + if settings:IsMetric() then + P_set=string.format("%.1f mmHg", P_mmHg) + end -- Message text. - local text=string.format("%s at %s: P = %.1f hPa = %.2f inHg = %.1f mmHg.", Qcode, location, P, P_inHg, P_mmHg) + local text=string.format("%s at %s: P = %.1f hPa = %s.", Qcode, location, P, P_set) -- Send message. MESSAGE:New(text, self.mdur):ToGroup(self.player[id].group) @@ -565,8 +634,17 @@ function PSEUDOATC:ReportTemperature(id, position, location) local Tc=string.format('%d°C', T) local Tf=string.format('%d°F', celsius2fahrenheit(T)) + -- Settings. + local settings=_DATABASE:GetPlayerSettings(self.player[id].playername) or _SETTINGS --Core.Settings#SETTINGS + + -- Correct unit system. + local _T=string.format('%d°F', celsius2fahrenheit(T)) + if settings:IsMetric() then + _T=string.format('%d°C', T) + end + -- Message text. - local text=string.format("Temperature at %s is %s = %s", location, Tc, Tf) + local text=string.format("Temperature at %s is %s", location, _T) -- Send message to player group. MESSAGE:New(text, self.mdur):ToGroup(self.player[id].group) @@ -589,8 +667,17 @@ function PSEUDOATC:ReportWind(id, position, location) -- Formatted wind direction. local Ds = string.format('%03d°', Dir) + -- Settings. + local settings=_DATABASE:GetPlayerSettings(self.player[id].playername) or _SETTINGS --Core.Settings#SETTINGS + + -- Velocity in player units. + local Vs=string.format('%.1f m/s', Vel) + if settings:IsImperial() then + Vs=string.format("%.1f knots", Vel*1.94384) + end + -- Message text. - local text=string.format("%s: Wind from %s at %.1f m/s (%s).", location, Ds, Vel, Bd) + local text=string.format("%s: Wind from %s at %s (%s).", location, Ds, Vs, Bd) -- Send message to player group. MESSAGE:New(text, self.mdur):ToGroup(self.player[id].group) @@ -614,10 +701,18 @@ function PSEUDOATC:ReportBR(id, position, location) local range=coord:Get2DDistance(position) -- Bearing string. - local Bs=string.format('%03d°', angle) + local Bs=string.format('%03d°', angle) + + -- Settings. + local settings=_DATABASE:GetPlayerSettings(self.player[id].playername) or _SETTINGS --Core.Settings#SETTINGS + + local Rs=string.format("%.1f km", range/1000) + if settings:IsImperial() then + Rs=string.format("%.1f NM", range/1000 * PSEUDOATC.unit.km2nm) + end -- Message text. - local text=string.format("%s: Bearing %s, Range %.1f km = %.1f NM.", location, Bs, range/1000, range/1000 * PSEUDOATC.unit.km2nm) + local text=string.format("%s: Bearing %s, Range %s.", location, Bs, Rs) -- Send message to player group. MESSAGE:New(text, self.mdur):ToGroup(self.player[id].group) @@ -627,11 +722,15 @@ end -- @param #PSEUDOATC self -- @param #number id Group id to the report is delivered. -- @param #number dt (Optional) Duration the message is displayed. +-- @param #boolean _clear (Optional) Clear previouse messages. -- @return #number Altuitude above ground. -function PSEUDOATC:ReportHeight(id, dt) +function PSEUDOATC:ReportHeight(id, dt, _clear) self:F({id=id, dt=dt}) local dt = dt or self.mdur + if _clear==nil then + _clear=false + end -- Return height [m] above ground level. local function get_AGL(p) @@ -647,11 +746,20 @@ function PSEUDOATC:ReportHeight(id, dt) local height=get_AGL(position) local callsign=unit:GetCallsign() + -- Settings. + local settings=_DATABASE:GetPlayerSettings(self.player[id].playername) or _SETTINGS --Core.Settings#SETTINGS + + local Hs=string.format("%d m", height) + if settings:IsMetric() then + Hs=string.format("%d ft", height*PSEUDOATC.unit.meter2feet) + end + -- Message text. - local text=string.format("%s: Your altitude is %d m = %d ft AGL.", callsign, height, height*PSEUDOATC.unit.meter2feet) + local _text=string.format("%s: Your altitude is %s AGL.", callsign, Hs) -- Send message to player group. - MESSAGE:New(text, dt):ToGroup(self.player[id].group) + --MESSAGE:New(text, dt):ToGroup(self.player[id].group) + self:_DisplayMessageToGroup(self.player[id].unit,_text, dt,_clear) -- Return height return height @@ -659,20 +767,18 @@ end ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Start DCS scheduler function. +--- Start altitude reporting scheduler. -- @param #PSEUDOATC self. -- @param #number id Group id of player unit. function PSEUDOATC:AltidudeStartTimer(id) self:F(id) -- Debug info. - if self.Debug then - env.info(PSEUDOATC.id..string.format("Starting altitude report timer for player ID %d.", id)) - end + self:T(PSEUDOATC.id..string.format("Starting altitude report timer for player ID %d.", id)) -- Start timer. --self.player[id].altimer=timer.scheduleFunction(self.ReportAltTouchdown, self, id, Tnow+2) - self.player[id].altimer, self.player[id].altimerid=SCHEDULER:New(nil, self.ReportHeight, {self, id, 0.1}, 1, 5) + self.player[id].altimer, self.player[id].altimerid=SCHEDULER:New(nil, self.ReportHeight, {self, id, 0.1, true}, 1, 5) end --- Stop/destroy DCS scheduler function for reporting altitude. @@ -681,9 +787,7 @@ end function PSEUDOATC:AltidudeStopTimer(id) -- Debug info. - if self.Debug then - env.info(PSEUDOATC.id..string.format("Stopping altitude report timer for player ID %d.", id)) - end + self:T(PSEUDOATC.id..string.format("Stopping altitude report timer for player ID %d.", id)) -- Stop timer. --timer.removeFunction(self.player[id].alttimer) @@ -752,9 +856,10 @@ function PSEUDOATC:_GetPlayerUnitAndName(_unitName) if _unitName ~= nil then local DCSunit=Unit.getByName(_unitName) local playername=DCSunit:getPlayerName() - local unit=UNIT:Find(DCSunit) - if DCSunit and unit and playername then + + if DCSunit and playername then + local unit=UNIT:Find(DCSunit) return unit, playername end end @@ -763,4 +868,30 @@ function PSEUDOATC:_GetPlayerUnitAndName(_unitName) end +--- Display message to group. +-- @param #PSEUDOATC self +-- @param Wrapper.Unit#UNIT _unit Player unit. +-- @param #string _text Message text. +-- @param #number _time Duration how long the message is displayed. +-- @param #boolean _clear Clear up old messages. +function PSEUDOATC:_DisplayMessageToGroup(_unit, _text, _time, _clear) + self:F({unit=_unit, text=_text, time=_time, clear=_clear}) + + _time=_time or self.Tmsg + if _clear==nil then + _clear=false + end + + -- Group ID. + local _gid=_unit:GetGroup():GetID() + + if _gid then + if _clear == true then + trigger.action.outTextForGroup(_gid, _text, _time, _clear) + else + trigger.action.outTextForGroup(_gid, _text, _time) + end + end + +end diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 937dc9002..a9ba91251 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -1594,6 +1594,13 @@ function RANGE:_DisplayStrafePits(_unitname) -- Pit parameters. local coord=_strafepit.coordinate --Core.Point#COORDINATE local heading=_strafepit.heading + + -- Turn heading around ==> approach heading. + if heading>180 then + heading=heading-180 + else + heading=heading+180 + end local mycoord=coord:ToStringA2G(_unit, _settings) _text=_text..string.format("\n- %s: %s - heading %03d",_strafepit.name, mycoord, heading) From c999389cda86d91ea07a8c39568ce8a62b1b838b Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 23 Apr 2018 06:51:49 +0200 Subject: [PATCH 063/420] Handler for unloading too. --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 268 ++++++-- .../Moose/AI/AI_Cargo_Helicopter.lua | 35 +- Moose Development/Moose/Cargo/Cargo.lua | 20 +- Moose Development/Moose/Cargo/CargoCrate.lua | 6 +- Moose Development/Moose/Cargo/CargoGroup.lua | 20 +- .../Moose/Cargo/CargoSlingload.lua | 8 +- Moose Development/Moose/Cargo/CargoUnit.lua | 2 +- Moose Development/Moose/Core/Database.lua | 20 +- Moose Development/Moose/Core/Point.lua | 14 +- .../Moose/Wrapper/Controllable.lua | 645 +++++++++--------- 10 files changed, 600 insertions(+), 438 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 2c34615cd..68ffc7046 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -24,18 +24,21 @@ AI_CARGO_APC = { --- Creates a new AI_CARGO_APC object. -- @param #AI_CARGO_APC self --- @param Wrapper.Unit#UNIT CargoCarrier --- @param Cargo.CargoGroup#CARGO_GROUP CargoGroup +-- @param Wrapper.Group#GROUP CargoCarrier +-- @param Core.Set#SET_CARGO CargoSet -- @param #number CombatRadius -- @return #AI_CARGO_APC -function AI_CARGO_APC:New( CargoCarrier, CargoGroup, CombatRadius ) +function AI_CARGO_APC:New( CargoCarrier, CargoSet, CombatRadius ) local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_CARGO_APC - self.CargoGroup = CargoGroup -- Cargo.CargoGroup#CARGO_GROUP + self.CargoSet = CargoSet -- Core.Set#SET_CARGO self.CombatRadius = CombatRadius - self:SetStartState( "UnLoaded" ) + self:SetStartState( "Unloaded" ) + + self:AddTransition( "Unloaded", "Pickup", "*" ) + self:AddTransition( "Loaded", "Deploy", "*" ) self:AddTransition( "*", "Load", "Boarding" ) self:AddTransition( "Boarding", "Board", "Boarding" ) @@ -46,10 +49,85 @@ function AI_CARGO_APC:New( CargoCarrier, CargoGroup, CombatRadius ) self:AddTransition( "*", "Monitor", "*" ) self:AddTransition( "*", "Follow", "Following" ) - self:AddTransition( "*", "Guard", "Guarding" ) + self:AddTransition( "*", "Guard", "Unloaded" ) self:AddTransition( "*", "Destroyed", "Destroyed" ) + + --- Pickup Handler OnBefore for AI_CARGO_APC + -- @function [parent=#AI_CARGO_APC] OnBeforePickup + -- @param #AI_CARGO_APC self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Core.Point#COORDINATE Coordinate + -- @return #boolean + + --- Pickup Handler OnAfter for AI_CARGO_APC + -- @function [parent=#AI_CARGO_APC] OnAfterPickup + -- @param #AI_CARGO_APC self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Core.Point#COORDINATE Coordinate + + --- Pickup Trigger for AI_CARGO_APC + -- @function [parent=#AI_CARGO_APC] Pickup + -- @param #AI_CARGO_APC self + -- @param Core.Point#COORDINATE Coordinate + + --- Pickup Asynchronous Trigger for AI_CARGO_APC + -- @function [parent=#AI_CARGO_APC] __Pickup + -- @param #AI_CARGO_APC self + -- @param #number Delay + -- @param Core.Point#COORDINATE Coordinate + + --- Deploy Handler OnBefore for AI_CARGO_APC + -- @function [parent=#AI_CARGO_APC] OnBeforeDeploy + -- @param #AI_CARGO_APC self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Core.Point#COORDINATE Coordinate + -- @return #boolean + + --- Deploy Handler OnAfter for AI_CARGO_APC + -- @function [parent=#AI_CARGO_APC] OnAfterDeploy + -- @param #AI_CARGO_APC self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Core.Point#COORDINATE Coordinate + + --- Deploy Trigger for AI_CARGO_APC + -- @function [parent=#AI_CARGO_APC] Deploy + -- @param #AI_CARGO_APC self + -- @param Core.Point#COORDINATE Coordinate + + --- Deploy Asynchronous Trigger for AI_CARGO_APC + -- @function [parent=#AI_CARGO_APC] __Deploy + -- @param #AI_CARGO_APC self + -- @param Core.Point#COORDINATE Coordinate + -- @param #number Delay + + + --- Loaded Handler OnAfter for AI_CARGO_APC + -- @function [parent=#AI_CARGO_APC] OnAfterLoaded + -- @param #AI_CARGO_APC self + -- @param Wrapper.Group#GROUP APC + -- @param #string From + -- @param #string Event + -- @param #string To + + --- Unloaded Handler OnAfter for AI_CARGO_APC + -- @function [parent=#AI_CARGO_APC] OnAfterUnloaded + -- @param #AI_CARGO_APC self + -- @param Wrapper.Group#GROUP APC + -- @param #string From + -- @param #string Event + -- @param #string To + + self:__Monitor( 1 ) self:SetCarrier( CargoCarrier ) @@ -60,11 +138,11 @@ end --- Set the Carrier. -- @param #AI_CARGO_APC self --- @param Wrapper.Unit#UNIT CargoCarrier +-- @param Wrapper.Group#GROUP CargoCarrier -- @return #AI_CARGO_APC function AI_CARGO_APC:SetCarrier( CargoCarrier ) - self.CargoCarrier = CargoCarrier -- Wrapper.Unit#UNIT + self.CargoCarrier = CargoCarrier -- Wrapper.Group#GROUP self.CargoCarrier:SetState( self.CargoCarrier, "AI_CARGO_APC", self ) CargoCarrier:HandleEvent( EVENTS.Dead ) @@ -110,7 +188,7 @@ end -- @param #AI_CARGO_APC self -- @param Core.Point#COORDINATE Coordinate -- @param #number Radius --- @return Wrapper.Unit#UNIT NewCarrier +-- @return Wrapper.Group#GROUP NewCarrier function AI_CARGO_APC:FindCarrier( Coordinate, Radius ) local CoordinateZone = ZONE_RADIUS:New( "Zone" , Coordinate:GetVec2(), Radius ) @@ -122,11 +200,12 @@ function AI_CARGO_APC:FindCarrier( Coordinate, Radius ) local Attributes = NearUnit:GetDesc() self:F({Desc=Attributes}) if NearUnit:HasAttribute( "Trucks" ) then - self:SetCarrier( NearUnit ) - break + return NearUnit:GetGroup() end end end + + return nil end @@ -135,7 +214,7 @@ end --- Follow Infantry to the Carrier. -- @param #AI_CARGO_APC self -- @param #AI_CARGO_APC Me --- @param Wrapper.Unit#UNIT CargoCarrier +-- @param Wrapper.Group#GROUP CargoCarrier -- @param Wrapper.Group#GROUP InfantryGroup -- @return #AI_CARGO_APC function AI_CARGO_APC:FollowToCarrier( Me, CargoCarrier, InfantryGroup ) @@ -186,7 +265,7 @@ end --- @param #AI_CARGO_APC self --- @param Wrapper.Unit#UNIT CargoCarrier +-- @param Wrapper.Group#GROUP CargoCarrier function AI_CARGO_APC:onafterMonitor( CargoCarrier, From, Event, To ) self:F( { CargoCarrier, From, Event, To } ) @@ -195,32 +274,33 @@ function AI_CARGO_APC:onafterMonitor( CargoCarrier, From, Event, To ) local Coordinate = CargoCarrier:GetCoordinate() self.Zone:Scan( { Object.Category.UNIT } ) if self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then - if self:Is( "Unloaded" ) or self:Is( "Guarding" ) or self:Is( "Following" ) then + if self:Is( "Unloaded" ) or self:Is( "Following" ) then -- There are no enemies within combat range. Load the CargoCarrier. - self:__Load( 1 ) + self:Load() end else if self:Is( "Loaded" ) then -- There are enemies within combat range. Unload the CargoCarrier. self:__Unload( 1 ) - end - end - if self:Is( "Guarding" ) then - if not self.CargoGroup:IsNear( CargoCarrier, 5 ) then - self:Follow() - end - end - if self:Is( "Following" ) then - local Distance = Coordinate:Get2DDistance( self.CargoGroup:GetCoordinate() ) - self:F( { Distance = Distance } ) - if Distance > 40 then - CargoCarrier:RouteStop() - self.CarrierStopped = true else - if self.CarrierStopped then - if self.CargoGroup:IsNear( CargoCarrier, 10 ) then - CargoCarrier:RouteResume() - self.CarrierStopped = nil + if self:Is( "Unloaded" ) then + if not self.Cargo:IsNear( CargoCarrier, 5 ) then + self:Follow() + end + end + if self:Is( "Following" ) then + local Distance = Coordinate:Get2DDistance( self.Cargo:GetCoordinate() ) + self:F( { Distance = Distance } ) + if Distance > 40 then + CargoCarrier:RouteStop() + self.CarrierStopped = true + else + if self.CarrierStopped then + if self.Cargo:IsNear( CargoCarrier, 10 ) then + CargoCarrier:RouteResume() + self.CarrierStopped = nil + end + end end end end @@ -236,66 +316,77 @@ end --- @param #AI_CARGO_APC self --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_APC:onafterLoad( CargoCarrier, From, Event, To ) - self:F( { CargoCarrier, From, Event, To } ) +-- @param Wrapper.Group#GROUP Carrier +function AI_CARGO_APC:onbeforeLoad( Carrier, From, Event, To ) + self:F( { Carrier, From, Event, To } ) - if CargoCarrier and CargoCarrier:IsAlive() then - CargoCarrier:RouteStop() - self:__Board( 10 ) - self.CargoGroup:Board( CargoCarrier, 10 ) + if Carrier and Carrier:IsAlive() then + for _, Cargo in pairs( self.CargoSet:GetSet() ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + self:F( Cargo ) + if Cargo:IsInLoadRadius( Carrier:GetCoordinate() ) then + self:F( "In radius" ) + Carrier:RouteStop() + self:__Board( 1, Cargo ) + Cargo:Board( Carrier:GetUnit(1), 25 ) + return true + end + end end + return false + end --- @param #AI_CARGO_APC self --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_APC:onafterBoard( CargoCarrier, From, Event, To ) - self:F( { CargoCarrier, From, Event, To } ) +-- @param Wrapper.Group#GROUP Carrier +function AI_CARGO_APC:onafterBoard( Carrier, From, Event, To, Cargo ) + self:F( { Carrier, From, Event, To, Cargo } ) - if CargoCarrier and CargoCarrier:IsAlive() then - self:F({ IsLoaded = self.CargoGroup:IsLoaded() } ) - if not self.CargoGroup:IsLoaded() then - self:__Board( 10 ) + if Carrier and Carrier:IsAlive() then + self:F({ IsLoaded = Cargo:IsLoaded() } ) + if not Cargo:IsLoaded() then + self:__Board( 10, Cargo ) else - self:__Loaded( 1 ) + self:__Loaded( 1, Cargo ) end end end --- @param #AI_CARGO_APC self --- @param Wrapper.Unit#UNIT CargoCarrier -function AI_CARGO_APC:onafterLoaded( CargoCarrier, From, Event, To ) - self:F( { CargoCarrier, From, Event, To } ) +-- @param Wrapper.Group#GROUP CargoCarrier +function AI_CARGO_APC:onafterLoaded( CargoCarrier, From, Event, To, Cargo ) + self:F( { CargoCarrier, From, Event, To, Cargo } ) if CargoCarrier and CargoCarrier:IsAlive() then CargoCarrier:RouteResume() + self.Cargo = Cargo end end --- @param #AI_CARGO_APC self --- @param Wrapper.Unit#UNIT CargoCarrier +-- @param Wrapper.Group#GROUP CargoCarrier function AI_CARGO_APC:onafterUnload( CargoCarrier, From, Event, To ) self:F( { CargoCarrier, From, Event, To } ) if CargoCarrier and CargoCarrier:IsAlive() then CargoCarrier:RouteStop() - self.CargoGroup:UnBoard( ) + self.Cargo:UnBoard() self:__Unboard( 10 ) end end --- @param #AI_CARGO_APC self --- @param Wrapper.Unit#UNIT CargoCarrier +-- @param Wrapper.Group#GROUP CargoCarrier function AI_CARGO_APC:onafterUnboard( CargoCarrier, From, Event, To ) self:F( { CargoCarrier, From, Event, To } ) if CargoCarrier and CargoCarrier:IsAlive() then - if not self.CargoGroup:IsUnLoaded() then + if not self.Cargo:IsUnLoaded() then self:__Unboard( 10 ) else self:__Unloaded( 1 ) @@ -305,7 +396,7 @@ function AI_CARGO_APC:onafterUnboard( CargoCarrier, From, Event, To ) end --- @param #AI_CARGO_APC self --- @param Wrapper.Unit#UNIT CargoCarrier +-- @param Wrapper.Group#GROUP CargoCarrier function AI_CARGO_APC:onafterUnloaded( CargoCarrier, From, Event, To ) self:F( { CargoCarrier, From, Event, To } ) @@ -319,13 +410,13 @@ end --- @param #AI_CARGO_APC self --- @param Wrapper.Unit#UNIT CargoCarrier +-- @param Wrapper.Group#GROUP CargoCarrier function AI_CARGO_APC:onafterFollow( CargoCarrier, From, Event, To ) self:F( { CargoCarrier, From, Event, To } ) self:F( "Follow" ) if CargoCarrier and CargoCarrier:IsAlive() then - self.CargoGroup.CargoSet:ForEach( + self.Cargo.CargoSet:ForEach( --- @param Core.Cargo#CARGO Cargo function( Cargo ) self:F( { "Follow", Cargo.CargoObject:GetName() } ) @@ -339,3 +430,66 @@ function AI_CARGO_APC:onafterFollow( CargoCarrier, From, Event, To ) end + +--- @param #AI_CARGO_APC +-- @param Wrapper.Group#GROUP Carrier +function AI_CARGO_APC._Pickup( Carrier ) + + Carrier:F( { "AI_CARGO_APC._Pickup:", Carrier:GetName() } ) + + if Carrier:IsAlive() then + Carrier:__Load( 1 ) + end +end + + +--- @param #AI_CARGO_APC +-- @param Wrapper.Group#GROUP Carrier +function AI_CARGO_APC._Deploy( Carrier ) + + Carrier:F( { "AI_CARGO_APC._Deploy:", Carrier:GetName() } ) + + if Carrier:IsAlive() then + Carrier:__Unload( 1 ) + end +end + + + +--- @param #AI_CARGO_APC self +-- @param Wrapper.Group#GROUP Carrier +-- @param From +-- @param Event +-- @param To +-- @param Core.Point#COORDINATE Coordinate +-- @param #number Speed +function AI_CARGO_APC:onafterPickup( Carrier, From, Event, To, Coordinate, Speed ) + + if Carrier and Carrier:IsAlive() then + + self.RoutePickup = true + + Carrier:RouteGroundOnRoad( Coordinate, Speed, 1 ) + end + +end + + +--- @param #AI_CARGO_APC self +-- @param Wrapper.Group#GROUP Carrier +-- @param From +-- @param Event +-- @param To +-- @param Core.Point#COORDINATE Coordinate +-- @param #number Speed +function AI_CARGO_APC:onafterDeploy( Carrier, From, Event, To, Coordinate, Speed ) + + if Carrier and Carrier:IsAlive() then + + self.RouteDeploy = true + + Carrier:RouteGroundOnRoad( Coordinate, Speed, 1 ) + end + +end + diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 05afee6a4..ad502c322 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -24,7 +24,7 @@ AI_CARGO_HELICOPTER = { --- Creates a new AI_CARGO_HELICOPTER object. -- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Unit#UNIT Helicopter +-- @param Wrapper.Group#GROUP Helicopter -- @param Core.Set#SET_CARGO CargoSet -- @param #number CombatRadius -- @return #AI_CARGO_HELICOPTER @@ -116,13 +116,13 @@ end --- Set the Carrier. -- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Unit#UNIT Helicopter +-- @param Wrapper.Group#GROUP Helicopter -- @return #AI_CARGO_HELICOPTER function AI_CARGO_HELICOPTER:SetCarrier( Helicopter ) local AICargo = self - self.Helicopter = Helicopter -- Wrapper.Unit#UNIT + self.Helicopter = Helicopter -- Wrapper.Group#GROUP self.Helicopter:SetState( self.Helicopter, "AI_CARGO_HELICOPTER", self ) self.RoutePickup = false @@ -173,7 +173,7 @@ end -- @param #AI_CARGO_HELICOPTER self -- @param Core.Point#COORDINATE Coordinate -- @param #number Radius --- @return Wrapper.Unit#UNIT NewCarrier +-- @return Wrapper.Group#GROUP NewCarrier function AI_CARGO_HELICOPTER:FindCarrier( Coordinate, Radius ) local CoordinateZone = ZONE_RADIUS:New( "Zone" , Coordinate:GetVec2(), Radius ) @@ -183,18 +183,19 @@ function AI_CARGO_HELICOPTER:FindCarrier( Coordinate, Radius ) self:F({NearUnit=NearUnit}) if not NearUnit:GetState( NearUnit, "AI_CARGO_HELICOPTER" ) then local Attributes = NearUnit:GetDesc() - self:F({Desc=Attributes}) + self:F({Attributes=Attributes}) if NearUnit:HasAttribute( "Trucks" ) then - self:SetCarrier( NearUnit ) - break + return NearUnit:GetGroup() end end end + + return nil end --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Unit#UNIT Helicopter +-- @param Wrapper.Group#GROUP Helicopter -- @param From -- @param Event -- @param To @@ -221,7 +222,7 @@ end --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Unit#UNIT Helicopter +-- @param Wrapper.Group#GROUP Helicopter -- @param From -- @param Event -- @param To @@ -276,7 +277,7 @@ end --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Unit#UNIT Helicopter +-- @param Wrapper.Group#GROUP Helicopter -- @param From -- @param Event -- @param To @@ -331,7 +332,7 @@ end --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Unit#UNIT Helicopter +-- @param Wrapper.Group#GROUP Helicopter function AI_CARGO_HELICOPTER:onafterLoad( Helicopter, From, Event, To, Coordinate ) if Helicopter and Helicopter:IsAlive() then @@ -339,7 +340,7 @@ function AI_CARGO_HELICOPTER:onafterLoad( Helicopter, From, Event, To, Coordinat for _, Cargo in pairs( self.CargoSet:GetSet() ) do if Cargo:IsInLoadRadius( Coordinate ) then self:__Board( 5 ) - Cargo:Board( Helicopter, 25 ) + Cargo:Board( Helicopter:GetUnit(1), 25 ) self.Cargo = Cargo break end @@ -349,7 +350,7 @@ function AI_CARGO_HELICOPTER:onafterLoad( Helicopter, From, Event, To, Coordinat end --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Unit#UNIT Helicopter +-- @param Wrapper.Group#GROUP Helicopter function AI_CARGO_HELICOPTER:onafterBoard( Helicopter, From, Event, To ) if Helicopter and Helicopter:IsAlive() then @@ -364,7 +365,7 @@ function AI_CARGO_HELICOPTER:onafterBoard( Helicopter, From, Event, To ) end --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Unit#UNIT Helicopter +-- @param Wrapper.Group#GROUP Helicopter function AI_CARGO_HELICOPTER:onafterLoaded( Helicopter, From, Event, To ) if Helicopter and Helicopter:IsAlive() then @@ -374,7 +375,7 @@ end --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Unit#UNIT Helicopter +-- @param Wrapper.Group#GROUP Helicopter function AI_CARGO_HELICOPTER:onafterUnload( Helicopter, From, Event, To ) if Helicopter and Helicopter:IsAlive() then @@ -385,7 +386,7 @@ function AI_CARGO_HELICOPTER:onafterUnload( Helicopter, From, Event, To ) end --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Unit#UNIT Helicopter +-- @param Wrapper.Group#GROUP Helicopter function AI_CARGO_HELICOPTER:onafterUnboard( Helicopter, From, Event, To ) if Helicopter and Helicopter:IsAlive() then @@ -399,7 +400,7 @@ function AI_CARGO_HELICOPTER:onafterUnboard( Helicopter, From, Event, To ) end --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Unit#UNIT Helicopter +-- @param Wrapper.Group#GROUP Helicopter function AI_CARGO_HELICOPTER:onafterUnloaded( Helicopter, From, Event, To ) if Helicopter and Helicopter:IsAlive() then diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 446d58da4..6c2bbe033 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -566,14 +566,14 @@ do -- CARGO --- Check if Cargo is in the LoadRadius for the Cargo to be Boarded or Loaded. -- @param #CARGO self - -- @param Core.Point#Coordinate Coordinate + -- @param Core.Point#COORDINATE Coordinate -- @return #boolean true if the CargoGroup is within the loading radius. function CARGO:IsInLoadRadius( Coordinate ) self:F( { Coordinate, LoadRadius = self.LoadRadius } ) local Distance = 0 if self:IsUnLoaded() then - Distance = Coordinate:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) + Distance = Coordinate:Get2DDistance( self.CargoObject:GetCoordinate() ) self:T( Distance ) if Distance <= self.LoadRadius then return true @@ -586,14 +586,14 @@ do -- CARGO --- Check if the Cargo can report itself to be Boarded or Loaded. -- @param #CARGO self - -- @param Core.Point#Coordinate Coordinate + -- @param Core.Point#COORDINATE Coordinate -- @return #boolean true if the Cargo can report itself. function CARGO:IsInReportRadius( Coordinate ) self:F( { Coordinate } ) local Distance = 0 if self:IsUnLoaded() then - Distance = Coordinate:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) + Distance = Coordinate:Get2DDistance( self.CargoObject:GetCoordinate() ) self:T( Distance ) if Distance <= self.LoadRadius then return true @@ -606,18 +606,18 @@ do -- CARGO --- Check if CargoCarrier is near the Cargo to be Loaded. -- @param #CARGO self - -- @param Core.Point#POINT_VEC2 PointVec2 + -- @param Core.Point#COORDINATE Coordinate -- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). -- @return #boolean - function CARGO:IsNear( PointVec2, NearRadius ) + function CARGO:IsNear( Coordinate, NearRadius ) --self:F( { PointVec2 = PointVec2, NearRadius = NearRadius } ) if self.CargoObject:IsAlive() then - --local Distance = PointVec2:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) + --local Distance = PointVec2:Get2DDistance( self.CargoObject:GetPointVec2() ) --self:F( { CargoObjectName = self.CargoObject:GetName() } ) --self:F( { CargoObjectVec2 = self.CargoObject:GetVec2() } ) --self:F( { PointVec2 = PointVec2:GetVec2() } ) - local Distance = PointVec2:Get2DDistance( self.CargoObject:GetPointVec2() ) + local Distance = Coordinate:Get2DDistance( self.CargoObject:GetCoordinate() ) --self:F( Distance ) if Distance <= NearRadius then @@ -979,9 +979,9 @@ end function CARGO_PACKAGE:IsNear( CargoCarrier ) self:F() - local CargoCarrierPoint = CargoCarrier:GetPointVec2() + local CargoCarrierPoint = CargoCarrier:GetCoordinate() - local Distance = CargoCarrierPoint:DistanceFromPointVec2( self.CargoCarrier:GetPointVec2() ) + local Distance = CargoCarrierPoint:Get2DDistance( self.CargoCarrier:GetCoordinate() ) self:T( Distance ) if Distance <= self.NearRadius then diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua index 18c49a4db..7fc3a7293 100644 --- a/Moose Development/Moose/Cargo/CargoCrate.lua +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -166,14 +166,14 @@ do -- CARGO_CRATE --- Check if Cargo Crate is in the radius for the Cargo to be reported. -- @param #CARGO self - -- @param Core.Point#Coordinate Coordinate + -- @param Core.Point#COORDINATE Coordinate -- @return #boolean true if the Cargo Crate is within the report radius. function CARGO_CRATE:IsInReportRadius( Coordinate ) --self:F( { Coordinate, LoadRadius = self.LoadRadius } ) local Distance = 0 if self:IsUnLoaded() then - Distance = Coordinate:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) + Distance = Coordinate:Get2DDistance( self.CargoObject:GetCoordinate() ) --self:T( Distance ) if Distance <= self.LoadRadius then return true @@ -193,7 +193,7 @@ do -- CARGO_CRATE local Distance = 0 if self:IsUnLoaded() then - Distance = Coordinate:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) + Distance = Coordinate:Get2DDistance( self.CargoObject:GetCoordinate() ) --self:T( Distance ) if Distance <= self.NearRadius then return true diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index ab7addf54..d71c81a16 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -58,8 +58,9 @@ do -- CARGO_GROUP local WeightGroup = 0 - self.GroupName = CargoGroup:GetName() - self.CargoTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplate( self.GroupName ) ) + local GroupName = CargoGroup:GetName() + local CargoName = GroupName:match("(.*)~CARGO") or GroupName + self.CargoTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplate( GroupName ) ) CargoGroup:Destroy() @@ -67,11 +68,11 @@ do -- CARGO_GROUP for UnitID, UnitTemplate in pairs( self.CargoTemplate.units ) do local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate ) - local GroupName = env.getValueDictByKey( GroupTemplate.name ) + --local GroupName = env.getValueDictByKey( GroupTemplate.name ) -- We create a new group object with one unit... -- First we prepare the template... - GroupTemplate.name = GroupName .. "#CARGO#" .. UnitID + GroupTemplate.name = CargoName .. "#CARGO#" .. UnitID GroupTemplate.groupId = nil GroupTemplate.units = {} GroupTemplate.units[1] = UnitTemplate @@ -442,7 +443,7 @@ do -- CARGO_GROUP --- Check if Cargo Group is in the radius for the Cargo to be Boarded. -- @param #CARGO_GROUP self - -- @param Core.Point#Coordinate Coordinate + -- @param Core.Point#COORDINATE Coordinate -- @return #boolean true if the Cargo Group is within the load radius. function CARGO_GROUP:IsInLoadRadius( Coordinate ) --self:F( { Coordinate } ) @@ -452,12 +453,12 @@ do -- CARGO_GROUP if Cargo then local Distance = 0 if Cargo:IsLoaded() then - Distance = Coordinate:DistanceFromPointVec2( Cargo.CargoCarrier:GetPointVec2() ) + Distance = Coordinate:Get2DDistance( Cargo.CargoCarrier:GetCoordinate() ) else - Distance = Coordinate:DistanceFromPointVec2( Cargo.CargoObject:GetPointVec2() ) + Distance = Coordinate:Get2DDistance( Cargo.CargoObject:GetCoordinate() ) end - --self:T( Distance ) + self:F( { Distance = Distance, LoadRadius = self.LoadRadius } ) if Distance <= self.LoadRadius then return true else @@ -480,9 +481,10 @@ do -- CARGO_GROUP local Cargo = self.CargoSet:GetFirst() -- #CARGO if Cargo then + self:F( { Cargo } ) local Distance = 0 if Cargo:IsUnLoaded() then - Distance = Coordinate:DistanceFromPointVec2( Cargo.CargoObject:GetPointVec2() ) + Distance = Coordinate:Get2DDistance( Cargo.CargoObject:GetCoordinate() ) --self:T( Distance ) if Distance <= self.LoadRadius then return true diff --git a/Moose Development/Moose/Cargo/CargoSlingload.lua b/Moose Development/Moose/Cargo/CargoSlingload.lua index 642fde94f..cb7898175 100644 --- a/Moose Development/Moose/Cargo/CargoSlingload.lua +++ b/Moose Development/Moose/Cargo/CargoSlingload.lua @@ -119,14 +119,14 @@ do -- CARGO_SLINGLOAD --- Check if Cargo Crate is in the radius for the Cargo to be reported. -- @param #CARGO_SLINGLOAD self - -- @param Core.Point#Coordinate Coordinate + -- @param Core.Point#COORDINATE Coordinate -- @return #boolean true if the Cargo Crate is within the report radius. function CARGO_SLINGLOAD:IsInReportRadius( Coordinate ) --self:F( { Coordinate, LoadRadius = self.LoadRadius } ) local Distance = 0 if self:IsUnLoaded() then - Distance = Coordinate:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) + Distance = Coordinate:Get2DDistance( self.CargoObject:GetCoordinate() ) if Distance <= self.LoadRadius then return true end @@ -138,14 +138,14 @@ do -- CARGO_SLINGLOAD --- Check if Cargo Slingload is in the radius for the Cargo to be Boarded or Loaded. -- @param #CARGO_SLINGLOAD self - -- @param Core.Point#Coordinate Coordinate + -- @param Core.Point#COORDINATE Coordinate -- @return #boolean true if the Cargo Slingload is within the loading radius. function CARGO_SLINGLOAD:IsInLoadRadius( Coordinate ) --self:F( { Coordinate } ) local Distance = 0 if self:IsUnLoaded() then - Distance = Coordinate:DistanceFromPointVec2( self.CargoObject:GetPointVec2() ) + Distance = Coordinate:Get2DDistance( self.CargoObject:GetCoordinate() ) if Distance <= self.NearRadius then return true end diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index 318852cb1..d28d113a7 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -49,7 +49,7 @@ do -- CARGO_UNIT -- @return #CARGO_UNIT function CARGO_UNIT:New( CargoUnit, Type, Name, Weight, NearRadius ) local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, Weight, NearRadius ) ) -- #CARGO_UNIT - self:F( { Type, Name, Weight, NearRadius } ) + self:I( { Type, Name, Weight, NearRadius } ) self:T( CargoUnit ) self.CargoObject = CargoUnit diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 262175514..6173a355b 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -284,7 +284,7 @@ do -- cargo TemplateName = env.getValueDictByKey( TemplateName ) - local Cargo = TemplateName:match( "#(CARGO)" ) + local Cargo = TemplateName:match( "~(CARGO)" ) return Cargo and Cargo == "CARGO" end @@ -293,6 +293,7 @@ do -- cargo -- @param #DATABASE self -- @return #DATABASE self function DATABASE:RegisterCargos() + for CargoGroupName, CargoGroup in pairs( self.GROUPS ) do if self:IsCargo( CargoGroupName ) then @@ -300,11 +301,12 @@ do -- cargo local CargoParam = CargoInfo and CargoInfo:match( "%((.*)%)") local CargoName = CargoGroupName:match("(.*)~CARGO") local Type = CargoParam and CargoParam:match( "T=([%a%d ]+),?") - local Name = CargoParam and CargoParam:match( "N=([%a%d]+),?") - local LoadRadius = CargoParam and CargoParam:match( "RR=([%a%d]+),?") - local NearRadius = CargoParam and CargoParam:match( "NR=([%a%d]+),?") + local Name = CargoParam and CargoParam:match( "N=([%a%d]+),?") or CargoName + local LoadRadius = CargoParam and tonumber( CargoParam:match( "RR=([%a%d]+),?") ) + local NearRadius = CargoParam and tonumber( CargoParam:match( "NR=([%a%d]+),?") ) - CARGO_GROUP:New( CargoGroup, Type, Name or CargoName, LoadRadius, NearRadius ) + self:F({"Register CargoGroup:",Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius}) + CARGO_GROUP:New( CargoGroup, Type, Name, LoadRadius, NearRadius ) end end @@ -315,15 +317,17 @@ do -- cargo local CargoName = CargoStaticName:match("(.*)~CARGO") local Type = CargoParam and CargoParam:match( "T=([%a%d ]+),?") local Category = CargoParam and CargoParam:match( "C=([%a%d ]+),?") - local Name = CargoParam and CargoParam:match( "N=([%a%d]+),?") + local Name = CargoParam and CargoParam:match( "N=([%a%d]+),?") or CargoName local LoadRadius = CargoParam and tonumber( CargoParam:match( "RR=([%a%d]+),?") ) local NearRadius = CargoParam and tonumber( CargoParam:match( "NR=([%a%d]+),?") ) if Category == "SLING" then - CARGO_SLINGLOAD:New( CargoStatic, Type, Name or CargoName, LoadRadius, NearRadius ) + self:F({"Register CargoSlingload:",Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius}) + CARGO_SLINGLOAD:New( CargoStatic, Type, Name, LoadRadius, NearRadius ) else if Category == "CRATE" then - CARGO_CRATE:New( CargoStatic, Type, Name or CargoName, LoadRadius, NearRadius ) + self:F({"Register CargoCrate:",Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius}) + CARGO_CRATE:New( CargoStatic, Type, Name, LoadRadius, NearRadius ) end end end diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index de1a81cfc..15d9cf436 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -274,21 +274,19 @@ do -- COORDINATE - --TODO: check this to replace - --- Calculate the distance from a reference @{DCSTypes#Vec2}. + --- Calculate the distance from a reference @{#COORDINATE}. -- @param #COORDINATE self - -- @param Dcs.DCSTypes#Vec2 Vec2Reference The reference @{DCSTypes#Vec2}. - -- @return Dcs.DCSTypes#Distance The distance from the reference @{DCSTypes#Vec2} in meters. - function COORDINATE:DistanceFromVec2( Vec2Reference ) - self:F2( Vec2Reference ) + -- @param #COORDINATE PointVec2Reference The reference @{#COORDINATE}. + -- @return Dcs.DCSTypes#Distance The distance from the reference @{#COORDINATE} in meters. + function COORDINATE:DistanceFromPointVec2( PointVec2Reference ) + self:F2( PointVec2Reference ) - local Distance = ( ( Vec2Reference.x - self.x ) ^ 2 + ( Vec2Reference.y - self.z ) ^2 ) ^0.5 + local Distance = ( ( PointVec2Reference.x - self.x ) ^ 2 + ( PointVec2Reference.z - self.z ) ^2 ) ^ 0.5 self:T2( Distance ) return Distance end - --- Add a Distance in meters from the COORDINATE orthonormal plane, with the given angle, and calculate the new COORDINATE. -- @param #COORDINATE self -- @param Dcs.DCSTypes#Distance Distance The Distance to be added in meters. diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 0b68255c5..c63a94af8 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1761,351 +1761,354 @@ function CONTROLLABLE:TaskRoute( Points ) return DCSTask end ---- (AIR + GROUND) Make the Controllable move to fly to a given point. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec3 Point The destination point in Vec3 format. --- @param #number Speed The speed to travel. --- @return #CONTROLLABLE self -function CONTROLLABLE:RouteToVec2( Point, Speed ) - self:F2( { Point, Speed } ) +do -- Route methods - local ControllablePoint = self:GetUnit( 1 ):GetVec2() - - local PointFrom = {} - PointFrom.x = ControllablePoint.x - PointFrom.y = ControllablePoint.y - PointFrom.type = "Turning Point" - PointFrom.action = "Turning Point" - PointFrom.speed = Speed - PointFrom.speed_locked = true - PointFrom.properties = { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - } - - - local PointTo = {} - PointTo.x = Point.x - PointTo.y = Point.y - PointTo.type = "Turning Point" - PointTo.action = "Fly Over Point" - PointTo.speed = Speed - PointTo.speed_locked = true - PointTo.properties = { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - } - - - local Points = { PointFrom, PointTo } - - self:T3( Points ) - - self:Route( Points ) - - return self -end - ---- (AIR + GROUND) Make the Controllable move to a given point. --- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec3 Point The destination point in Vec3 format. --- @param #number Speed The speed to travel. --- @return #CONTROLLABLE self -function CONTROLLABLE:RouteToVec3( Point, Speed ) - self:F2( { Point, Speed } ) - - local ControllableVec3 = self:GetUnit( 1 ):GetVec3() - - local PointFrom = {} - PointFrom.x = ControllableVec3.x - PointFrom.y = ControllableVec3.z - PointFrom.alt = ControllableVec3.y - PointFrom.alt_type = "BARO" - PointFrom.type = "Turning Point" - PointFrom.action = "Turning Point" - PointFrom.speed = Speed - PointFrom.speed_locked = true - PointFrom.properties = { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - } - - - local PointTo = {} - PointTo.x = Point.x - PointTo.y = Point.z - PointTo.alt = Point.y - PointTo.alt_type = "BARO" - PointTo.type = "Turning Point" - PointTo.action = "Fly Over Point" - PointTo.speed = Speed - PointTo.speed_locked = true - PointTo.properties = { - ["vnav"] = 1, - ["scale"] = 0, - ["angle"] = 0, - ["vangle"] = 0, - ["steer"] = 2, - } - - - local Points = { PointFrom, PointTo } - - self:T3( Points ) - - self:Route( Points ) - - return self -end - - - ---- Make the controllable to follow a given route. --- @param #CONTROLLABLE self --- @param #table Route A table of Route Points. --- @param #number DelaySeconds Wait for the specified seconds before executing the Route. --- @return #CONTROLLABLE The CONTROLLABLE. -function CONTROLLABLE:Route( Route, DelaySeconds ) - self:F2( Route ) - - local DCSControllable = self:GetDCSObject() - if DCSControllable then - local RouteTask = self:TaskRoute( Route ) -- Create a RouteTask, that will route the CONTROLLABLE to the Route. - self:SetTask( RouteTask, DelaySeconds or 1 ) -- Execute the RouteTask after the specified seconds (default is 1). - return self - end - - return nil -end - - ---- Stops the movement of the vehicle on the route. --- @param #CONTROLLABLE self --- @return #CONTROLLABLE -function CONTROLLABLE:RouteStop() - self:F("RouteStop") + --- (AIR + GROUND) Make the Controllable move to fly to a given point. + -- @param #CONTROLLABLE self + -- @param Dcs.DCSTypes#Vec3 Point The destination point in Vec3 format. + -- @param #number Speed The speed to travel. + -- @return #CONTROLLABLE self + function CONTROLLABLE:RouteToVec2( Point, Speed ) + self:F2( { Point, Speed } ) - local CommandStop = self:CommandStopRoute( true ) - self:SetCommand( CommandStop ) - -end - ---- Resumes the movement of the vehicle on the route. --- @param #CONTROLLABLE self --- @return #CONTROLLABLE -function CONTROLLABLE:RouteResume() - self:F("RouteResume") + local ControllablePoint = self:GetUnit( 1 ):GetVec2() - local CommandResume = self:CommandStopRoute( false ) - self:SetCommand( CommandResume ) - -end - ---- Make the GROUND Controllable to drive towards a specific point. --- @param #CONTROLLABLE self --- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to. --- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h. --- @param #string Formation (optional) The route point Formation, which is a text string that specifies exactly the Text in the Type of the route point, like "Vee", "Echelon Right". --- @param #number DelaySeconds Wait for the specified seconds before executing the Route. --- @return #CONTROLLABLE The CONTROLLABLE. -function CONTROLLABLE:RouteGroundTo( ToCoordinate, Speed, Formation, DelaySeconds ) - - local FromCoordinate = self:GetCoordinate() - - local FromWP = FromCoordinate:WaypointGround() - local ToWP = ToCoordinate:WaypointGround( Speed, Formation ) - - self:Route( { FromWP, ToWP }, DelaySeconds ) - - return self -end - ---- Make the GROUND Controllable to drive towards a specific point using (only) roads. --- @param #CONTROLLABLE self --- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to. --- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h. --- @param #number DelaySeconds Wait for the specified seconds before executing the Route. --- @return #CONTROLLABLE The CONTROLLABLE. -function CONTROLLABLE:RouteGroundOnRoad( ToCoordinate, Speed, DelaySeconds ) - - -- Current coordinate. - local FromCoordinate = self:GetCoordinate() - - -- Formation is set to on road. - local Formation="On Road" - - -- Path on road from current position to destination coordinate. - local path=FromCoordinate:GetPathOnRoad(ToCoordinate) - - -- Route, ground waypoints along roads. - local route={} - table.insert(route, FromCoordinate:WaypointGround(Speed, Formation)) - - -- Convert coordinates to ground waypoints and insert into table. - for _, coord in ipairs(path) do - table.insert(route, coord:WaypointGround(Speed, Formation)) - end - - -- Add the final coordinate because the final coordinate in path is last point on road. - local dist=ToCoordinate:Get2DDistance(path[#path]) - if dist>10 then - table.insert(route, ToCoordinate:WaypointGround(Speed, "Vee")) - end - - -- Route controllable to destination. - self:Route(route, DelaySeconds) - - return self -end - - ---- Make the AIR Controllable fly towards a specific point. --- @param #CONTROLLABLE self --- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to. --- @param Core.Point#COORDINATE.RoutePointAltType AltType The altitude type. --- @param Core.Point#COORDINATE.RoutePointType Type The route point type. --- @param Core.Point#COORDINATE.RoutePointAction Action The route point action. --- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h. --- @param #number DelaySeconds Wait for the specified seconds before executing the Route. --- @return #CONTROLLABLE The CONTROLLABLE. -function CONTROLLABLE:RouteAirTo( ToCoordinate, AltType, Type, Action, Speed, DelaySeconds ) - - local FromCoordinate = self:GetCoordinate() - local FromWP = FromCoordinate:WaypointAir() - - local ToWP = ToCoordinate:WaypointAir( AltType, Type, Action, Speed ) - - self:Route( { FromWP, ToWP }, DelaySeconds ) - - return self -end - - ---- (AIR + GROUND) Route the controllable to a given zone. --- The controllable final destination point can be randomized. --- A speed can be given in km/h. --- A given formation can be given. --- @param #CONTROLLABLE self --- @param Core.Zone#ZONE Zone The zone where to route to. --- @param #boolean Randomize Defines whether to target point gets randomized within the Zone. --- @param #number Speed The speed. --- @param Base#FORMATION Formation The formation string. -function CONTROLLABLE:TaskRouteToZone( Zone, Randomize, Speed, Formation ) - self:F2( Zone ) - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - - local ControllablePoint = self:GetVec2() - local PointFrom = {} PointFrom.x = ControllablePoint.x PointFrom.y = ControllablePoint.y PointFrom.type = "Turning Point" - PointFrom.action = Formation or "Cone" - PointFrom.speed = 20 / 1.6 - - + PointFrom.action = "Turning Point" + PointFrom.speed = Speed + PointFrom.speed_locked = true + PointFrom.properties = { + ["vnav"] = 1, + ["scale"] = 0, + ["angle"] = 0, + ["vangle"] = 0, + ["steer"] = 2, + } + + local PointTo = {} - local ZonePoint - - if Randomize then - ZonePoint = Zone:GetRandomVec2() - else - ZonePoint = Zone:GetVec2() - end - - PointTo.x = ZonePoint.x - PointTo.y = ZonePoint.y + PointTo.x = Point.x + PointTo.y = Point.y PointTo.type = "Turning Point" - - if Formation then - PointTo.action = Formation - else - PointTo.action = "Cone" - end - - if Speed then - PointTo.speed = Speed - else - PointTo.speed = 20 / 1.6 - end - + PointTo.action = "Fly Over Point" + PointTo.speed = Speed + PointTo.speed_locked = true + PointTo.properties = { + ["vnav"] = 1, + ["scale"] = 0, + ["angle"] = 0, + ["vangle"] = 0, + ["steer"] = 2, + } + + local Points = { PointFrom, PointTo } - + self:T3( Points ) - + self:Route( Points ) - + return self end - - return nil -end - ---- (GROUND) Route the controllable to a given Vec2. --- A speed can be given in km/h. --- A given formation can be given. --- @param #CONTROLLABLE self --- @param #Vec2 Vec2 The Vec2 where to route to. --- @param #number Speed The speed. --- @param Base#FORMATION Formation The formation string. -function CONTROLLABLE:TaskRouteToVec2( Vec2, Speed, Formation ) - - local DCSControllable = self:GetDCSObject() - - if DCSControllable then - - local ControllablePoint = self:GetVec2() - + + --- (AIR + GROUND) Make the Controllable move to a given point. + -- @param #CONTROLLABLE self + -- @param Dcs.DCSTypes#Vec3 Point The destination point in Vec3 format. + -- @param #number Speed The speed to travel. + -- @return #CONTROLLABLE self + function CONTROLLABLE:RouteToVec3( Point, Speed ) + self:F2( { Point, Speed } ) + + local ControllableVec3 = self:GetUnit( 1 ):GetVec3() + local PointFrom = {} - PointFrom.x = ControllablePoint.x - PointFrom.y = ControllablePoint.y + PointFrom.x = ControllableVec3.x + PointFrom.y = ControllableVec3.z + PointFrom.alt = ControllableVec3.y + PointFrom.alt_type = "BARO" PointFrom.type = "Turning Point" - PointFrom.action = Formation or "Cone" - PointFrom.speed = 20 / 1.6 - - + PointFrom.action = "Turning Point" + PointFrom.speed = Speed + PointFrom.speed_locked = true + PointFrom.properties = { + ["vnav"] = 1, + ["scale"] = 0, + ["angle"] = 0, + ["vangle"] = 0, + ["steer"] = 2, + } + + local PointTo = {} - - PointTo.x = Vec2.x - PointTo.y = Vec2.y + PointTo.x = Point.x + PointTo.y = Point.z + PointTo.alt = Point.y + PointTo.alt_type = "BARO" PointTo.type = "Turning Point" - - if Formation then - PointTo.action = Formation - else - PointTo.action = "Cone" - end - - if Speed then - PointTo.speed = Speed - else - PointTo.speed = 60 / 3.6 - end - + PointTo.action = "Fly Over Point" + PointTo.speed = Speed + PointTo.speed_locked = true + PointTo.properties = { + ["vnav"] = 1, + ["scale"] = 0, + ["angle"] = 0, + ["vangle"] = 0, + ["steer"] = 2, + } + + local Points = { PointFrom, PointTo } - + self:T3( Points ) - + self:Route( Points ) - + return self end + + + + --- Make the controllable to follow a given route. + -- @param #CONTROLLABLE self + -- @param #table Route A table of Route Points. + -- @param #number DelaySeconds Wait for the specified seconds before executing the Route. + -- @return #CONTROLLABLE The CONTROLLABLE. + function CONTROLLABLE:Route( Route, DelaySeconds ) + self:F2( Route ) + + local DCSControllable = self:GetDCSObject() + if DCSControllable then + local RouteTask = self:TaskRoute( Route ) -- Create a RouteTask, that will route the CONTROLLABLE to the Route. + self:SetTask( RouteTask, DelaySeconds or 1 ) -- Execute the RouteTask after the specified seconds (default is 1). + return self + end + + return nil + end + + + --- Stops the movement of the vehicle on the route. + -- @param #CONTROLLABLE self + -- @return #CONTROLLABLE + function CONTROLLABLE:RouteStop() + self:F("RouteStop") + + local CommandStop = self:CommandStopRoute( true ) + self:SetCommand( CommandStop ) + + end + + --- Resumes the movement of the vehicle on the route. + -- @param #CONTROLLABLE self + -- @return #CONTROLLABLE + function CONTROLLABLE:RouteResume() + self:F("RouteResume") + + local CommandResume = self:CommandStopRoute( false ) + self:SetCommand( CommandResume ) + + end + + --- Make the GROUND Controllable to drive towards a specific point. + -- @param #CONTROLLABLE self + -- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to. + -- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h. + -- @param #string Formation (optional) The route point Formation, which is a text string that specifies exactly the Text in the Type of the route point, like "Vee", "Echelon Right". + -- @param #number DelaySeconds Wait for the specified seconds before executing the Route. + -- @return #CONTROLLABLE The CONTROLLABLE. + function CONTROLLABLE:RouteGroundTo( ToCoordinate, Speed, Formation, DelaySeconds ) + + local FromCoordinate = self:GetCoordinate() + + local FromWP = FromCoordinate:WaypointGround() + local ToWP = ToCoordinate:WaypointGround( Speed, Formation ) + + self:Route( { FromWP, ToWP }, DelaySeconds ) + + return self + end + + --- Make the GROUND Controllable to drive towards a specific point using (only) roads. + -- @param #CONTROLLABLE self + -- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to. + -- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h. + -- @param #number DelaySeconds Wait for the specified seconds before executing the Route. + -- @return #CONTROLLABLE The CONTROLLABLE. + function CONTROLLABLE:RouteGroundOnRoad( ToCoordinate, Speed, DelaySeconds ) + + -- Current coordinate. + local FromCoordinate = self:GetCoordinate() + + -- Formation is set to on road. + local Formation="On Road" + + -- Path on road from current position to destination coordinate. + local path=FromCoordinate:GetPathOnRoad(ToCoordinate) + + -- Route, ground waypoints along roads. + local route={} + table.insert(route, FromCoordinate:WaypointGround(Speed, Formation)) + + -- Convert coordinates to ground waypoints and insert into table. + for _, coord in ipairs(path) do + table.insert(route, coord:WaypointGround(Speed, Formation)) + end + + -- Add the final coordinate because the final coordinate in path is last point on road. + local dist=ToCoordinate:Get2DDistance(path[#path]) + if dist>10 then + table.insert(route, ToCoordinate:WaypointGround(Speed, "Vee")) + end + + -- Route controllable to destination. + self:Route(route, DelaySeconds) + + return self + end + + + --- Make the AIR Controllable fly towards a specific point. + -- @param #CONTROLLABLE self + -- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to. + -- @param Core.Point#COORDINATE.RoutePointAltType AltType The altitude type. + -- @param Core.Point#COORDINATE.RoutePointType Type The route point type. + -- @param Core.Point#COORDINATE.RoutePointAction Action The route point action. + -- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h. + -- @param #number DelaySeconds Wait for the specified seconds before executing the Route. + -- @return #CONTROLLABLE The CONTROLLABLE. + function CONTROLLABLE:RouteAirTo( ToCoordinate, AltType, Type, Action, Speed, DelaySeconds ) + + local FromCoordinate = self:GetCoordinate() + local FromWP = FromCoordinate:WaypointAir() + + local ToWP = ToCoordinate:WaypointAir( AltType, Type, Action, Speed ) + + self:Route( { FromWP, ToWP }, DelaySeconds ) + + return self + end + + + --- (AIR + GROUND) Route the controllable to a given zone. + -- The controllable final destination point can be randomized. + -- A speed can be given in km/h. + -- A given formation can be given. + -- @param #CONTROLLABLE self + -- @param Core.Zone#ZONE Zone The zone where to route to. + -- @param #boolean Randomize Defines whether to target point gets randomized within the Zone. + -- @param #number Speed The speed. + -- @param Base#FORMATION Formation The formation string. + function CONTROLLABLE:TaskRouteToZone( Zone, Randomize, Speed, Formation ) + self:F2( Zone ) + + local DCSControllable = self:GetDCSObject() + + if DCSControllable then + + local ControllablePoint = self:GetVec2() + + local PointFrom = {} + PointFrom.x = ControllablePoint.x + PointFrom.y = ControllablePoint.y + PointFrom.type = "Turning Point" + PointFrom.action = Formation or "Cone" + PointFrom.speed = 20 / 1.6 + + + local PointTo = {} + local ZonePoint + + if Randomize then + ZonePoint = Zone:GetRandomVec2() + else + ZonePoint = Zone:GetVec2() + end + + PointTo.x = ZonePoint.x + PointTo.y = ZonePoint.y + PointTo.type = "Turning Point" + + if Formation then + PointTo.action = Formation + else + PointTo.action = "Cone" + end + + if Speed then + PointTo.speed = Speed + else + PointTo.speed = 20 / 1.6 + end + + local Points = { PointFrom, PointTo } + + self:T3( Points ) + + self:Route( Points ) + + return self + end + + return nil + end + + --- (GROUND) Route the controllable to a given Vec2. + -- A speed can be given in km/h. + -- A given formation can be given. + -- @param #CONTROLLABLE self + -- @param #Vec2 Vec2 The Vec2 where to route to. + -- @param #number Speed The speed. + -- @param Base#FORMATION Formation The formation string. + function CONTROLLABLE:TaskRouteToVec2( Vec2, Speed, Formation ) + + local DCSControllable = self:GetDCSObject() + + if DCSControllable then + + local ControllablePoint = self:GetVec2() + + local PointFrom = {} + PointFrom.x = ControllablePoint.x + PointFrom.y = ControllablePoint.y + PointFrom.type = "Turning Point" + PointFrom.action = Formation or "Cone" + PointFrom.speed = 20 / 1.6 + + + local PointTo = {} + + PointTo.x = Vec2.x + PointTo.y = Vec2.y + PointTo.type = "Turning Point" + + if Formation then + PointTo.action = Formation + else + PointTo.action = "Cone" + end + + if Speed then + PointTo.speed = Speed + else + PointTo.speed = 60 / 3.6 + end + + local Points = { PointFrom, PointTo } + + self:T3( Points ) + + self:Route( Points ) + + return self + end + + return nil + end - return nil -end - +end -- Route methods -- Commands From 7dbc9436ed64fcc2b41632b7e0aa31949dfd7b00 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Tue, 24 Apr 2018 00:29:09 +0200 Subject: [PATCH 064/420] PseudoATC restructured menu --- .../Moose/Functional/PseudoATC.lua | 85 ++++++++++--------- Moose Development/Moose/Functional/Range.lua | 3 + 2 files changed, 50 insertions(+), 38 deletions(-) diff --git a/Moose Development/Moose/Functional/PseudoATC.lua b/Moose Development/Moose/Functional/PseudoATC.lua index 6295fc646..3d17615a7 100644 --- a/Moose Development/Moose/Functional/PseudoATC.lua +++ b/Moose Development/Moose/Functional/PseudoATC.lua @@ -70,7 +70,7 @@ PSEUDOATC={ ClassName = "PSEUDOATC", Debug=false, player={}, - maxairport=9, + maxairport=10, mdur=30, mrefresh=120, eventsmoose=true, @@ -231,8 +231,16 @@ function PSEUDOATC:_PlayerLanded(EventData) -- Get unit, player and place. local _unitName=EventData.IniUnitName local _unit, _playername=self:_GetPlayerUnitAndName(_unitName) - local _base=EventData.Place - local _baseName=EventData.PlaceName + local _base=nil + local _baseName=nil + if EventData.place then + _base=EventData.place + _baseName=EventData.place:getName() + end +-- if EventData.subplace then +-- local _subPlace=EventData.subplace +-- local _subPlaceName=EventData.subplace:getName() +-- end -- Call landed function. if _unit and _playername and _base then @@ -300,12 +308,13 @@ function PSEUDOATC:PlayerLanded(unit, place) local group=unit:GetGroup() local id=group:GetID() local PlayerName=self.player[id].playername - local UnitName=self.player[id].playername + local Callsign=self.player[id].callsign + local UnitName=self.player[id].unitname local GroupName=self.player[id].groupname local CallSign=self.player[id].callsign -- Debug message. - local text=string.format("Player %s (%s) from group %s with ID %d landed at %s", PlayerName, UnitName, GroupName, place) + local text=string.format("Player %s (%s) from group %s (ID %d) landed at %s", PlayerName, UnitName, GroupName, id, place) self:T(PSEUDOATC.id..text) MESSAGE:New(text, 30):ToAllIf(self.Debug) @@ -368,26 +377,28 @@ function PSEUDOATC:MenuRefresh(id) self:LocalAirports(id) -- Create submenu My Positon. - self:MenuAircraft(id) + --self:MenuAircraft(id) -- Create submenu airports. self:MenuAirports(id) end ---- Clear player menues. +--- Clear player menus. -- @param #PSEUDOATC self. -- @param #number id Group id of player unit. function PSEUDOATC:MenuClear(id) self:F(id) -- Debug message. - local text=string.format("Clearing menues for player %s in group %s.", self.player[id].playername, self.player[id].groupname) + local text=string.format("Clearing menus for player %s in group %s.", self.player[id].playername, self.player[id].groupname) self:T(PSEUDOATC.id..text) MESSAGE:New(text,30):ToAllIf(self.Debug) if self.player[id].menu_airports then + missionCommands.removeItemForGroup(id, self.player[id].menu_airports) + --[[ for name,item in pairs(self.player[id].menu_airports) do -- Debug message. @@ -396,7 +407,7 @@ function PSEUDOATC:MenuClear(id) -- Remove menu item. missionCommands.removeItemForGroup(id, self.player[id].menu_airports[name]) end - + ]] else self:T2(PSEUDOATC.id.."No airports to clear menus.") end @@ -407,7 +418,7 @@ function PSEUDOATC:MenuClear(id) end self.player[id].menu_airports=nil - self.player[id].menu_aircraft=nil + --self.player[id].menu_aircraft=nil end --- Create "F10/Pseudo ATC" menu items "Airport Data". @@ -417,7 +428,7 @@ function PSEUDOATC:MenuAirports(id) self:F(id) -- Table for menu entries. - self.player[id].menu_airports={} + self.player[id].menu_airports=missionCommands.addSubMenuForGroup(id, "Airports", self.player[id].menu_main) local i=0 for _,airport in pairs(self.player[id].airports) do @@ -432,15 +443,15 @@ function PSEUDOATC:MenuAirports(id) local pos=AIRBASE:FindByName(name):GetCoordinate() --F10menu_ATC_airports[ID][name] = missionCommands.addSubMenuForGroup(ID, name, F10menu_ATC) - local submenu=missionCommands.addSubMenuForGroup(id, name, self.player[id].menu_main) - self.player[id].menu_airports[name]=submenu + local submenu=missionCommands.addSubMenuForGroup(id, name, self.player[id].menu_airports) + --self.player[id].menu_airports[name]=submenu -- Create menu reporting commands missionCommands.addCommandForGroup(id, "Weather Report", submenu, self.ReportWeather, self, id, pos, name) - missionCommands.addCommandForGroup(id, "Request QFE", submenu, self.ReportPressure, self, id, "QFE", pos, name) - missionCommands.addCommandForGroup(id, "Request QNH", submenu, self.ReportPressure, self, id, "QNH", pos, name) - missionCommands.addCommandForGroup(id, "Request Wind", submenu, self.ReportWind, self, id, pos, name) - missionCommands.addCommandForGroup(id, "Request Temperature", submenu, self.ReportTemperature, self, id, pos, name) + --missionCommands.addCommandForGroup(id, "Request QFE", submenu, self.ReportPressure, self, id, "QFE", pos, name) + --missionCommands.addCommandForGroup(id, "Request QNH", submenu, self.ReportPressure, self, id, "QNH", pos, name) + --missionCommands.addCommandForGroup(id, "Request Wind", submenu, self.ReportWind, self, id, pos, name) + --missionCommands.addCommandForGroup(id, "Request Temperature", submenu, self.ReportTemperature, self, id, pos, name) missionCommands.addCommandForGroup(id, "Request BR", submenu, self.ReportBR, self, id, pos, name) -- Debug message. @@ -455,7 +466,7 @@ function PSEUDOATC:MenuAircraft(id) self:F(id) -- Table for menu entries. - self.player[id].menu_aircraft={} + --self.player[id].menu_aircraft={} local unit=self.player[id].unit --Wrapper.Unit#UNIT local callsign=self.player[id].callsign @@ -465,14 +476,13 @@ function PSEUDOATC:MenuAircraft(id) self:T(PSEUDOATC.id..string.format("Creating menu item %s for ID %d", name,id)) -- F10/PseudoATC/My Aircraft (callsign) - self.player[id].menu_aircraft.main = missionCommands.addSubMenuForGroup(id, name, self.player[id].menu_main) + --self.player[id].menu_aircraft.main = missionCommands.addSubMenuForGroup(id, name, self.player[id].menu_main) + - -- F10/PseudoATC/My Aircraft (callsign)/Waypoints if #self.player[id].waypoints>0 then - --F10menu_ATC_waypoints[ID]={} - self.player[id].menu_aircraft_waypoints={} - self.player[id].menu_aircraft_waypoints.main=missionCommands.addSubMenuForGroup(id, "Waypoints", self.player[id].menu_aircraft.main) + -- F10/PseudoATC/Waypoints + self.player[id].menu_waypoints=missionCommands.addSubMenuForGroup(id, "Waypoints", self.player[id].menu_main) local j=0 for i, wp in pairs(self.player[id].waypoints) do @@ -483,27 +493,26 @@ function PSEUDOATC:MenuAircraft(id) break -- max ten menu entries end + -- Position of Waypoint local pos=COORDINATE:New(wp.x,wp.alt,wp.z) - - local fname=string.format("Waypoint %d for %s", i-1, callsign) - local pname=string.format("Waypoint %d", i-1) + local name=string.format("Waypoint %d", i-1) - -- "F10/PseudoATC/My Aircraft (callsign)/Waypoints/Waypoint X" - local submenu=missionCommands.addSubMenuForGroup(id, pname, self.player[id].menu_aircraft_waypoints.main) - self.player[id].menu_aircraft_waypoints.pname=submenu + -- "F10/PseudoATC/Waypoints/Waypoint X" + local submenu=missionCommands.addSubMenuForGroup(id, name, self.player[id].menu_waypoints) -- Menu commands for each waypoint "F10/PseudoATC/My Aircraft (callsign)/Waypoints/Waypoint X/" - missionCommands.addCommandForGroup(id, "Weather Report", submenu, self.ReportWeather, self, id, pos, pname) - missionCommands.addCommandForGroup(id, "Request QFE", submenu, self.ReportPressure, self, id, "QFE", pos, pname) - missionCommands.addCommandForGroup(id, "Request QNH", submenu, self.ReportPressure, self, id, "QNH", pos, pname) - missionCommands.addCommandForGroup(id, "Request Wind", submenu, self.ReportWind, self, id, pos, pname) - missionCommands.addCommandForGroup(id, "Request Temperature", submenu, self.ReportTemperature, self, id, pos, pname) - missionCommands.addCommandForGroup(id, "Request BR", submenu, self.ReportBR, self, id, pos, pname) + missionCommands.addCommandForGroup(id, "Weather Report", submenu, self.ReportWeather, self, id, pos, name) + --missionCommands.addCommandForGroup(id, "Request QFE", submenu, self.ReportPressure, self, id, "QFE", pos, pname) + --missionCommands.addCommandForGroup(id, "Request QNH", submenu, self.ReportPressure, self, id, "QNH", pos, pname) + --missionCommands.addCommandForGroup(id, "Request Wind", submenu, self.ReportWind, self, id, pos, pname) + --missionCommands.addCommandForGroup(id, "Request Temperature", submenu, self.ReportTemperature, self, id, pos, pname) + missionCommands.addCommandForGroup(id, "Request BR", submenu, self.ReportBR, self, id, pos, name) end end - missionCommands.addCommandForGroup(id, "Request current altitude AGL", self.player[id].menu_aircraft.main, self.ReportHeight, self, id) - missionCommands.addCommandForGroup(id, "Report altitude until touchdown", self.player[id].menu_aircraft.main, self.AltidudeStartTimer, self, id) - missionCommands.addCommandForGroup(id, "Quit reporting altitude", self.player[id].menu_aircraft.main, self.AltidudeStopTimer, self, id) + + missionCommands.addCommandForGroup(id, "Request current altitude AGL", self.player[id].menu_main, self.ReportHeight, self, id) + missionCommands.addCommandForGroup(id, "Report altitude until touchdown", self.player[id].menu_main, self.AltidudeStartTimer, self, id) + missionCommands.addCommandForGroup(id, "Quit reporting altitude", self.player[id].menu_main, self.AltidudeStopTimer, self, id) end ----------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index a9ba91251..bf8cbb193 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -211,6 +211,9 @@ -- -- The function @{#RANGE.DebugON}() can be used to send messages on screen. It also smokes all defined strafe and bombing targets, the strafe pit approach boxes and the range zone. -- +-- Note that it can happen that the RANGE radio menu is not shown. Check that the range object is defined as a **global** variable rather than a local one. +-- The could avoid the lua garbage collection to accidentally/falsely deallocate the RANGE objects. +-- -- -- -- @field #RANGE From 441fba0830522e8f1f3cef6133a3437edbf6dd2d Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Tue, 24 Apr 2018 21:12:35 +0200 Subject: [PATCH 065/420] PseudoATC Fixed waypoints BR --- Moose Development/Moose/Functional/PseudoATC.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Functional/PseudoATC.lua b/Moose Development/Moose/Functional/PseudoATC.lua index 3d17615a7..d70e9d069 100644 --- a/Moose Development/Moose/Functional/PseudoATC.lua +++ b/Moose Development/Moose/Functional/PseudoATC.lua @@ -68,7 +68,7 @@ -- @field #PSEUDOATC PSEUDOATC={ ClassName = "PSEUDOATC", - Debug=false, + Debug=true, player={}, maxairport=10, mdur=30, @@ -477,8 +477,7 @@ function PSEUDOATC:MenuAircraft(id) -- F10/PseudoATC/My Aircraft (callsign) --self.player[id].menu_aircraft.main = missionCommands.addSubMenuForGroup(id, name, self.player[id].menu_main) - - + if #self.player[id].waypoints>0 then -- F10/PseudoATC/Waypoints @@ -494,7 +493,7 @@ function PSEUDOATC:MenuAircraft(id) end -- Position of Waypoint - local pos=COORDINATE:New(wp.x,wp.alt,wp.z) + local pos=COORDINATE:New(wp.x, wp.alt, wp.y) local name=string.format("Waypoint %d", i-1) -- "F10/PseudoATC/Waypoints/Waypoint X" @@ -705,7 +704,8 @@ function PSEUDOATC:ReportBR(id, position, location) local coord=unit:GetCoordinate() -- Direction vector from current position (coord) to target (position). - local vec3=coord:GetDirectionVec3(position) + local pos=coord:Translate(30,90) + local vec3=coord:GetDirectionVec3(pos) local angle=coord:GetAngleDegrees(vec3) local range=coord:Get2DDistance(position) From 33271edf78a8bc03c74c18c71984c1f95f11f300 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Tue, 24 Apr 2018 23:38:41 +0200 Subject: [PATCH 066/420] Added ARTY class First draft... --- .../Moose/Functional/Artillery.lua | 267 ++++++++++++++++++ 1 file changed, 267 insertions(+) create mode 100644 Moose Development/Moose/Functional/Artillery.lua diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua new file mode 100644 index 000000000..0695ae427 --- /dev/null +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -0,0 +1,267 @@ +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- **Functional** - Control artillery units. +-- +-- ![Banner Image](..\Presentations\ARTILLERY\Artillery_Main.png) +-- +-- ==== +-- +-- Make artillery fire on targets. +-- +-- ==== +-- +-- # Demo Missions +-- +-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- +-- ==== +-- +-- # YouTube Channel +-- +-- ### [MOOSE YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl1jirWIo4t4YxqN-HxjqRkL) +-- +-- === +-- +-- ### Author: **[funkyfranky](https://forums.eagle.ru/member.php?u=115026)** +-- +-- ### Contributions: **Sven van de Velde ([FlightControl](https://forums.eagle.ru/member.php?u=89536))** +-- +-- ==== +-- @module Arty + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- ARTY class +-- @type ARTY +-- @field #string ClassName Name of the class. +-- @field #boolean Debug Write Debug messages to DCS log file and send Debug messages to all players. +-- @field #table targets Targets assigned. +-- @extends Core.Fsm#FSM_CONTROLLABLE +-- + +---# ARTY class, extends @{Core.Fsm#FSM_CONTROLLABLE} +-- Artillery class.. +-- +-- ## Target aquisition... +-- +-- ![Process](..\Presentations\ART\Arty_Process.png) +-- +-- The arty process can be described as follows. +-- +-- ### Submenu +-- +-- @field #ARTY +ARTY={ + ClassName = "ARTY", + Debug = false, + targets = {}, +} + +--- Enumerator of possible rules of engagement. +-- @field #list ROE +ARTY.ROE={ + Hold="Weapon Hold", + Free="Weapon Free", + Return="Return Fire", +} + +--- Enumerator of possible alarm states. +-- @field #list AlarmState +ARTY.AlarmState={ + Auto="Auto", + Green="Green", + Red="Red", +} + +--- Main F10 menu for suppresion, i.e. F10/Artillery. +-- @field #string MenuF10 +ARTY.MenuF10=nil + +--- Some ID to identify who we are in output of the DCS.log file. +-- @field #string id +ARTY.id="ARTY | " + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- TODO list: +-- TODO: don't know yet... +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Creates a new ARTY object. +-- @param #ARTY self +-- @param Wrapper.Group#GROUP group The GROUP object for which artillery tasks should be assigned. +-- @return #ARTY ARTY object. +-- @return nil If group does not exist or is not a ground group. +function ARTY:New(group) + BASE:F2(group) + + -- Inherits from FSM_CONTROLLABLE + local self=BASE:Inherit(self, FSM_CONTROLLABLE:New()) -- #ARTY + + -- Check that group is present. + if group then + self:T(ARTY.id.."ARTY group "..group:GetName()) + else + self:E(ARTY.id.."ARTY: Requested group does not exist! (Has to be a MOOSE group.)") + return nil + end + + -- Check that we actually have a GROUND group. + if group:IsGround()==false and group:IsShip()==false then + self:E(ARTY.id.."ARTY group "..group:GetName().." has to be a GROUND or SHIP group!") + return nil + end + + -- Set the controllable for the FSM. + self:SetControllable(group) + + -- Get DCS descriptors of group. + local DCSgroup=Group.getByName(group:GetName()) + local DCSunit=DCSgroup:getUnit(1) + self.DCSdesc=DCSunit:getDesc() + + -- Get max speed the group can do and convert to km/h. + --self.SpeedMax=self.DCSdesc.speedMaxOffRoad*3.6 + + -- Set speed to maximum. + --self.Speed=self.SpeedMax + + -- Is this infantry or not. + self.IsInfantry=DCSunit:hasAttribute("Infantry") + + -- Type of group. + self.Type=group:GetTypeName() + + -- Initial group strength. + self.IniGroupStrength=#group:GetUnits() + + -- Set ROE and Alarm State. + --self:SetDefaultROE("Free") + --self:SetDefaultAlarmState("Auto") + + -- Transitions + self:AddTransition("*", "Start", "CombatReady") + self:AddTransition("CombatReady", "OpenFire", "Firing") + self:AddTransition("Firing", "CeaseFire", "CombatReady") + self:AddTransition("*", "Dead", "*") + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- After "Start" event. Initialized ROE and alarm state. Starts the event handler. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function ARTY:onafterStart(Controllable, From, Event, To) + self:_EventFromTo("onafterStart", Event, From, To) + + local text=string.format("Started ARTY for group %s.", Controllable:GetName()) + MESSAGE:New(text, 10):ToAllIf(self.Debug) + + -- Create main F10 menu if it is not there yet. + if self.MenuON then + if not ARTY.MenuF10 then + ARTY.MenuF10 = MENU_MISSION:New("ARTY") + end + self:_CreateMenuGroup() + end + + -- Set the current ROE and alam state. + --self:_SetAlarmState(self.DefaultAlarmState) + --self:_SetROE(self.DefaultROE) + + local text=string.format("\n******************************************************\n") + text=text..string.format("Arty group = %s\n", Controllable:GetName()) + text=text..string.format("Type = %s\n", self.Type) + text=text..string.format("******************************************************\n") + self:T(ARTY.id..text) + + -- Add event handler. + self:HandleEvent(EVENTS.Shot, self._OnEventShot) + self:HandleEvent(EVENTS.Dead, self._OnEventDead) + +end + + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Assign a group of targets +-- @param #ARTY self +-- @param Wrapper.Group#GROUP group Group of targets. +-- @param #number range Range. +function ARTY:AssignTargetGroup(group, range) + self:E({group=group, range=range}) + + local _target={coord=group:GetCoordinate(), range=range} + + table.insert(self.targets, _target) + --table.insert(self.strafeTargets, {name=_name, polygon=_polygon, coordinate= Ccenter, goodPass=goodpass, targets=_targets, foulline=foulline, smokepoints=p, heading=heading}) + + local vec2=group:GetVec2() + --local zone=ZONE:New("target", vec2, range) + local zone=ZONE_RADIUS:New("target", vec2, range) + self:_FireAtZone(zone, 10) + +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +function ARTY:_OnEventShot(EventData) + self:F(EventData) +end + +function ARTY:_OnEventDead(EventData) + self:F(EventData) +end + +--- Set task for firing at a zone +-- @param #ARTY self +-- @param Wrapper.Zone#ZONE zone Zone to fire upon. +-- @param #number nshells Number of shells to fire. +function ARTY:_FireAtZone(zone, nshells) + self:E({zone=zone, nshells=nshells}) + + local group=self.Controllable --Wrapper.Controllable#CONTROLLABLE + + local units=group:GetUnits() + local nunits=#units + + local nshells_tot=nshells*nunits + + -- set ROE to weapon free + group:OptionROEWeaponFree() + + -- assign task + local q=zone:GetVec2() + local r=zone:GetRadius() + local fire=group:TaskFireAtPoint(q, r, nshells_tot) + + -- Execute task + group:SetTask(fire) +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Print event-from-to string to DCS log file. +-- @param #ARTY self +-- @param #string BA Before/after info. +-- @param #string Event Event. +-- @param #string From From state. +-- @param #string To To state. +function ARTY:_EventFromTo(BA, Event, From, To) + local text=string.format("\n%s: %s EVENT %s: %s --> %s", BA, self.Controllable:GetName(), Event, From, To) + self:T(ARTY.id..text) +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- \ No newline at end of file From 0ec3192fb7d993e1b30e147f53e1044a8061915c Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 25 Apr 2018 22:49:36 +0200 Subject: [PATCH 067/420] Arty improvements --- .../Moose/Functional/Artillery.lua | 112 ++++++++++++++---- 1 file changed, 91 insertions(+), 21 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 0695ae427..ea95af7dc 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -84,6 +84,7 @@ ARTY.id="ARTY | " -- TODO list: -- TODO: don't know yet... + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Creates a new ARTY object. @@ -185,6 +186,25 @@ function ARTY:onafterStart(Controllable, From, Event, To) self:HandleEvent(EVENTS.Shot, self._OnEventShot) self:HandleEvent(EVENTS.Dead, self._OnEventDead) + -- Start scheduler to monitor task queue. + self.TaskQueueSched=SCHEDULER:New(nil, ARTY._CheckTaskQueue, {self}, 5, 10) + +end + +--- Assign a group of targets +-- @param #ARTY self +function ARTY:_CheckTaskQueue() + self:F() + + local _counter=0 + for _,target in pairs(self.targets) do + if target.underfire==false then + env.info(ARTY.id..string.format("Opening fire on target %s", target.name)) + self:OpenFire(target) + break + end + end + end @@ -195,20 +215,29 @@ end --- Assign a group of targets -- @param #ARTY self -- @param Wrapper.Group#GROUP group Group of targets. --- @param #number range Range. -function ARTY:AssignTargetGroup(group, range) - self:E({group=group, range=range}) +-- @param #number radius (Optional) Radius. Default is 100 m. +-- @param #number nshells (Optional) How many shells are fired on target per unit. Default 5. +function ARTY:AssignTargetGroup(group, radius, nshells) + self:E({group=group, radius=radius, nshells=nshells}) - local _target={coord=group:GetCoordinate(), range=range} + nshells=nshells or 5 + radius=radius or 100 + local coord=group:GetCoordinate() + local name=group:GetName() + + -- Prepare target array. + local _target={name=name, coord=coord, radius=radius, nshells=nshells, engaged=0, underfire=false} + + -- Add to table. table.insert(self.targets, _target) - --table.insert(self.strafeTargets, {name=_name, polygon=_polygon, coordinate= Ccenter, goodPass=goodpass, targets=_targets, foulline=foulline, smokepoints=p, heading=heading}) - - local vec2=group:GetVec2() - --local zone=ZONE:New("target", vec2, range) - local zone=ZONE_RADIUS:New("target", vec2, range) - self:_FireAtZone(zone, 10) + -- Debug info. + env.info(ARTY.id.."Targets:") + for _,target in pairs(self.targets) do + env.info(ARTY.id..string.format("Name: %s", target.name)) + end + end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -216,6 +245,7 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- function ARTY:_OnEventShot(EventData) + env.info("Event Shot") self:F(EventData) end @@ -223,29 +253,33 @@ function ARTY:_OnEventDead(EventData) self:F(EventData) end ---- Set task for firing at a zone +--- Set task for firing at a coordinate. -- @param #ARTY self --- @param Wrapper.Zone#ZONE zone Zone to fire upon. --- @param #number nshells Number of shells to fire. -function ARTY:_FireAtZone(zone, nshells) - self:E({zone=zone, nshells=nshells}) +-- @param Core.Point#COORDINATE coord Coordinates to fire upon. +-- @param #number radius Radius around coordinate. +-- @param #number nshells Number of shells to fire per unit. +function ARTY:_FireAtCoord(coord, radius, nshells) + self:E({coord=coord, radius=radius, nshells=nshells}) + -- Controllable. local group=self.Controllable --Wrapper.Controllable#CONTROLLABLE + -- Number of units. local units=group:GetUnits() local nunits=#units local nshells_tot=nshells*nunits - -- set ROE to weapon free + -- Set ROE to weapon free. group:OptionROEWeaponFree() - -- assign task - local q=zone:GetVec2() - local r=zone:GetRadius() - local fire=group:TaskFireAtPoint(q, r, nshells_tot) + -- Get Vec2 + local vec2=coord:GetVec2() - -- Execute task + -- Get task. + local fire=group:TaskFireAtPoint(vec2, radius, nshells_tot) + + -- Execute task. group:SetTask(fire) end @@ -253,6 +287,42 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Before "OpenFire" event. +-- @param #SUPPRESSION self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #table target Array holding the target info. +-- @return boolean +function ARTY:onbeforeOpenFire(Controllable, From, Event, To, target) + self:_EventFromTo("onbeforeOpenFire", Event, From, To) + + return true +end + +--- After "OpenFire" event. +-- @param #SUPPRESSION self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #table target Array holding the target info. _target={coord=coord, radius=radius, nshells=nshells, engaged=0, underattack=false} +function ARTY:onbeforeOpenFire(Controllable, From, Event, To, target) + self:_EventFromTo("onafterOpenFire", Event, From, To) + + local _coord=target.coord --Core.Point#COORDINATE + + --_coord:MarkToAll("Arty Target") + + self:_FireAtCoord(target.coord, target.radius, target.nshells) + +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + --- Print event-from-to string to DCS log file. -- @param #ARTY self -- @param #string BA Before/after info. From b14a672b0ef438c1fb83619c1869ab6cc039d15b Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 26 Apr 2018 23:08:16 +0200 Subject: [PATCH 068/420] ARTY improvements. --- .../Moose/Functional/Artillery.lua | 360 ++++++++++++++---- 1 file changed, 295 insertions(+), 65 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index ea95af7dc..00c83a08a 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -52,8 +52,10 @@ -- @field #ARTY ARTY={ ClassName = "ARTY", - Debug = false, + Debug = true, targets = {}, + currentTarget = nil, + Nshots=0, } --- Enumerator of possible rules of engagement. @@ -80,6 +82,10 @@ ARTY.MenuF10=nil -- @field #string id ARTY.id="ARTY | " +--- Range script version. +-- @field #number version +ARTY.version="0.1.0" + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list: @@ -100,15 +106,15 @@ function ARTY:New(group) -- Check that group is present. if group then - self:T(ARTY.id.."ARTY group "..group:GetName()) + self:T(ARTY.id..string.format("ARTY script version %s. Added group %s.", ARTY.version, group:GetName())) else - self:E(ARTY.id.."ARTY: Requested group does not exist! (Has to be a MOOSE group.)") + self:E(ARTY.id.."ERROR! Requested ARTY group does not exist! (Has to be a MOOSE group.)") return nil end -- Check that we actually have a GROUND group. if group:IsGround()==false and group:IsShip()==false then - self:E(ARTY.id.."ARTY group "..group:GetName().." has to be a GROUND or SHIP group!") + self:E(ARTY.id..string.format("ERROR! ARTY group %s has to be a GROUND or SHIP group!",group:GetName())) return nil end @@ -142,7 +148,8 @@ function ARTY:New(group) -- Transitions self:AddTransition("*", "Start", "CombatReady") self:AddTransition("CombatReady", "OpenFire", "Firing") - self:AddTransition("Firing", "CeaseFire", "CombatReady") + self:AddTransition("Firing", "CeaseFire", "CombatReady") + self:AddTransition("*", "NoAmmo", "OutOfAmmo") self:AddTransition("*", "Dead", "*") return self @@ -152,6 +159,41 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Assign a group of target(s). +-- @param #ARTY self +-- @param Wrapper.Group#GROUP group Group of targets. +-- @param #number radius (Optional) Radius. Default is 100 m. +-- @param #number nshells (Optional) How many shells are fired on target per unit. Default 5. +-- @param #number prio (Optional) Priority of target. Number between 1 (high) and 100 (low). Default 50. +function ARTY:AssignTargetGroup(group, radius, nshells, prio) + self:E({group=group, radius=radius, nshells=nshells, prio=prio}) + + -- Set default values. + nshells=nshells or 5 + radius=radius or 100 + prio=prio or 50 + prio=math.max( 1, prio) + prio=math.min(100, prio) + + -- Coordinate and name. + local coord=group:GetCoordinate() + local name=group:GetName() + + -- Prepare target array. + local _target={name=name, coord=coord, radius=radius, nshells=nshells, engaged=0, underfire=false, prio=prio} + + -- Add to table. + table.insert(self.targets, _target) + + -- Debug info. + env.info(ARTY.id..string.format("Added target %s, radius=%d, nshells=%d, prio=%d.", name, radius, nshells, prio)) + +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + --- After "Start" event. Initialized ROE and alarm state. Starts the event handler. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. @@ -181,7 +223,14 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("Type = %s\n", self.Type) text=text..string.format("******************************************************\n") self:T(ARTY.id..text) - + + -- Get Ammo. + self:_GetAmmo(self.Controllable) + + for _, target in pairs(self.targets) do + env.info(ARTY.id..string.format("Target %s, radius=%d, nshells=%d, prio=%d.", target.name, target.radius, target.nshells, target.prio)) + end + -- Add event handler. self:HandleEvent(EVENTS.Shot, self._OnEventShot) self:HandleEvent(EVENTS.Dead, self._OnEventDead) @@ -191,64 +240,57 @@ function ARTY:onafterStart(Controllable, From, Event, To) end ---- Assign a group of targets +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Eventhandler for shot event. -- @param #ARTY self -function ARTY:_CheckTaskQueue() - self:F() +-- @param Core.Event#EVENTDATA EventData +function ARTY:_OnEventShot(EventData) + self:F(EventData) - local _counter=0 - for _,target in pairs(self.targets) do - if target.underfire==false then - env.info(ARTY.id..string.format("Opening fire on target %s", target.name)) - self:OpenFire(target) - break - end - end - -end - - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Assign a group of targets --- @param #ARTY self --- @param Wrapper.Group#GROUP group Group of targets. --- @param #number radius (Optional) Radius. Default is 100 m. --- @param #number nshells (Optional) How many shells are fired on target per unit. Default 5. -function ARTY:AssignTargetGroup(group, radius, nshells) - self:E({group=group, radius=radius, nshells=nshells}) - - nshells=nshells or 5 - radius=radius or 100 - - local coord=group:GetCoordinate() - local name=group:GetName() - - -- Prepare target array. - local _target={name=name, coord=coord, radius=radius, nshells=nshells, engaged=0, underfire=false} - - -- Add to table. - table.insert(self.targets, _target) + -- Weapon data. + local _weapon = EventData.Weapon:getTypeName() -- should be the same as Event.WeaponTypeName + local _weaponStrArray = self:_split(_weapon,"%.") + local _weaponName = _weaponStrArray[#_weaponStrArray] -- Debug info. - env.info(ARTY.id.."Targets:") - for _,target in pairs(self.targets) do - env.info(ARTY.id..string.format("Name: %s", target.name)) + self:T(ARTY.id.."EVENT SHOT: Ini unit = "..EventData.IniUnitName) + self:T(ARTY.id.."EVENT SHOT: Ini group = "..EventData.IniGroupName) + self:T(ARTY.id.."EVENT SHOT: Weapon type = ".._weapon) + self:T(ARTY.id.."EVENT SHOT: Weapon name = ".._weaponName) + + local group = EventData.IniGroup --Wrapper.Group#GROUP + + if group and group:IsAlive() then + + if EventData.IniGroupName == self.Controllable:GetName() then + + if self.currentTarget then + + -- Increase number of shots fired by this group on this target. + self.Nshots=self.Nshots+1 + + -- Debug output. + self:T(ARTY.id..string.format("Group %s fired shot # %d on target %s.", self.Controllable:GetName(), self.Nshots, self.currentTarget.name)) + + -- Check if number of shots reached max. + if self.Nshots >= self.currentTarget.nshells then + self:CeaseFire(self.currentTarget) + self.Nshots=0 + end + + else + self:T(ARTY.id..string.format("No current target?!")) + end + end end - -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - -function ARTY:_OnEventShot(EventData) - env.info("Event Shot") - self:F(EventData) end +--- Eventhandler for dead event. +-- @param #ARTY self +-- @param Core.Event#EVENTDATA EventData function ARTY:_OnEventDead(EventData) self:F(EventData) end @@ -257,7 +299,7 @@ end -- @param #ARTY self -- @param Core.Point#COORDINATE coord Coordinates to fire upon. -- @param #number radius Radius around coordinate. --- @param #number nshells Number of shells to fire per unit. +-- @param #number nshells Number of shells to fire. function ARTY:_FireAtCoord(coord, radius, nshells) self:E({coord=coord, radius=radius, nshells=nshells}) @@ -288,41 +330,211 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Before "OpenFire" event. --- @param #SUPPRESSION self +-- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param #table target Array holding the target info. --- @return boolean +-- @return #boolean If true proceed to onafterOpenfire. function ARTY:onbeforeOpenFire(Controllable, From, Event, To, target) self:_EventFromTo("onbeforeOpenFire", Event, From, To) - + + if self.currentTarget then + self:T(ARTY.id..string.format("Group %s already has a target %s.", self.Controllable:GetName(), target.name)) + return false + end + return true end --- After "OpenFire" event. --- @param #SUPPRESSION self +-- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param #table target Array holding the target info. _target={coord=coord, radius=radius, nshells=nshells, engaged=0, underattack=false} -function ARTY:onbeforeOpenFire(Controllable, From, Event, To, target) +function ARTY:onafterOpenFire(Controllable, From, Event, To, target) self:_EventFromTo("onafterOpenFire", Event, From, To) local _coord=target.coord --Core.Point#COORDINATE --_coord:MarkToAll("Arty Target") - + + -- Get target array index. + local id=self:_GetTargetByName(target.name) + + -- Target is now under fire and has been engaged once more. + if id then + self.targets[id].underfire=true + self.targets[id].engaged=self.targets[id].engaged+1 + self.currentTarget=target + end + + -- Start firing. self:_FireAtCoord(target.coord, target.radius, target.nshells) end +--- Before "CeaseFire" event. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #table target Array holding the target info. +-- @return #boolean +function ARTY:onbeforeCeaseFire(Controllable, From, Event, To, target) + self:_EventFromTo("onbeforeCeaseFire", Event, From, To) + + return true +end + +--- After "CeaseFire" event. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #table target Array holding the target info. +function ARTY:onafterCeaseFire(Controllable, From, Event, To, target) + self:_EventFromTo("onafterCeaseFire", Event, From, To) + + local name=self.currentTarget.name + + local id=self:_GetTargetByName(name) + + self.targets[id].underfire=false + + self.currentTarget=nil + + --Controllable:ClearTasks() + +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Go through queue of assigned tasks. +-- @param #ARTY self +function ARTY:_CheckTaskQueue() + self:F() + + -- Sort targets. + self:_SortTaskQueue() + + for i=1,#self.targets do + + local _target=self.targets[i] + + if _target.underfire==false then + + env.info(ARTY.id..string.format("Opening fire on target %s. Prio = %d, engaged = %d", _target.name, _target.prio, _target.engaged)) + + -- Call OpenFire event. + self:OpenFire(_target) + + break + end + end + +end + + +--- Sort targets with respect to priority and number of times it was already engaged. +-- @param #ARTY self +function ARTY:_SortTaskQueue() + self:F() + + -- Sort results table wrt times they have already been engaged. + local function _sort(a, b) + return (a.engaged < b.engaged) or (a.engaged==b.engaged and a.prio < b.prio) + end + table.sort(self.targets, _sort) + + -- Debug output. + env.info(ARTY.id.."Sorted targets:") + for i=1,#self.targets do + env.info(ARTY.id..string.format("Target %s. Prio = %d, engaged = %d", self.targets[i].name, self.targets[i].prio, self.targets[i].engaged)) + end +end + + +--- Get the number of shells a unit or group currently has. For a group the ammo count of all units is summed up. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE controllable +-- @return Number of shells left +function ARTY:_GetAmmo(controllable) + self:F2(controllable) + + -- Get all units. + local units=controllable:GetUnits() + + -- Init counter. + local ammo=0 + + for _,unit in pairs(units) do + + local ammotable=unit:GetAmmo() + self:T2({ammotable=ammotable}) + + local name=unit:GetName() + + if ammotable ~= nil then + + local weapons=#ammotable + self:T2(ARTY.id..string.format("Number of weapons %d.", weapons)) + + for w=1,weapons do + + local Nammo=ammotable[w]["count"] + local Tammo=ammotable[w]["desc"]["typeName"] + + -- We are specifically looking for shells here. + if string.match(Tammo, "shell") then + + -- Add up all shells + ammo=ammo+Nammo + + local text=string.format("Unit %s has %d rounds ammo of type %s (shells)", name, Nammo, Tammo) + self:T(ARTY.id..text) + MESSAGE:New(text, 10):ToAllIf(self.Debug) + else + local text=string.format("Unit %s has %d ammo of type %s", name, Nammo, Tammo) + self:T(ARTY.id..text) + MESSAGE:New(text, 10):ToAllIf(self.Debug) + end + + end + end + end + + return ammo +end + + +--- Get a target by its name. +-- @param #ARTY self +-- @param #string name Name of target. +-- @return #number Arrayindex of target. +function ARTY:_GetTargetByName(name) + self:F2(name) + + for i=1,#self.targets do + local targetname=self.targets[i].name + if targetname==name then + self:E(ARTY.id..string.format("Found target with name %s. Index = %d", name, i)) + return i + end + end + + self:E(ARTY.id..string.format("ERROR: Target with name %s could not be found!", name)) + return nil +end + + --- Print event-from-to string to DCS log file. -- @param #ARTY self -- @param #string BA Before/after info. @@ -330,8 +542,26 @@ end -- @param #string From From state. -- @param #string To To state. function ARTY:_EventFromTo(BA, Event, From, To) - local text=string.format("\n%s: %s EVENT %s: %s --> %s", BA, self.Controllable:GetName(), Event, From, To) + local text=string.format("%s: %s EVENT %s: %s --> %s", BA, self.Controllable:GetName(), Event, From, To) self:T(ARTY.id..text) end + +--- Split string. Cf http://stackoverflow.com/questions/1426954/split-string-in-lua +-- @param #ARTY self +-- @param #string str Sting to split. +-- @param #string sep Speparator for split. +-- @return #table Split text. +function ARTY:_split(str, sep) + self:F2({str=str, sep=sep}) + + local result = {} + local regex = ("([^%s]+)"):format(sep) + for each in str:gmatch(regex) do + table.insert(result, each) + end + + return result +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- \ No newline at end of file From e5268a29cf48f3dd9eec9810a8a1d53544df872e Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 30 Apr 2018 12:05:51 +0200 Subject: [PATCH 069/420] ARTY v0.3 First working version. But still WIP. --- .../Moose/Functional/Artillery.lua | 914 +++++++++++++++--- .../Moose/Wrapper/Controllable.lua | 9 +- 2 files changed, 789 insertions(+), 134 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 00c83a08a..d18a1b725 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -1,11 +1,12 @@ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- **Functional** - Control artillery units. -- --- ![Banner Image](..\Presentations\ARTILLERY\Artillery_Main.png) +-- ![Banner Image](..\Presentations\ARTY\Artillery_Main.png) -- -- ==== -- --- Make artillery fire on targets. +-- The ARTY class can be used to easily assign targets for artillery units. Multiple targets can be assigned. +-- -- -- ==== -- @@ -35,6 +36,26 @@ -- @field #string ClassName Name of the class. -- @field #boolean Debug Write Debug messages to DCS log file and send Debug messages to all players. -- @field #table targets Targets assigned. +-- @field #table currentTarget Holds the current target, if there is one assigned. +-- @field #number Nammo0 Initial amount total ammunition (shells+rockets+missiles) of the whole group. +-- @field #number Nshells0 Initial amount of shells of the whole group. +-- @field #number Nrockets0 Initial amount of rockets of the whole group. +-- @field #number Nmissiles0 Initial amount of missiles of the whole group. +-- @field Core.Scheduler#SCHEDULER TargetQueueSched Scheduler updating the target queue and calling OpenFire event. +-- @field #number TargetQueueUpdate Interval between updates of the target queue. +-- @field Core.Scheduler#SCHEDULER CheckRearmedSched Scheduler checking whether reaming of the ARTY group is complete. +-- @field #table DCSdesc DCS descriptors of the ARTY group. +-- @field #string Type Type of the ARTY group. +-- @field #number IniGroupStrength Inital number of units in the ARTY group. +-- @field #boolean IsArtillery If true, ARTY group has attribute "Artillery". +-- @field #number Speed Max speed of ARTY group. +-- @field Wrapper.Unit#UNIT RearmingUnit Unit designated to rearm the ARTY group. +-- @field #boolean report Arty group sends messages about their current state or target to its coaliton. +-- @field #table ammoshells Table holding names of the shell types which are included when counting the ammo. Default is {"weapons.shells"} which include most shells. +-- @field #table ammorockets Table holding names of the rocket types which are included when counting the ammo. Default is {"weapons.nurs"} which includes most unguided rockets. +-- @field #table ammomissiles Table holding names of the missile types which are included when counting the ammo. Default is {"weapons.missiles"} which includes some guided missiles. +-- @field #number Nshots Number of shots fired on current target. +-- @field #number WaitForShotTime Max time in seconds to wait until fist shot event occurs after target is assigned. If time is passed without shot, the target is deleted. -- @extends Core.Fsm#FSM_CONTROLLABLE -- @@ -43,7 +64,7 @@ -- -- ## Target aquisition... -- --- ![Process](..\Presentations\ART\Arty_Process.png) +-- ![Process](..\Presentations\ARTY\Artillery_Process.png) -- -- The arty process can be described as follows. -- @@ -55,41 +76,59 @@ ARTY={ Debug = true, targets = {}, currentTarget = nil, + Nammo0=0, + Nshells0=0, + Nrockets0=0, + Nmissiles0=0, + TargetQueueSched=nil, + TargetQueueUpdate=5, + CheckRearmedSched=nil, + DCSdesc=nil, + Type=nil, + IniGroupStrength=0, + IsArtillery=nil, + RearmingUnit=nil, + report=true, + ammoshells={"weapons.shells"}, + ammorockets={"weapons.nurs"}, + ammomissiles={"weapons.missiles"}, Nshots=0, + WaitForShotTime=300, } ---- Enumerator of possible rules of engagement. --- @field #list ROE -ARTY.ROE={ - Hold="Weapon Hold", - Free="Weapon Free", - Return="Return Fire", +--- Weapong type ID. http://wiki.hoggit.us/view/DCS_enum_weapon_flag +-- @list WeaponType +ARTY.WeaponType={ + Auto=1073741822, + UnguidedAny=805339120, + UnguidedCannon=805306368, + UnguidedRockets=30720, + GuidedAny=268402702, + GuidedMissile=268402688, + CruiseMissile=2097152, } ---- Enumerator of possible alarm states. --- @field #list AlarmState -ARTY.AlarmState={ - Auto="Auto", - Green="Green", - Red="Red", -} - ---- Main F10 menu for suppresion, i.e. F10/Artillery. --- @field #string MenuF10 -ARTY.MenuF10=nil - --- Some ID to identify who we are in output of the DCS.log file. -- @field #string id ARTY.id="ARTY | " --- Range script version. -- @field #number version -ARTY.version="0.1.0" +ARTY.version="0.3.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list: --- TODO: don't know yet... +-- DONE: Delete targets from queue user function. +-- TODO: Delete entire target queue user function. +-- TODO: Add weapon types. +-- DONE: Add user defined rearm weapon types. +-- TODO: Check if target is in range. Maybe this requires a data base with the ranges of all arty units. Pfff... +-- TODO: Make ARTY move to reaming position. +-- TODO: Check that right reaming vehicle is specified. Blue M818, Red Ural-375. Are there more? +-- TODO: Check if ARTY group is still alive. +-- TODO: Handle dead events. +-- TODO: Abort firing task if no shooting event occured with 5(?) minutes. Something went wrong then. Min/max range for example. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -108,13 +147,13 @@ function ARTY:New(group) if group then self:T(ARTY.id..string.format("ARTY script version %s. Added group %s.", ARTY.version, group:GetName())) else - self:E(ARTY.id.."ERROR! Requested ARTY group does not exist! (Has to be a MOOSE group.)") + self:E(ARTY.id.."ERROR: Requested ARTY group does not exist! (Has to be a MOOSE group.)") return nil end -- Check that we actually have a GROUND group. if group:IsGround()==false and group:IsShip()==false then - self:E(ARTY.id..string.format("ERROR! ARTY group %s has to be a GROUND or SHIP group!",group:GetName())) + self:E(ARTY.id..string.format("ERROR: ARTY group %s has to be a GROUND or SHIP group!", group:GetName())) return nil end @@ -126,31 +165,40 @@ function ARTY:New(group) local DCSunit=DCSgroup:getUnit(1) self.DCSdesc=DCSunit:getDesc() - -- Get max speed the group can do and convert to km/h. - --self.SpeedMax=self.DCSdesc.speedMaxOffRoad*3.6 + -- DCS descriptors. + self:T3(ARTY.id.."DCS descriptors for group "..group:GetName()) + for id,desc in pairs(self.DCSdesc) do + self:T3({id=id, desc=desc}) + end - -- Set speed to maximum. - --self.Speed=self.SpeedMax + -- Set speed to maximum in km/h. + self.Speed=self.DCSdesc.speedMax*3.6 + + -- Displayed name (similar to type name below) + self.DisplayName=self.DCSdesc.displayName -- Is this infantry or not. - self.IsInfantry=DCSunit:hasAttribute("Infantry") + self.IsArtillery=DCSunit:hasAttribute("Artillery") -- Type of group. self.Type=group:GetTypeName() -- Initial group strength. self.IniGroupStrength=#group:GetUnits() - + -- Set ROE and Alarm State. --self:SetDefaultROE("Free") --self:SetDefaultAlarmState("Auto") -- Transitions - self:AddTransition("*", "Start", "CombatReady") - self:AddTransition("CombatReady", "OpenFire", "Firing") - self:AddTransition("Firing", "CeaseFire", "CombatReady") - self:AddTransition("*", "NoAmmo", "OutOfAmmo") - self:AddTransition("*", "Dead", "*") + self:AddTransition("*", "Start", "CombatReady") + self:AddTransition("CombatReady", "OpenFire", "Firing") + self:AddTransition("Firing", "OpenFire", "Firing") -- Other target assigned + self:AddTransition("Firing", "CeaseFire", "CombatReady") + self:AddTransition("*", "Winchester", "OutOfAmmo") + self:AddTransition("OutOfAmmo", "Rearm", "Rearming") + self:AddTransition("Rearming", "Rearmed", "CombatReady") + --self:AddTransition("*", "Dead", "*") return self end @@ -159,35 +207,140 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Assign a group of target(s). +--- Add a group of target(s) for the ARTY group. -- @param #ARTY self -- @param Wrapper.Group#GROUP group Group of targets. --- @param #number radius (Optional) Radius. Default is 100 m. --- @param #number nshells (Optional) How many shells are fired on target per unit. Default 5. -- @param #number prio (Optional) Priority of target. Number between 1 (high) and 100 (low). Default 50. -function ARTY:AssignTargetGroup(group, radius, nshells, prio) - self:E({group=group, radius=radius, nshells=nshells, prio=prio}) +-- @param #number radius (Optional) Radius. Default is 100 m. +-- @param #number nshells (Optional) How many shells (or rockets) are fired on target per engagement. Default 5. +-- @param #number maxengage (Optional) How many times a target is engaged. Default 9999. +-- @param #string time Day time at which the target should be engaged. Passed as a string in format "08:13:45". Current task will be canceled. +-- @param #number weapontype Type of weapon to be used to attack this target. Default ARTY.WeaponType.Auto. +-- @return #string Name of the target. Can be used for further reference, e.g. deleting the target from the list. +-- @usage ARTY:AssignTargetGroup(GROUP:FindByName("Red Target"), 10, 250, 10, 2, "13:25:45") +function ARTY:AssignTargetGroup(group, prio, radius, nshells, maxengage, time, weapontype) + self:E({group=group, prio=prio, radius=radius, nshells=nshells, maxengage=maxengage, time=time, weapontype=weapontype}) -- Set default values. nshells=nshells or 5 radius=radius or 100 + maxengage=maxengage or 9999 + prio=prio or 50 + prio=math.max( 1, prio) + prio=math.min(100, prio) + weapontype=weapontype or ARTY.WeaponType.Auto + + -- Coordinate of target. + local coord=group:GetCoordinate() + local name=group:GetName() + + -- Name of target defined my Lat/long in Degree Minute Second format. + --local name=coord:ToStringLLDMS() + + -- Check if the name has already been used for another target. If so, the function returns a new unique name. + name=self:_CheckTargetName(name) + + -- Time in seconds. + local _time=self:_ClockToSeconds(time) + + -- Prepare target array. + local _target={name=name, coord=coord, radius=radius, nshells=nshells, engaged=0, underfire=false, prio=prio, maxengage=maxengage, time=_time, weapontype=weapontype} + + -- Add to table. + table.insert(self.targets, _target) + + -- Clock. + local _clock=self:_SecondsToClock(_target.time) + + -- Debug info. + self:T(ARTY.id..string.format("Added target %s, prio=%d, radius=%d, nshells=%d, maxengage=%d, time=%s, weapontype=%d", name, prio, radius, nshells, maxengage, _clock, weapontype)) +end + + +--- Assign coordinates of a target for the ARTY group. +-- @param #ARTY self +-- @param Wrapper.Point#COORDINATE coord Coordinates of the target. +-- @param #number prio (Optional) Priority of target. Number between 1 (high) and 100 (low). Default 50. +-- @param #number radius (Optional) Radius. Default is 100 m. +-- @param #number nshells (Optional) How many shells are fired on target per engagement. Default 5. +-- @param #number maxengage (Optional) How many times a target is engaged. Default 9999. +-- @return #string targetname Name of the target. +function ARTY:AssignTargetCoord(coord, prio, radius, nshells, maxengage) + self:E({coord=coord, prio=prio, radius=radius, nshells=nshells, maxengage=maxengage}) + + -- Set default values. + nshells=nshells or 5 + radius=radius or 100 + maxengage=maxengage or 9999 prio=prio or 50 prio=math.max( 1, prio) prio=math.min(100, prio) -- Coordinate and name. - local coord=group:GetCoordinate() - local name=group:GetName() + local name=coord:ToStringLLDMS() -- Prepare target array. - local _target={name=name, coord=coord, radius=radius, nshells=nshells, engaged=0, underfire=false, prio=prio} + local _target={name=name, coord=coord, radius=radius, nshells=nshells, engaged=0, underfire=false, prio=prio, maxengage=maxengage} -- Add to table. table.insert(self.targets, _target) -- Debug info. - env.info(ARTY.id..string.format("Added target %s, radius=%d, nshells=%d, prio=%d.", name, radius, nshells, prio)) + self:T(ARTY.id..string.format("Added target %s, radius=%d, nshells=%d, prio=%d, maxengage=%d.", name, prio, radius, nshells, maxengage)) + return name +end + +--- Assign a unit which is responsible for rearming the ARTY group. If the unit is too far away from the ARTY group it will be guided towards the ARTY group. +-- @param #ARTY self +-- @param Wrapper.Unit#UNIT unit Unit that is supposed to rearm the ARTY group. +function ARTY:SetRearmingUnit(unit) + self:F({unit=unit}) + self.RearmingUnit=unit +end + +--- Delete target from target list. +-- @param #ARTY self +-- @param #string name Name of the target. +function ARTY:RemoveTarget(name) + self:F2(name) + local id=self:_GetTargetByName(name) + if id then + table.remove(self.targets, id) + end +end + +--- Define shell types that are counted to determine the ammo amount the ARTY group has. +-- @param #ARTY self +-- @param #table tableofnames Table of shell type names. +function ARTY:SetShellTypes(tableofnames) + self:F2(tableofnames) + self.ammoshells={} + for _,_type in pairs(tableofnames) do + table.insert(self.ammoshells, _type) + end +end + +--- Define rocket types that are counted to determine the ammo amount the ARTY group has. +-- @param #ARTY self +-- @param #table tableofnames Table of rocket type names. +function ARTY:SetRocketTypes(tableofnames) + self:F2(tableofnames) + self.ammorockets={} + for _,_type in pairs(tableofnames) do + table.insert(self.ammorockets, _type) + end +end + +--- Define missile types that are counted to determine the ammo amount the ARTY group has. +-- @param #ARTY self +-- @param #table tableofnames Table of rocket type names. +function ARTY:SetMissileTypes(tableofnames) + self:F2(tableofnames) + self.ammomissiles={} + for _,_type in pairs(tableofnames) do + table.insert(self.ammomissiles, _type) + end end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -206,37 +359,52 @@ function ARTY:onafterStart(Controllable, From, Event, To) local text=string.format("Started ARTY for group %s.", Controllable:GetName()) MESSAGE:New(text, 10):ToAllIf(self.Debug) - -- Create main F10 menu if it is not there yet. - if self.MenuON then - if not ARTY.MenuF10 then - ARTY.MenuF10 = MENU_MISSION:New("ARTY") - end - self:_CreateMenuGroup() - end - -- Set the current ROE and alam state. --self:_SetAlarmState(self.DefaultAlarmState) --self:_SetROE(self.DefaultROE) - local text=string.format("\n******************************************************\n") - text=text..string.format("Arty group = %s\n", Controllable:GetName()) - text=text..string.format("Type = %s\n", self.Type) - text=text..string.format("******************************************************\n") - self:T(ARTY.id..text) - -- Get Ammo. - self:_GetAmmo(self.Controllable) + self.Nammo0, self.Nshells0, self.Nrockets0, self.Nmissiles0=self:_GetAmmo(self.Controllable) + local text=string.format("\n******************************************************\n") + text=text..string.format("Arty group = %s\n", Controllable:GetName()) + text=text..string.format("Artillery attribute = %s\n", tostring(self.IsArtillery)) + text=text..string.format("Type = %s\n", self.Type) + text=text..string.format("Number of units = %d\n", self.IniGroupStrength) + text=text..string.format("Max Speed [km/h] = %d\n", self.Speed) + text=text..string.format("Total ammo count = %d\n", self.Nammo0) + text=text..string.format("Number of shells = %d\n", self.Nshells0) + text=text..string.format("Number of rockets = %d\n", self.Nrockets0) + text=text..string.format("Number of missiles = %d\n", self.Nmissiles0) + text=text..string.format("******************************************************\n") + text=text..string.format("Targets:\n") for _, target in pairs(self.targets) do - env.info(ARTY.id..string.format("Target %s, radius=%d, nshells=%d, prio=%d.", target.name, target.radius, target.nshells, target.prio)) + local _clock=self:_SecondsToClock(target.time) + local _weapon=self:_WeaponTypeName(target.weapontype) + text=text..string.format("- %s, prio=%3d, radius=%5d, nshells=%4d, maxengage=%3d, time=%s, weapon=%s\n", target.name, target.prio, target.radius, target.nshells, target.maxengage, _clock, _weapon) end + text=text..string.format("******************************************************\n") + text=text..string.format("Shell types:\n") + for _,_type in pairs(self.ammoshells) do + text=text..string.format("- %s\n", _type) + end + text=text..string.format("Rocket types:\n") + for _,_type in pairs(self.ammorockets) do + text=text..string.format("- %s\n", _type) + end + text=text..string.format("Missile types:\n") + for _,_type in pairs(self.ammomissiles) do + text=text..string.format("- %s\n", _type) + end + text=text..string.format("******************************************************") + self:T(ARTY.id..text) -- Add event handler. self:HandleEvent(EVENTS.Shot, self._OnEventShot) self:HandleEvent(EVENTS.Dead, self._OnEventDead) -- Start scheduler to monitor task queue. - self.TaskQueueSched=SCHEDULER:New(nil, ARTY._CheckTaskQueue, {self}, 5, 10) + self.TargetQueueSched=SCHEDULER:New(nil, ARTY._TargetQueue, {self}, 5, self.TargetQueueUpdate) end @@ -256,10 +424,10 @@ function ARTY:_OnEventShot(EventData) local _weaponName = _weaponStrArray[#_weaponStrArray] -- Debug info. - self:T(ARTY.id.."EVENT SHOT: Ini unit = "..EventData.IniUnitName) - self:T(ARTY.id.."EVENT SHOT: Ini group = "..EventData.IniGroupName) - self:T(ARTY.id.."EVENT SHOT: Weapon type = ".._weapon) - self:T(ARTY.id.."EVENT SHOT: Weapon name = ".._weaponName) + self:T3(ARTY.id.."EVENT SHOT: Ini unit = "..EventData.IniUnitName) + self:T3(ARTY.id.."EVENT SHOT: Ini group = "..EventData.IniGroupName) + self:T3(ARTY.id.."EVENT SHOT: Weapon type = ".._weapon) + self:T3(ARTY.id.."EVENT SHOT: Weapon name = ".._weaponName) local group = EventData.IniGroup --Wrapper.Group#GROUP @@ -273,16 +441,70 @@ function ARTY:_OnEventShot(EventData) self.Nshots=self.Nshots+1 -- Debug output. - self:T(ARTY.id..string.format("Group %s fired shot # %d on target %s.", self.Controllable:GetName(), self.Nshots, self.currentTarget.name)) - + local text=string.format("Group %s fired shot %d of %d with weapon %s on target %s.", self.Controllable:GetName(), self.Nshots, self.currentTarget.nshells, _weaponName, self.currentTarget.name) + self:T(ARTY.id..text) + MESSAGE:New(text, 5):ToAllIf(self.Debug) + + -- Get current ammo. + local _nammo,_nshells,_nrockets,_nmissiles=self:_GetAmmo(self.Controllable) + + if _nammo==0 then + + self:E(ARTY.id.."completely out of ammo") + self.Nshots=0 + self:Winchester() + + -- Current target is deallocated ==> return + return + end + + -- Weapon type name for current target. + local _weapontype=self:_WeaponTypeName(self.currentTarget.weapontype) + self:E(ARTY.id..string.format("nammo=%d, nshells=%d, nrockets=%d, nmissiles=%d", _nammo, _nshells, _nrockets, _nmissiles)) + self:E(ARTY.id..string.format("Weapontype = %s", _weapontype)) + + -- Special weapon type requested ==> Check if corresponding ammo is empty. + if self.currentTarget.weapontype==ARTY.WeaponType.UnguidedCannon and _nshells==0 then + + self:E(ARTY.id.."cannons requested and shells empty") + self.Nshots=0 + self:CeaseFire(self.currentTarget) + return + + elseif self.currentTarget.weapontype==ARTY.WeaponType.UnguidedRockets and _nrockets==0 then + + self:E(ARTY.id.."rockets requested and rockets empty") + self.Nshots=0 + self:CeaseFire(self.currentTarget) + return + + elseif self.currentTarget.weapontype==ARTY.WeaponType.UnguidedAny and _nshells+_nrockets==0 then + + self:E(ARTY.id.."unguided weapon requested and shells+rockets empty") + self.Nshots=0 + self:CeaseFire(self.currentTarget) + return + + elseif self.currentTarget.weapontype==ARTY.WeaponType.CruiseMissile and _nmissiles==0 then + + self:E(ARTY.id.."cruise missiles requested and missiles empty") + self.Nshots=0 + self:CeaseFire(self.currentTarget) + return + end + -- Check if number of shots reached max. if self.Nshots >= self.currentTarget.nshells then - self:CeaseFire(self.currentTarget) + local text=string.format("Group %s stop firing on target %s.", self.Controllable:GetName(), self.currentTarget.name) + self:T(ARTY.id..text) + MESSAGE:New(text, 5):ToAllIf(self.Debug) + self.Nshots=0 + self:CeaseFire(self.currentTarget) end else - self:T(ARTY.id..string.format("No current target?!")) + self:E(ARTY.id..string.format("ERROR: No current target?!")) end end end @@ -300,29 +522,25 @@ end -- @param Core.Point#COORDINATE coord Coordinates to fire upon. -- @param #number radius Radius around coordinate. -- @param #number nshells Number of shells to fire. -function ARTY:_FireAtCoord(coord, radius, nshells) +-- @param #number weapontype Type of weapon to use. +function ARTY:_FireAtCoord(coord, radius, nshells, weapontype) self:E({coord=coord, radius=radius, nshells=nshells}) -- Controllable. local group=self.Controllable --Wrapper.Controllable#CONTROLLABLE - -- Number of units. - local units=group:GetUnits() - local nunits=#units - - local nshells_tot=nshells*nunits - -- Set ROE to weapon free. - group:OptionROEWeaponFree() + group:OptionROEOpenFire() -- Get Vec2 local vec2=coord:GetVec2() -- Get task. - local fire=group:TaskFireAtPoint(vec2, radius, nshells_tot) + local fire=group:TaskFireAtPoint(vec2, radius, nshells, weapontype) -- Execute task. group:SetTask(fire) + --group:PushTask(fire) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -340,11 +558,34 @@ end function ARTY:onbeforeOpenFire(Controllable, From, Event, To, target) self:_EventFromTo("onbeforeOpenFire", Event, From, To) - if self.currentTarget then - self:T(ARTY.id..string.format("Group %s already has a target %s.", self.Controllable:GetName(), target.name)) - return false + + -- If this target has an attack time and it's prio is higher than the current task, we allow the transition. + if target.time~=nil and self.currentTarget~=nil and self.currentTarget.prio > target.prio then + -- Debug info. + self:T(ARTY.id..string.format("Group %s current target %s has lower prio than new target %s with attack time.", self.Controllable:GetName(), self.currentTarget.name, target.name)) + + -- Reset current task. + --self.Controllable:ClearTasks() + + -- Set number of shots counter to zero. + self.Nshots=0 + + -- Stop firing on current target. + self:CeaseFire(self.currentTarget) + + -- Alow transition to onafterOpenfire. + return true end + -- Check that group has no current target already. + if self.currentTarget then + -- Debug info. + self:T(ARTY.id..string.format("Group %s already has a target %s.", self.Controllable:GetName(), self.currentTarget.name)) + + -- Deny transition. + return false + end + return true end @@ -367,16 +608,34 @@ function ARTY:onafterOpenFire(Controllable, From, Event, To, target) -- Target is now under fire and has been engaged once more. if id then + -- Set under fire flag. self.targets[id].underfire=true + -- Increase engaged counter self.targets[id].engaged=self.targets[id].engaged+1 + -- Clear the attack time. + self.targets[id].time=nil + -- Set current target. self.currentTarget=target end + -- Distance to target + local range=Controllable:GetCoordinate():Get2DDistance(target.coord) + + -- Send message. + local text=string.format("%s, opening fire on target %s with %s shells. Distance %.1f km.", Controllable:GetName(), target.name, target.nshells, range/1000) + self:T(ARTY.id..text) + MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report) + -- Start firing. - self:_FireAtCoord(target.coord, target.radius, target.nshells) + self:_FireAtCoord(target.coord, target.radius, target.nshells, target.weapontype) + + -- Check that after a certain time a shot event occured. + --self.CheckShootingSched, self.CheckRearmedSchedID=SCHEDULER:New(nil, self._CheckShootingStarted, {self}, self.WaitForShotTime) end +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + --- Before "CeaseFire" event. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. @@ -400,42 +659,195 @@ end -- @param #table target Array holding the target info. function ARTY:onafterCeaseFire(Controllable, From, Event, To, target) self:_EventFromTo("onafterCeaseFire", Event, From, To) + + -- Send message. + local text=string.format("%s, ceasing fire on target %s.", Controllable:GetName(), target.name) + self:T(ARTY.id..text) + MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report) - local name=self.currentTarget.name + -- ARTY group has no current target any more. + self.currentTarget=nil - local id=self:_GetTargetByName(name) + -- Get target array index. + local id=self:_GetTargetByName(target.name) + -- Target is not under fire any more. self.targets[id].underfire=false - self.currentTarget=nil - - --Controllable:ClearTasks() + -- If number of engagements has been reached, the target is removed. + if target.engaged >= target.maxengage then + self:RemoveTarget(target.name) + end + +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- After "Winchester" event. Group is out of ammo. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function ARTY:onafterWinchester(Controllable, From, Event, To) + self:_EventFromTo("onafterWinchester", Event, From, To) + + local text=string.format("Group %s is winchester (out of ammo)!", Controllable:GetName()) + self:T(ARTY.id..text) + MESSAGE:New(text, 10):ToAllIf(self.Debug) + + -- Send message. + local text=string.format("%s, winchester.", Controllable:GetName()) + self:T(ARTY.id..text) + MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report) + + -- Remove current target. + if self.currentTarget then + local id=self:_GetTargetByName(self.currentTarget.name) + self.targets[id].underfire=false + self.currentTarget=nil + end + + -- Init rearming. + self:Rearm() end +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Before "Rearm" event. Check if a unit to rearm the ARTY group has been defined. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function ARTY:onbeforeRearm(Controllable, From, Event, To) + self:_EventFromTo("onbeforeRearm", Event, From, To) + + if self.RearmingUnit and self.RearmingUnit:IsAlive() then + return true + else + return false + end + +end + + +--- After "Rearm" event. Send message if reporting is on. Route rearming unit to ARTY group. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function ARTY:onafterRearm(Controllable, From, Event, To) + self:_EventFromTo("onafterRearm", Event, From, To) + + -- Send message. + local text=string.format("%s, %s, request rearming.", Controllable:GetName(), self.RearmingUnit:GetName()) + self:T(ARTY.id..text) + MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report) + + -- Random point 20-100 m away from unit. + local coord=self.Controllable:GetCoordinate() + local vec2=coord:GetRandomVec2InRadius(20, 100) + local pops=COORDINATE:NewFromVec2(vec2) + + -- Route unit to ARTY group. + self.RearmingUnit:RouteGroundOnRoad(pops, 50, 5) + + -- Start scheduler to monitor ammo count until rearming is complete. + self.CheckRearmedSched=SCHEDULER:New(nil,self._CheckRearmed, {self}, 5, 10) +end + + +--- Check if ARTY group is reamed. +-- @param #ARTY self +function ARTY:_CheckRearmed() + self:F2() + + -- Get current ammo. + local nammo,nshells,nrockets,nmissiles=self:_GetAmmo(self.Controllable) + + -- Rearming --> Rearmed --> CombatReady + if nammo==self.Nammo0 then + self:Rearmed() + end + +end + +--- After "Rearmed" event. Send message if reporting is on and stop the scheduler. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function ARTY:onafterRearmed(Controllable, From, Event, To) + self:_EventFromTo("onafterRearmed", Event, From, To) + + -- Send message. + local text=string.format("%s, rearming complete.", Controllable:GetName()) + self:T(ARTY.id..text) + MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report) + + -- Stop scheduler. + self.CheckRearmedSched:Stop() +end + + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Go through queue of assigned tasks. -- @param #ARTY self -function ARTY:_CheckTaskQueue() - self:F() +function ARTY:_TargetQueue() + self:F2() + + -- Debug info + self:T(ARTY.id..string.format("Group %s, number of targets = %d", self.Controllable:GetName(), #self.targets)) - -- Sort targets. - self:_SortTaskQueue() + -- We already have a target. +-- if self.currentTarget then +-- self:T(ARTY.id..string.format("Group %s already has a target %s.", self.Controllable:GetName(), self.currentTarget.name)) +-- return +-- end + + -- First check if there is a target with a certain time for attack. + for i=1,#self.targets do + local _target=self.targets[i] + if _target and _target.time then + if timer.getAbsTime() >= _target.time and _target.underfire==false then + + -- Clock time format. + local _clock=self:_SecondsToClock(_target.time) + local _Cnow=self:_SecondsToClock(timer.getAbsTime()) + -- Debug info. + self:T(ARTY.id..string.format("Engaging timed target %s. Prio=%d, engaged=%d, time=%s, tnow=%s",_target.name,_target.prio,_target.engaged,_clock,_Cnow)) + + -- Call OpenFire event. + self:OpenFire(_target) + + end + end + end + + -- Sort targets w.r.t. prio and number times engaged already. + self:_SortTargetQueuePrio() + + -- Loop over all sorted targets. for i=1,#self.targets do local _target=self.targets[i] - if _target.underfire==false then - - env.info(ARTY.id..string.format("Opening fire on target %s. Prio = %d, engaged = %d", _target.name, _target.prio, _target.engaged)) + if _target.underfire==false and _target.time==nil and _target.maxengage > _target.engaged then + -- Debug info. + self:T(ARTY.id..string.format("Engaging target %s. Prio = %d, engaged = %d", _target.name, _target.prio, _target.engaged)) + -- Call OpenFire event. self:OpenFire(_target) - + break end end @@ -445,8 +857,8 @@ end --- Sort targets with respect to priority and number of times it was already engaged. -- @param #ARTY self -function ARTY:_SortTaskQueue() - self:F() +function ARTY:_SortTargetQueuePrio() + self:F2() -- Sort results table wrt times they have already been engaged. local function _sort(a, b) @@ -455,17 +867,44 @@ function ARTY:_SortTaskQueue() table.sort(self.targets, _sort) -- Debug output. - env.info(ARTY.id.."Sorted targets:") + self:T2(ARTY.id.."Sorted targets wrt prio and number of engagements:") for i=1,#self.targets do - env.info(ARTY.id..string.format("Target %s. Prio = %d, engaged = %d", self.targets[i].name, self.targets[i].prio, self.targets[i].engaged)) + self:T2(ARTY.id..string.format("Target %s, prio=%d, engaged=%d", self.targets[i].name, self.targets[i].prio, self.targets[i].engaged)) end end +--- Sort targets with respect to engage time. +-- @param #ARTY self +function ARTY:_SortTargetQueueTime() + self:F2() + + -- Sort targets w.r.t attack time. + local function _sort(a, b) + if a.time == nil and b.time == nil then + return false + end + if a.time == nil then + return false + end + if b.time == nil then + return true + end + return a.time < b.time + end + table.sort(self.targets, _sort) + + -- Debug output. + self:T2(ARTY.id.."Sorted targets wrt time:") + for i=1,#self.targets do + self:T(ARTY.id..string.format("Target %s, prio=%d, engaged=%d", self.targets[i].name, self.targets[i].prio, self.targets[i].engaged)) + end + +end --- Get the number of shells a unit or group currently has. For a group the ammo count of all units is summed up. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE controllable --- @return Number of shells left +-- @return Number of ALL shells left from the whole group. function ARTY:_GetAmmo(controllable) self:F2(controllable) @@ -473,48 +912,136 @@ function ARTY:_GetAmmo(controllable) local units=controllable:GetUnits() -- Init counter. - local ammo=0 + local nammo=0 + local nshells=0 + local nrockets=0 + local nmissiles=0 for _,unit in pairs(units) do - local ammotable=unit:GetAmmo() - self:T2({ammotable=ammotable}) - - local name=unit:GetName() - - if ammotable ~= nil then - - local weapons=#ammotable - self:T2(ARTY.id..string.format("Number of weapons %d.", weapons)) + if unit and unit:IsAlive() then + + local ammotable=unit:GetAmmo() + self:T({ammotable=ammotable}) - for w=1,weapons do + local name=unit:GetName() - local Nammo=ammotable[w]["count"] - local Tammo=ammotable[w]["desc"]["typeName"] + if ammotable ~= nil then + + local weapons=#ammotable - -- We are specifically looking for shells here. - if string.match(Tammo, "shell") then + self:T2(ARTY.id..string.format("Number of weapons %d.", weapons)) + self:T2(ammotable) - -- Add up all shells - ammo=ammo+Nammo + -- Loop over all weapons. + for w=1,weapons do - local text=string.format("Unit %s has %d rounds ammo of type %s (shells)", name, Nammo, Tammo) - self:T(ARTY.id..text) - MESSAGE:New(text, 10):ToAllIf(self.Debug) - else - local text=string.format("Unit %s has %d ammo of type %s", name, Nammo, Tammo) - self:T(ARTY.id..text) - MESSAGE:New(text, 10):ToAllIf(self.Debug) + -- Number of current weapon. + local Nammo=ammotable[w]["count"] + + -- Typename of current weapon + local Tammo=ammotable[w]["desc"]["typeName"] + + -- Check for correct shell type. + local _gotshell=false + for _,_type in pairs(self.ammoshells) do + if string.match(Tammo, _type) then + _gotshell=true + end + end + + -- Check for correct rocket type. + local _gotrocket=false + for _,_type in pairs(self.ammorockets) do + if string.match(Tammo, _type) then + _gotrocket=true + end + end + + -- Check for correct missile type. + local _gotmissile=false + for _,_type in pairs(self.ammomissiles) do + if string.match(Tammo,_type) then + _gotmissile=true + end + end + + + -- We are specifically looking for shells or rockets here. + if _gotshell then + + -- Add up all shells. + nshells=nshells+Nammo + + -- Debug info. + local text=string.format("Unit %s has %d shells of type %s", name, Nammo, Tammo) + self:T2(ARTY.id..text) + MESSAGE:New(text, 10):ToAllIf(self.Debug and not self.report) + + elseif _gotrocket then + + -- Add up all rockets. + nrockets=nrockets+Nammo + + -- Debug info. + local text=string.format("Unit %s has %d rockets of type %s", name, Nammo, Tammo) + self:T2(ARTY.id..text) + MESSAGE:New(text, 10):ToAllIf(self.Debug and not self.report) + + elseif _gotmissile then + + -- Add up all rockets. + nmissiles=nmissiles+Nammo + + -- Debug info. + local text=string.format("Unit %s has %d missiles of type %s", name, Nammo, Tammo) + self:T2(ARTY.id..text) + MESSAGE:New(text, 10):ToAllIf(self.Debug and not self.report) + + else + + -- Debug info. + local text=string.format("Unit %s has %d ammo of type %s", name, Nammo, Tammo) + self:T2(ARTY.id..text) + MESSAGE:New(text, 10):ToAllIf(self.Debug and not self.report) + + end + end - end end end - return ammo + -- Total amount of ammunition. + nammo=nshells+nrockets+nmissiles + + return nammo, nshells, nrockets, nmissiles end +--- Check whether shooting started within a certain time (~5 min). If not, the current target is considered invalid and removed from the target list. +-- @param #ARTY self +function ARTY:_CheckShootingStarted() + self:F2() + + if self.currentTarget and self.Nshots==0 then + + -- Get name and id of target. + local name=self.currentTarget.name + local id=self:_GetTargetByName(name) + + -- Debug info. + self:T(ARTY.id..string.format("No shot event after %d seconds. Removing current target %s from list.", self.WaitForShotTime, name)) + + -- CeaseFire. + self:CeaseFire(self.currentTarget) + + -- Remove target from list. + self:RemoveTarget(name) + + end +end + --- Get a target by its name. -- @param #ARTY self -- @param #string name Name of target. @@ -525,7 +1052,7 @@ function ARTY:_GetTargetByName(name) for i=1,#self.targets do local targetname=self.targets[i].name if targetname==name then - self:E(ARTY.id..string.format("Found target with name %s. Index = %d", name, i)) + self:T2(ARTY.id..string.format("Found target with name %s. Index = %d", name, i)) return i end end @@ -535,6 +1062,70 @@ function ARTY:_GetTargetByName(name) end +--- Get the weapon type name, which should be used to attack the target. +-- @param #ARTY self +-- @param #string name Desired target name. +-- @return #string Unique name, which is not already given for another target. +function ARTY:_CheckTargetName(name) + self:F2(name) + + local newname=name + local counter=1 + + repeat + -- We assume the name is unique. + local unique=true + + -- Loop over all targets already defined. + for _,_target in pairs(self.targets) do + + -- Target name. + local _targetname=_target.name + + if _targetname==newname then + -- Define new name = "name #01" + newname=string.format("%s #%02d", name, counter) + + -- Increase counter. + counter=counter+1 + + -- Name is already used for another target ==> try again with new name. + unique=false + end + end + + until (unique) + + -- Debug output and return new name. + self:T(string.format("Original name %s, new name = %s", name, newname)) + return newname +end + +--- Get the weapon type name, which should be used to attack the target. +-- @param #ARTY self +-- @param #number tnumber Number of weapon type ARTY.WeaponType.XXX +-- @return #number tnumber of weapon type. +function ARTY:_WeaponTypeName(tnumber) + local name="unknown" + if tnumber==ARTY.WeaponType.Auto then + name="Auto (Cannon, Rockets, Missiles)" + elseif tnumber==ARTY.WeaponType.CruiseMissile then + name="Cruise Missile" + elseif tnumber==ARTY.WeaponType.GuidedAny then + name="Any Guided Missile" + elseif tnumber==ARTY.WeaponType.GuidedMissile then + name="Guided Missile" + elseif tnumber==ARTY.WeaponType.UnguidedAny then + name="Any Unguided Weapon (Cannon or Rockets)" + elseif tnumber==ARTY.WeaponType.UnguidedCannon then + name="Unguided Cannon" + elseif tnumber==ARTY.WeaponType.UnguidedRockets then + name="Unguided Rockets" + end + + return name +end + --- Print event-from-to string to DCS log file. -- @param #ARTY self -- @param #string BA Before/after info. @@ -543,25 +1134,84 @@ end -- @param #string To To state. function ARTY:_EventFromTo(BA, Event, From, To) local text=string.format("%s: %s EVENT %s: %s --> %s", BA, self.Controllable:GetName(), Event, From, To) - self:T(ARTY.id..text) + self:T3(ARTY.id..text) end ---- Split string. Cf http://stackoverflow.com/questions/1426954/split-string-in-lua +--- Split string. C.f. http://stackoverflow.com/questions/1426954/split-string-in-lua -- @param #ARTY self -- @param #string str Sting to split. -- @param #string sep Speparator for split. -- @return #table Split text. function ARTY:_split(str, sep) - self:F2({str=str, sep=sep}) + self:F3({str=str, sep=sep}) local result = {} local regex = ("([^%s]+)"):format(sep) for each in str:gmatch(regex) do - table.insert(result, each) + table.insert(result, each) end return result end +--- Convert time in seconds to hours, minutes and seconds. +-- @param #ARTY self +-- @param #number seconds Time in seconds. +-- @return #string Time in format Hours:minutes:seconds. +function ARTY:_SecondsToClock(seconds) + self:F3({seconds=seconds}) + + -- Seconds + local seconds = tonumber(seconds) + + if seconds==nil then + return "00:00:00" + end + + if seconds <= 0 then + return "00:00:00" + else + local hours = string.format("%02.f", math.floor(seconds/3600)) + local mins = string.format("%02.f", math.floor(seconds/60 - (hours*60))) + local secs = string.format("%02.f", math.floor(seconds - hours*3600 - mins *60)) + return hours..":"..mins..":"..secs + --return hours, mins, secs + end +end + +--- Convert clock time from hours, minutes and seconds to seconds. +-- @param #ARTY self +-- @param #string clock String of clock time. E.g., "06:12:35". +function ARTY:_ClockToSeconds(clock) + self:F3({clock=clock}) + + if clock==nil then + return nil + end + + -- Split string by ":" + local tsplit=string.gmatch(clock, '([^:]+)') + + -- Get time in seconds + local seconds=0 + local i=1 + for time in tsplit do + if i==1 then + -- Hours + seconds=seconds+tonumber(time)*60*60 + elseif i==2 then + -- Minutes + seconds=seconds+tonumber(time)*60 + elseif i==3 then + -- Seconds + seconds=seconds+tonumber(time) + end + i=i+1 + end + + self:T3(ARTY.id..string.format("Clock %s = %d seconds", clock, seconds)) + return seconds +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- \ No newline at end of file diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 0b68255c5..92d258b5f 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1048,9 +1048,10 @@ end -- @param Dcs.DCSTypes#Vec2 Vec2 The point to fire at. -- @param Dcs.DCSTypes#Distance Radius The radius of the zone to deploy the fire at. -- @param #number AmmoCount (optional) Quantity of ammunition to expand (omit to fire until ammunition is depleted). +-- @param #number WeaponType (optional) Enum for weapon type ID. This value is only required if you want the group firing to use a specific weapon, for instance using the task on a ship to force it to fire guided missiles at targets within cannon range. See http://wiki.hoggit.us/view/DCS_enum_weapon_flag -- @return Dcs.DCSTasking.Task#Task The DCS task structure. -function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount ) - self:F2( { self.ControllableName, Vec2, Radius, AmmoCount } ) +function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType ) + self:F2( { self.ControllableName, Vec2, Radius, AmmoCount, WeaponType } ) -- FireAtPoint = { -- id = 'FireAtPoint', @@ -1076,6 +1077,10 @@ function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount ) DCSTask.params.expendQty = AmmoCount DCSTask.params.expendQtyEnabled = true end + + if WeaponType then + DCSTask.params.weaponType=WeaponType + end self:T3( { DCSTask } ) return DCSTask From 394b5f5b89994732b04ba16565d120de4a98ecaf Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 30 Apr 2018 22:53:57 +0200 Subject: [PATCH 070/420] ARTY v0.4.0 Improved assigned time for engagement. Next day should be possible now. Added check whether a group started firing within a certain time. --- .../Moose/Functional/Artillery.lua | 169 ++++++++++-------- 1 file changed, 95 insertions(+), 74 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index d18a1b725..01d909b1b 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -55,7 +55,7 @@ -- @field #table ammorockets Table holding names of the rocket types which are included when counting the ammo. Default is {"weapons.nurs"} which includes most unguided rockets. -- @field #table ammomissiles Table holding names of the missile types which are included when counting the ammo. Default is {"weapons.missiles"} which includes some guided missiles. -- @field #number Nshots Number of shots fired on current target. --- @field #number WaitForShotTime Max time in seconds to wait until fist shot event occurs after target is assigned. If time is passed without shot, the target is deleted. +-- @field #number WaitForShotTime Max time in seconds to wait until fist shot event occurs after target is assigned. If time is passed without shot, the target is deleted. Default is 300 seconds. -- @extends Core.Fsm#FSM_CONTROLLABLE -- @@ -114,7 +114,7 @@ ARTY.id="ARTY | " --- Range script version. -- @field #number version -ARTY.version="0.3.0" +ARTY.version="0.4.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -128,7 +128,8 @@ ARTY.version="0.3.0" -- TODO: Check that right reaming vehicle is specified. Blue M818, Red Ural-375. Are there more? -- TODO: Check if ARTY group is still alive. -- TODO: Handle dead events. --- TODO: Abort firing task if no shooting event occured with 5(?) minutes. Something went wrong then. Min/max range for example. +-- DONE: Abort firing task if no shooting event occured with 5(?) minutes. Something went wrong then. Min/max range for example. +-- DONE: Improve assigned time for engagement. Next day? ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -185,10 +186,6 @@ function ARTY:New(group) -- Initial group strength. self.IniGroupStrength=#group:GetUnits() - - -- Set ROE and Alarm State. - --self:SetDefaultROE("Free") - --self:SetDefaultAlarmState("Auto") -- Transitions self:AddTransition("*", "Start", "CombatReady") @@ -213,7 +210,7 @@ end -- @param #number prio (Optional) Priority of target. Number between 1 (high) and 100 (low). Default 50. -- @param #number radius (Optional) Radius. Default is 100 m. -- @param #number nshells (Optional) How many shells (or rockets) are fired on target per engagement. Default 5. --- @param #number maxengage (Optional) How many times a target is engaged. Default 9999. +-- @param #number maxengage (Optional) How many times a target is engaged. Default 1. -- @param #string time Day time at which the target should be engaged. Passed as a string in format "08:13:45". Current task will be canceled. -- @param #number weapontype Type of weapon to be used to attack this target. Default ARTY.WeaponType.Auto. -- @return #string Name of the target. Can be used for further reference, e.g. deleting the target from the list. @@ -224,7 +221,7 @@ function ARTY:AssignTargetGroup(group, prio, radius, nshells, maxengage, time, w -- Set default values. nshells=nshells or 5 radius=radius or 100 - maxengage=maxengage or 9999 + maxengage=maxengage or 1 prio=prio or 50 prio=math.max( 1, prio) prio=math.min(100, prio) @@ -253,7 +250,7 @@ function ARTY:AssignTargetGroup(group, prio, radius, nshells, maxengage, time, w local _clock=self:_SecondsToClock(_target.time) -- Debug info. - self:T(ARTY.id..string.format("Added target %s, prio=%d, radius=%d, nshells=%d, maxengage=%d, time=%s, weapontype=%d", name, prio, radius, nshells, maxengage, _clock, weapontype)) + self:T(ARTY.id..string.format("Added target %s, prio=%d, radius=%d, nshells=%d, maxengage=%d, time=%s, weapontype=%d", name, prio, radius, nshells, maxengage, tostring(_clock), weapontype)) end @@ -381,7 +378,7 @@ function ARTY:onafterStart(Controllable, From, Event, To) for _, target in pairs(self.targets) do local _clock=self:_SecondsToClock(target.time) local _weapon=self:_WeaponTypeName(target.weapontype) - text=text..string.format("- %s, prio=%3d, radius=%5d, nshells=%4d, maxengage=%3d, time=%s, weapon=%s\n", target.name, target.prio, target.radius, target.nshells, target.maxengage, _clock, _weapon) + text=text..string.format("- %s, prio=%3d, radius=%5d, nshells=%4d, maxengage=%3d, time=%11s, weapon=%s\n", target.name, target.prio, target.radius, target.nshells, target.maxengage, tostring(_clock), _weapon) end text=text..string.format("******************************************************\n") text=text..string.format("Shell types:\n") @@ -406,6 +403,9 @@ function ARTY:onafterStart(Controllable, From, Event, To) -- Start scheduler to monitor task queue. self.TargetQueueSched=SCHEDULER:New(nil, ARTY._TargetQueue, {self}, 5, self.TargetQueueUpdate) + -- Start scheduler to monitor if ARTY group started firing within a certain time. + self.CheckShootingSched=SCHEDULER:New(nil, ARTY._CheckShootingStarted, {self}, 60, 60) + end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -450,8 +450,7 @@ function ARTY:_OnEventShot(EventData) if _nammo==0 then - self:E(ARTY.id.."completely out of ammo") - self.Nshots=0 + self:E(ARTY.id..string.format("Group %s completely out of ammo.", self.Controllable:GetName())) self:Winchester() -- Current target is deallocated ==> return @@ -466,29 +465,25 @@ function ARTY:_OnEventShot(EventData) -- Special weapon type requested ==> Check if corresponding ammo is empty. if self.currentTarget.weapontype==ARTY.WeaponType.UnguidedCannon and _nshells==0 then - self:E(ARTY.id.."cannons requested and shells empty") - self.Nshots=0 + self:T(ARTY.id.."Cannons requested and shells empty.") self:CeaseFire(self.currentTarget) return elseif self.currentTarget.weapontype==ARTY.WeaponType.UnguidedRockets and _nrockets==0 then - self:E(ARTY.id.."rockets requested and rockets empty") - self.Nshots=0 + self:T(ARTY.id.."Rockets requested and rockets empty.") self:CeaseFire(self.currentTarget) return elseif self.currentTarget.weapontype==ARTY.WeaponType.UnguidedAny and _nshells+_nrockets==0 then - self:E(ARTY.id.."unguided weapon requested and shells+rockets empty") - self.Nshots=0 + self:T(ARTY.id.."Unguided weapon requested and shells+rockets empty.") self:CeaseFire(self.currentTarget) return elseif self.currentTarget.weapontype==ARTY.WeaponType.CruiseMissile and _nmissiles==0 then - self:E(ARTY.id.."cruise missiles requested and missiles empty") - self.Nshots=0 + self:E(ARTY.id.."Cruise missiles requested and missiles empty.") self:CeaseFire(self.currentTarget) return end @@ -499,7 +494,7 @@ function ARTY:_OnEventShot(EventData) self:T(ARTY.id..text) MESSAGE:New(text, 5):ToAllIf(self.Debug) - self.Nshots=0 + -- Cease fire. self:CeaseFire(self.currentTarget) end @@ -616,6 +611,8 @@ function ARTY:onafterOpenFire(Controllable, From, Event, To, target) self.targets[id].time=nil -- Set current target. self.currentTarget=target + -- Set time the target was assigned. + self.currentTarget.Tassigned=timer.getTime() end -- Distance to target @@ -629,9 +626,6 @@ function ARTY:onafterOpenFire(Controllable, From, Event, To, target) -- Start firing. self:_FireAtCoord(target.coord, target.radius, target.nshells, target.weapontype) - -- Check that after a certain time a shot event occured. - --self.CheckShootingSched, self.CheckRearmedSchedID=SCHEDULER:New(nil, self._CheckShootingStarted, {self}, self.WaitForShotTime) - end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -665,9 +659,9 @@ function ARTY:onafterCeaseFire(Controllable, From, Event, To, target) self:T(ARTY.id..text) MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report) - -- ARTY group has no current target any more. - self.currentTarget=nil - + -- Set number of shots to zero. + self.Nshots=0 + -- Get target array index. local id=self:_GetTargetByName(target.name) @@ -678,7 +672,10 @@ function ARTY:onafterCeaseFire(Controllable, From, Event, To, target) if target.engaged >= target.maxengage then self:RemoveTarget(target.name) end - + + -- ARTY group has no current target any more. + self.currentTarget=nil + end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -692,23 +689,15 @@ end function ARTY:onafterWinchester(Controllable, From, Event, To) self:_EventFromTo("onafterWinchester", Event, From, To) - local text=string.format("Group %s is winchester (out of ammo)!", Controllable:GetName()) - self:T(ARTY.id..text) - MESSAGE:New(text, 10):ToAllIf(self.Debug) - -- Send message. local text=string.format("%s, winchester.", Controllable:GetName()) self:T(ARTY.id..text) - MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report) + MESSAGE:New(text, 30):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) - -- Remove current target. - if self.currentTarget then - local id=self:_GetTargetByName(self.currentTarget.name) - self.targets[id].underfire=false - self.currentTarget=nil - end - - -- Init rearming. + -- Cease fire first. + self:CeaseFire(self.currentTarget) + + -- Init rearming if possible. self:Rearm() end @@ -745,7 +734,7 @@ function ARTY:onafterRearm(Controllable, From, Event, To) -- Send message. local text=string.format("%s, %s, request rearming.", Controllable:GetName(), self.RearmingUnit:GetName()) self:T(ARTY.id..text) - MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report) + MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) -- Random point 20-100 m away from unit. local coord=self.Controllable:GetCoordinate() @@ -756,7 +745,7 @@ function ARTY:onafterRearm(Controllable, From, Event, To) self.RearmingUnit:RouteGroundOnRoad(pops, 50, 5) -- Start scheduler to monitor ammo count until rearming is complete. - self.CheckRearmedSched=SCHEDULER:New(nil,self._CheckRearmed, {self}, 5, 10) + self.CheckRearmedSched=SCHEDULER:New(nil,self._CheckRearmed, {self}, 20, 20) end @@ -768,6 +757,14 @@ function ARTY:_CheckRearmed() -- Get current ammo. local nammo,nshells,nrockets,nmissiles=self:_GetAmmo(self.Controllable) + -- Rearming status in per cent. + local _rearmpc=nammo/self.Nammo0*100 + + -- Send message. + local text=string.format("%s, rearming %d %% complete.", self.Controllable:GetName(), _rearmpc) + self:T(ARTY.id..text) + MESSAGE:New(text, 10):ToCoalitionIf(self.Controllable:GetCoalition(), self.report or self.Debug) + -- Rearming --> Rearmed --> CombatReady if nammo==self.Nammo0 then self:Rearmed() @@ -806,11 +803,11 @@ function ARTY:_TargetQueue() -- Debug info self:T(ARTY.id..string.format("Group %s, number of targets = %d", self.Controllable:GetName(), #self.targets)) - -- We already have a target. --- if self.currentTarget then --- self:T(ARTY.id..string.format("Group %s already has a target %s.", self.Controllable:GetName(), self.currentTarget.name)) --- return --- end + -- No targets assigned at the moment. + if #self.targets==0 then + self:T(ARTY.id..string.format("Group %s, no targets assigned at the moment. No need for _TargetQueue.", self.Controllable:GetName())) + return + end -- First check if there is a target with a certain time for attack. for i=1,#self.targets do @@ -851,7 +848,7 @@ function ARTY:_TargetQueue() break end end - + end @@ -896,7 +893,7 @@ function ARTY:_SortTargetQueueTime() -- Debug output. self:T2(ARTY.id.."Sorted targets wrt time:") for i=1,#self.targets do - self:T(ARTY.id..string.format("Target %s, prio=%d, engaged=%d", self.targets[i].name, self.targets[i].prio, self.targets[i].engaged)) + self:T2(ARTY.id..string.format("Target %s, prio=%d, engaged=%d", self.targets[i].name, self.targets[i].prio, self.targets[i].engaged)) end end @@ -1024,21 +1021,30 @@ end function ARTY:_CheckShootingStarted() self:F2() - if self.currentTarget and self.Nshots==0 then + if self.currentTarget then + + -- Current time. + local Tnow=timer.getTime() - -- Get name and id of target. - local name=self.currentTarget.name - local id=self:_GetTargetByName(name) + -- Time that passed after current target has been assigned. + local dt=Tnow-self.currentTarget.Tassigned - -- Debug info. - self:T(ARTY.id..string.format("No shot event after %d seconds. Removing current target %s from list.", self.WaitForShotTime, name)) + + if dt > self.WaitForShotTime and self.Nshots==0 then - -- CeaseFire. - self:CeaseFire(self.currentTarget) + -- Get name and id of target. + local name=self.currentTarget.name + + -- Debug info. + self:T(ARTY.id..string.format("%s, no shot event after %d seconds. Removing current target %s from list.", self.Controllable:GetName(), self.WaitForShotTime, name)) - -- Remove target from list. - self:RemoveTarget(name) + -- CeaseFire. + self:CeaseFire(self.currentTarget) + + -- Remove target from list. + self:RemoveTarget(name) + end end end @@ -1161,21 +1167,26 @@ end -- @return #string Time in format Hours:minutes:seconds. function ARTY:_SecondsToClock(seconds) self:F3({seconds=seconds}) - + + if seconds==nil then + return nil + --return "00:00:00" + end + -- Seconds local seconds = tonumber(seconds) - if seconds==nil then - return "00:00:00" - end + -- Seconds of this day. + local _seconds=seconds%(60*60*24) if seconds <= 0 then return "00:00:00" else - local hours = string.format("%02.f", math.floor(seconds/3600)) - local mins = string.format("%02.f", math.floor(seconds/60 - (hours*60))) - local secs = string.format("%02.f", math.floor(seconds - hours*3600 - mins *60)) - return hours..":"..mins..":"..secs + local hours = string.format("%02.f", math.floor(_seconds/3600)) + local mins = string.format("%02.f", math.floor(_seconds/60 - (hours*60))) + local secs = string.format("%02.f", math.floor(_seconds - hours*3600 - mins *60)) + local days = string.format("%d", seconds/(60*60*24)) + return hours..":"..mins..":"..secs.."+"..days --return hours, mins, secs end end @@ -1190,13 +1201,23 @@ function ARTY:_ClockToSeconds(clock) return nil end - -- Split string by ":" - local tsplit=string.gmatch(clock, '([^:]+)') - - -- Get time in seconds + -- Seconds init. local seconds=0 + + -- Split additional days. + local dsplit=self:_split(clock, "+") + + -- Convert days to seconds. + if #dsplit>1 then + seconds=seconds+tonumber(dsplit[2])*60*60*24 + end + + -- Split hours, minutes, seconds + local tsplit=self:_split(dsplit[1], ":") + + -- Get time in seconds local i=1 - for time in tsplit do + for _,time in ipairs(tsplit) do if i==1 then -- Hours seconds=seconds+tonumber(time)*60*60 From 274b44459ee0df7a63b2f56725f89f26a513298f Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 2 May 2018 00:14:05 +0200 Subject: [PATCH 071/420] ARTY v0.5 - Adjusted schedulers. - Improved transitions. - Make rearming unit go back to its original position. - Added user functions. - Using only coodinates for target assignments. - Added dead event handling. --- Moose Development/Moose/Core/Point.lua | 8 +- .../Moose/Functional/Artillery.lua | 498 +++++++++++++----- Moose Development/Moose/Wrapper/Group.lua | 1 + Moose Development/Moose/Wrapper/Unit.lua | 14 +- 4 files changed, 384 insertions(+), 137 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index de1a81cfc..6b46e8e78 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -471,8 +471,8 @@ do -- COORDINATE -- @param height (Optional) parameter specifying the height ASL. -- @return Temperature in Degrees Celsius. function COORDINATE:GetTemperature(height) + self:F2(height) local y=height or self.y - env.info("FF height = "..y) local point={x=self.x, y=height or self.y, z=self.z} -- get temperature [K] and pressure [Pa] at point local T,P=atmosphere.getTemperatureAndPressure(point) @@ -940,13 +940,13 @@ do -- COORDINATE -- @param #COORDINATE ToCoord Coordinate of destination. -- @return #table Table of coordinates on road. function COORDINATE:GetPathOnRoad(ToCoord) - local Path={} + self:F2(ToCoord) local path = land.findPathOnRoads("roads", self.x, self.z, ToCoord.x, ToCoord.z) + local Path={} for i, v in ipairs(path) do - --self:E(v) - local coord=COORDINATE:NewFromVec2(v) Path[#Path+1]=COORDINATE:NewFromVec2(v) end + self:F(string.format("Number of points in Path on Road = %d", #Path)) return Path end diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 01d909b1b..10c5282eb 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -5,7 +5,7 @@ -- -- ==== -- --- The ARTY class can be used to easily assign targets for artillery units. Multiple targets can be assigned. +-- The ARTY class can be used to easily assign targets for artillery units. Multiple targets can be assigned. -- -- -- ==== @@ -41,21 +41,29 @@ -- @field #number Nshells0 Initial amount of shells of the whole group. -- @field #number Nrockets0 Initial amount of rockets of the whole group. -- @field #number Nmissiles0 Initial amount of missiles of the whole group. --- @field Core.Scheduler#SCHEDULER TargetQueueSched Scheduler updating the target queue and calling OpenFire event. +-- @field #number FullAmmo Full amount of all ammunition taking the number of alive units into account. +-- @field Core.Scheduler#SCHEDULER scheduler Scheduler object handling various timed functions. +-- @field #number SchedIDTargetQueue Scheduler ID for updating the target queue and calling OpenFire event. -- @field #number TargetQueueUpdate Interval between updates of the target queue. --- @field Core.Scheduler#SCHEDULER CheckRearmedSched Scheduler checking whether reaming of the ARTY group is complete. +-- @field #number SchedIDCheckRearmed Scheduler ID responsible for checking whether reaming of the ARTY group is complete. +-- @field #number SchedIDCheckShooting Scheduler ID for checking whether a group startet firing within a certain time after the fire at point task was assigned. +-- @field #number WaitForShotTime Max time in seconds to wait until fist shot event occurs after target is assigned. If time is passed without shot, the target is deleted. Default is 300 seconds. +-- @field #number SchedIDStatusReport Scheduler ID for status report messages. The scheduler is only launched in debug mode. -- @field #table DCSdesc DCS descriptors of the ARTY group. -- @field #string Type Type of the ARTY group. +-- @field #string DisplayName Extended type name of the ARTY group. -- @field #number IniGroupStrength Inital number of units in the ARTY group. -- @field #boolean IsArtillery If true, ARTY group has attribute "Artillery". -- @field #number Speed Max speed of ARTY group. -- @field Wrapper.Unit#UNIT RearmingUnit Unit designated to rearm the ARTY group. +-- @field Wrapper.Point#COORDINATE RearmingUnitCoord Initial coordinates of the rearming unit. After rearming complete, the unit will return to this position. -- @field #boolean report Arty group sends messages about their current state or target to its coaliton. -- @field #table ammoshells Table holding names of the shell types which are included when counting the ammo. Default is {"weapons.shells"} which include most shells. -- @field #table ammorockets Table holding names of the rocket types which are included when counting the ammo. Default is {"weapons.nurs"} which includes most unguided rockets. -- @field #table ammomissiles Table holding names of the missile types which are included when counting the ammo. Default is {"weapons.missiles"} which includes some guided missiles. -- @field #number Nshots Number of shots fired on current target. --- @field #number WaitForShotTime Max time in seconds to wait until fist shot event occurs after target is assigned. If time is passed without shot, the target is deleted. Default is 300 seconds. +-- @field #number minrange Minimum firing range in kilometers. Targets closer than this distance are not engaged. Default 0 km. +-- @field #number maxrange Maximum firing range in kilometers. Targets further away than this distance are not engaged. Default 10000 km. -- @extends Core.Fsm#FSM_CONTROLLABLE -- @@ -80,20 +88,28 @@ ARTY={ Nshells0=0, Nrockets0=0, Nmissiles0=0, - TargetQueueSched=nil, + FullAmmo=0, + scheduler=nil, + SchedIDTargetQueue=nil, TargetQueueUpdate=5, - CheckRearmedSched=nil, + SchedIDCheckRearmed=nil, + SchedIDCheckShooting=nil, + WaitForShotTime=300, + SchedIDStatusReport=nil, DCSdesc=nil, Type=nil, + DisplayName=nil, IniGroupStrength=0, IsArtillery=nil, RearmingUnit=nil, + RearmingUnitCoord=nil, report=true, ammoshells={"weapons.shells"}, ammorockets={"weapons.nurs"}, ammomissiles={"weapons.missiles"}, Nshots=0, - WaitForShotTime=300, + minrange=0, + maxrange=1000000, } --- Weapong type ID. http://wiki.hoggit.us/view/DCS_enum_weapon_flag @@ -114,22 +130,24 @@ ARTY.id="ARTY | " --- Range script version. -- @field #number version -ARTY.version="0.4.0" +ARTY.version="0.5.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list: -- DONE: Delete targets from queue user function. --- TODO: Delete entire target queue user function. --- TODO: Add weapon types. +-- DONE: Delete entire target queue user function. +-- TODO: Add weapon types. Done but needs improvements. -- DONE: Add user defined rearm weapon types. -- TODO: Check if target is in range. Maybe this requires a data base with the ranges of all arty units. Pfff... -- TODO: Make ARTY move to reaming position. -- TODO: Check that right reaming vehicle is specified. Blue M818, Red Ural-375. Are there more? -- TODO: Check if ARTY group is still alive. --- TODO: Handle dead events. +-- DONE: Handle dead events. -- DONE: Abort firing task if no shooting event occured with 5(?) minutes. Something went wrong then. Min/max range for example. -- DONE: Improve assigned time for engagement. Next day? +-- TODO: Improve documentation. +-- TODO: Add pseudo user transitions. OnAfter... ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -137,7 +155,7 @@ ARTY.version="0.4.0" -- @param #ARTY self -- @param Wrapper.Group#GROUP group The GROUP object for which artillery tasks should be assigned. -- @return #ARTY ARTY object. --- @return nil If group does not exist or is not a ground group. +-- @return nil If group does not exist or is not a ground or naval group. function ARTY:New(group) BASE:F2(group) @@ -161,6 +179,9 @@ function ARTY:New(group) -- Set the controllable for the FSM. self:SetControllable(group) + -- Create scheduler object. + self.scheduler=SCHEDULER:New(self) + -- Get DCS descriptors of group. local DCSgroup=Group.getByName(group:GetName()) local DCSunit=DCSgroup:getUnit(1) @@ -193,9 +214,9 @@ function ARTY:New(group) self:AddTransition("Firing", "OpenFire", "Firing") -- Other target assigned self:AddTransition("Firing", "CeaseFire", "CombatReady") self:AddTransition("*", "Winchester", "OutOfAmmo") - self:AddTransition("OutOfAmmo", "Rearm", "Rearming") + self:AddTransition("*", "Rearm", "Rearming") self:AddTransition("Rearming", "Rearmed", "CombatReady") - --self:AddTransition("*", "Dead", "*") + self:AddTransition("*", "Dead", "*") return self end @@ -204,19 +225,22 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Add a group of target(s) for the ARTY group. +--- Assign target coordinates to the ARTY group. Only the first parameter, i.e. the coordinate of the target is mandatory. The remaining parameters are optional and can be used to fine tune the engagement. -- @param #ARTY self --- @param Wrapper.Group#GROUP group Group of targets. +-- @param Wrapper.Point#COORDINATE coord Coordinates of the target. -- @param #number prio (Optional) Priority of target. Number between 1 (high) and 100 (low). Default 50. -- @param #number radius (Optional) Radius. Default is 100 m. -- @param #number nshells (Optional) How many shells (or rockets) are fired on target per engagement. Default 5. -- @param #number maxengage (Optional) How many times a target is engaged. Default 1. --- @param #string time Day time at which the target should be engaged. Passed as a string in format "08:13:45". Current task will be canceled. --- @param #number weapontype Type of weapon to be used to attack this target. Default ARTY.WeaponType.Auto. +-- @param #string time (Optional) Day time at which the target should be engaged. Passed as a string in format "08:13:45". Current task will be canceled. +-- @param #number weapontype (Optional) Type of weapon to be used to attack this target. Default ARTY.WeaponType.Auto, i.e. the DCS logic automatically determins the appropriate weapon. +-- @param #string name (Optional) Name of the target. Default is LL DMS coordinate of the target. If the name was already given, the numbering "#01", "#02",... is appended automatically. -- @return #string Name of the target. Can be used for further reference, e.g. deleting the target from the list. --- @usage ARTY:AssignTargetGroup(GROUP:FindByName("Red Target"), 10, 250, 10, 2, "13:25:45") -function ARTY:AssignTargetGroup(group, prio, radius, nshells, maxengage, time, weapontype) - self:E({group=group, prio=prio, radius=radius, nshells=nshells, maxengage=maxengage, time=time, weapontype=weapontype}) +-- @usage paladin=ARTY:New(GROUP:FindByName("Blue Paladin")) +-- paladin:AssignTargetCoord(GROUP:FindByName("Red Targets 1"):GetCoordinate(), 10, 300, 10, 1, "08:02:00", ARTY.WeaponType.Auto, "Red Targets 1") +-- paladin:Start() +function ARTY:AssignTargetCoord(coord, prio, radius, nshells, maxengage, time, weapontype, name) + self:T({coord=coord, prio=prio, radius=radius, nshells=nshells, maxengage=maxengage, time=time, weapontype=weapontype, name=name}) -- Set default values. nshells=nshells or 5 @@ -227,21 +251,17 @@ function ARTY:AssignTargetGroup(group, prio, radius, nshells, maxengage, time, w prio=math.min(100, prio) weapontype=weapontype or ARTY.WeaponType.Auto - -- Coordinate of target. - local coord=group:GetCoordinate() - local name=group:GetName() - - -- Name of target defined my Lat/long in Degree Minute Second format. - --local name=coord:ToStringLLDMS() - + -- Name of the target. + local _name=name or coord:ToStringLLDMS() + -- Check if the name has already been used for another target. If so, the function returns a new unique name. - name=self:_CheckTargetName(name) + _name=self:_CheckTargetName(_name) -- Time in seconds. local _time=self:_ClockToSeconds(time) -- Prepare target array. - local _target={name=name, coord=coord, radius=radius, nshells=nshells, engaged=0, underfire=false, prio=prio, maxengage=maxengage, time=_time, weapontype=weapontype} + local _target={name=_name, coord=coord, radius=radius, nshells=nshells, engaged=0, underfire=false, prio=prio, maxengage=maxengage, time=_time, weapontype=weapontype} -- Add to table. table.insert(self.targets, _target) @@ -254,38 +274,28 @@ function ARTY:AssignTargetGroup(group, prio, radius, nshells, maxengage, time, w end ---- Assign coordinates of a target for the ARTY group. +--- Set minimum firing range. Targets closer than this distance are not engaged. -- @param #ARTY self --- @param Wrapper.Point#COORDINATE coord Coordinates of the target. --- @param #number prio (Optional) Priority of target. Number between 1 (high) and 100 (low). Default 50. --- @param #number radius (Optional) Radius. Default is 100 m. --- @param #number nshells (Optional) How many shells are fired on target per engagement. Default 5. --- @param #number maxengage (Optional) How many times a target is engaged. Default 9999. --- @return #string targetname Name of the target. -function ARTY:AssignTargetCoord(coord, prio, radius, nshells, maxengage) - self:E({coord=coord, prio=prio, radius=radius, nshells=nshells, maxengage=maxengage}) - - -- Set default values. - nshells=nshells or 5 - radius=radius or 100 - maxengage=maxengage or 9999 - prio=prio or 50 - prio=math.max( 1, prio) - prio=math.min(100, prio) - - -- Coordinate and name. - local name=coord:ToStringLLDMS() - - -- Prepare target array. - local _target={name=name, coord=coord, radius=radius, nshells=nshells, engaged=0, underfire=false, prio=prio, maxengage=maxengage} - - -- Add to table. - table.insert(self.targets, _target) - - -- Debug info. - self:T(ARTY.id..string.format("Added target %s, radius=%d, nshells=%d, prio=%d, maxengage=%d.", name, prio, radius, nshells, maxengage)) +-- @param #number range Min range in kilometers. Default is 0 km. +function ARTY:SetMinFiringRange(range) + self:F({range=range}) + self.minrange=range or 0 +end - return name +--- Set maximum firing range. Targets further away than this distance are not engaged. +-- @param #ARTY self +-- @param #number range Max range in kilometers. Default is 1000 km. +function ARTY:SetMaxFiringRange(range) + self:F({range=range}) + self.maxrange=range*1000 or 1000*1000 +end + +--- Set time how it is waited a unit the first shot event happens. If no shot is fired after this time, the task to fire is aborted and the target removed. +-- @param #ARTY self +-- @param #number waittime Time in seconds. Default 300 seconds. +function ARTY:SetWaitForShotTime(waittime) + self:F({waittime=waittime}) + self.WaitForShotTime=waittime or 300 end --- Assign a unit which is responsible for rearming the ARTY group. If the unit is too far away from the ARTY group it will be guided towards the ARTY group. @@ -296,6 +306,26 @@ function ARTY:SetRearmingUnit(unit) self.RearmingUnit=unit end +--- Report messages of ARTY group turned on. This is the default. +-- @param #ARTY self +function ARTY:SetReportON() + self.report=true +end + +--- Report messages of ARTY group turned off. Default is on. +-- @param #ARTY self +function ARTY:SetReportOFF() + self.report=false +end + +--- Set target queue update time interval. +-- @param #ARTY self +-- @param #number interval Time interval in seconds. Default is 5 seconds. +function ARTY:SetTargetQueueUpdateInterval(interval) + self:F2({interval=interval}) + self.TargetQueueUpdate=interval or 5 +end + --- Delete target from target list. -- @param #ARTY self -- @param #string name Name of the target. @@ -303,8 +333,19 @@ function ARTY:RemoveTarget(name) self:F2(name) local id=self:_GetTargetByName(name) if id then + self:T(ARTY.id..string.format("Group %s: Removing target %s (id=%d).", self.Controllable:GetName(), name, id)) table.remove(self.targets, id) end + self:T(ARTY.id..string.format("Group %s: Number of targets = %d.", self.Controllable:GetName(), #self.targets)) +end + +--- Delete ALL targets from current target list. +-- @param #ARTY self +function ARTY:RemoveAllTargets() + self:F2() + for _,target in pairs(self.targets) do + self:RemoveTarget(target.name) + end end --- Define shell types that are counted to determine the ammo amount the ARTY group has. @@ -353,13 +394,10 @@ end function ARTY:onafterStart(Controllable, From, Event, To) self:_EventFromTo("onafterStart", Event, From, To) + -- Debug output. local text=string.format("Started ARTY for group %s.", Controllable:GetName()) MESSAGE:New(text, 10):ToAllIf(self.Debug) - -- Set the current ROE and alam state. - --self:_SetAlarmState(self.DefaultAlarmState) - --self:_SetROE(self.DefaultROE) - -- Get Ammo. self.Nammo0, self.Nshells0, self.Nrockets0, self.Nmissiles0=self:_GetAmmo(self.Controllable) @@ -367,8 +405,11 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("Arty group = %s\n", Controllable:GetName()) text=text..string.format("Artillery attribute = %s\n", tostring(self.IsArtillery)) text=text..string.format("Type = %s\n", self.Type) + text=text..string.format("Display Name = %s\n", self.DisplayName) text=text..string.format("Number of units = %d\n", self.IniGroupStrength) text=text..string.format("Max Speed [km/h] = %d\n", self.Speed) + text=text..string.format("Min range [km] = %d\n", self.minrange/1000) + text=text..string.format("Max range [km] = %d\n", self.maxrange/1000) text=text..string.format("Total ammo count = %d\n", self.Nammo0) text=text..string.format("Number of shells = %d\n", self.Nshells0) text=text..string.format("Number of rockets = %d\n", self.Nrockets0) @@ -401,13 +442,50 @@ function ARTY:onafterStart(Controllable, From, Event, To) self:HandleEvent(EVENTS.Dead, self._OnEventDead) -- Start scheduler to monitor task queue. - self.TargetQueueSched=SCHEDULER:New(nil, ARTY._TargetQueue, {self}, 5, self.TargetQueueUpdate) + self.SchedIDTargetQueue=self.scheduler:Schedule(self, ARTY._TargetQueue, {self}, 5, self.TargetQueueUpdate) -- Start scheduler to monitor if ARTY group started firing within a certain time. - self.CheckShootingSched=SCHEDULER:New(nil, ARTY._CheckShootingStarted, {self}, 60, 60) + self.SchedIDCheckShooting=self.scheduler:Schedule(self, ARTY._CheckShootingStarted, {self}, 60, 60) + + -- Start cheduler for status reports. + if self.Debug then + self.SchedIDStatusReport=self.scheduler:Schedule(self, ARTY._StatusReport, {self}, 30, 30) + end end +--- After "Start" event. Initialized ROE and alarm state. Starts the event handler. +-- @param #ARTY self +function ARTY:_StatusReport() + + -- Get Ammo. + local Nammo, Nshells, Nrockets, Nmissiles=self:_GetAmmo(self.Controllable) + + local text=string.format("\n******************************************************\n") + text=text..string.format("Status of ARTY = %s\n", self.Controllable:GetName()) + text=text..string.format("FSM state = %s\n", self:GetState()) + text=text..string.format("Total ammo count = %d\n", Nammo) + text=text..string.format("Number of shells = %d\n", Nshells) + text=text..string.format("Number of rockets = %d\n", Nrockets) + text=text..string.format("Number of missiles = %d\n", Nmissiles) + if self.currentTarget then + text=text..string.format("Current Target = %s\n", tostring(self.currentTarget.name)) + else + text=text..string.format("Current Target = %s\n", "none") + end + text=text..string.format("Nshots curr. Target = %d\n", self.Nshots) + text=text..string.format("Targets:\n") + for _, target in pairs(self.targets) do + local _clock=self:_SecondsToClock(target.time) + local _weapon=self:_WeaponTypeName(target.weapontype) + text=text..string.format("- %s, prio=%3d, radius=%5d, nshells=%4d, engaged=%3d, maxengage=%3d, weapon=%s, time=%s\n", + target.name, target.prio, target.radius, target.nshells, target.engaged, target.maxengage, _weapon, tostring(_clock)) + end + text=text..string.format("******************************************************") + env.info(ARTY.id..text) + +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -465,19 +543,19 @@ function ARTY:_OnEventShot(EventData) -- Special weapon type requested ==> Check if corresponding ammo is empty. if self.currentTarget.weapontype==ARTY.WeaponType.UnguidedCannon and _nshells==0 then - self:T(ARTY.id.."Cannons requested and shells empty.") + self:T(ARTY.id.."Cannons requested but shells empty.") self:CeaseFire(self.currentTarget) return elseif self.currentTarget.weapontype==ARTY.WeaponType.UnguidedRockets and _nrockets==0 then - self:T(ARTY.id.."Rockets requested and rockets empty.") + self:T(ARTY.id.."Rockets requested but rockets empty.") self:CeaseFire(self.currentTarget) return elseif self.currentTarget.weapontype==ARTY.WeaponType.UnguidedAny and _nshells+_nrockets==0 then - self:T(ARTY.id.."Unguided weapon requested and shells+rockets empty.") + self:T(ARTY.id.."Unguided weapon requested but shells and rockets empty.") self:CeaseFire(self.currentTarget) return @@ -505,37 +583,27 @@ function ARTY:_OnEventShot(EventData) end end ---- Eventhandler for dead event. +--- Event handler for event Dead. -- @param #ARTY self -- @param Core.Event#EVENTDATA EventData function ARTY:_OnEventDead(EventData) self:F(EventData) -end - ---- Set task for firing at a coordinate. --- @param #ARTY self --- @param Core.Point#COORDINATE coord Coordinates to fire upon. --- @param #number radius Radius around coordinate. --- @param #number nshells Number of shells to fire. --- @param #number weapontype Type of weapon to use. -function ARTY:_FireAtCoord(coord, radius, nshells, weapontype) - self:E({coord=coord, radius=radius, nshells=nshells}) - - -- Controllable. - local group=self.Controllable --Wrapper.Controllable#CONTROLLABLE - - -- Set ROE to weapon free. - group:OptionROEOpenFire() - -- Get Vec2 - local vec2=coord:GetVec2() + env.info("FF event dead") - -- Get task. - local fire=group:TaskFireAtPoint(vec2, radius, nshells, weapontype) - - -- Execute task. - group:SetTask(fire) - --group:PushTask(fire) + -- Name of controllable. + local _name=self.Controllable:GetName() + + -- Check for correct group. + if EventData.IniGroupName==_name then + + -- Dead Unit. + self:T2(string.format("%s: Captured dead event for unit %s.", _name, EventData.IniUnitName)) + + -- FSM Dead event. We give one second for update of data base. + self:__Dead(1) + end + end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -558,13 +626,7 @@ function ARTY:onbeforeOpenFire(Controllable, From, Event, To, target) if target.time~=nil and self.currentTarget~=nil and self.currentTarget.prio > target.prio then -- Debug info. self:T(ARTY.id..string.format("Group %s current target %s has lower prio than new target %s with attack time.", self.Controllable:GetName(), self.currentTarget.name, target.name)) - - -- Reset current task. - --self.Controllable:ClearTasks() - - -- Set number of shots counter to zero. - self.Nshots=0 - + -- Stop firing on current target. self:CeaseFire(self.currentTarget) @@ -580,7 +642,30 @@ function ARTY:onbeforeOpenFire(Controllable, From, Event, To, target) -- Deny transition. return false end + +-- Distance to target + local range=Controllable:GetCoordinate():Get2DDistance(target.coord) + + -- Check that distance to target is within range. + if rangeself.maxrange then + + -- Debug output. + local text + if rangeself.maxrange then + text=string.format("%s, target is out of range. Distance of %d km is greater than max range of %d km.", Controllable:GetName(), range/1000, self.maxrange/1000) + end + self:T(ARTY.id..text) + MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) + -- Remove target. + self:RemoveTarget(target.name) + + -- Deny transition. + return false + end + return true end @@ -605,10 +690,6 @@ function ARTY:onafterOpenFire(Controllable, From, Event, To, target) if id then -- Set under fire flag. self.targets[id].underfire=true - -- Increase engaged counter - self.targets[id].engaged=self.targets[id].engaged+1 - -- Clear the attack time. - self.targets[id].time=nil -- Set current target. self.currentTarget=target -- Set time the target was assigned. @@ -630,7 +711,7 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Before "CeaseFire" event. +--- Before "CeaseFire" event. Nothing to do at the moment. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. -- @param #string From From state. @@ -658,16 +739,28 @@ function ARTY:onafterCeaseFire(Controllable, From, Event, To, target) local text=string.format("%s, ceasing fire on target %s.", Controllable:GetName(), target.name) self:T(ARTY.id..text) MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report) - - -- Set number of shots to zero. - self.Nshots=0 - + -- Get target array index. local id=self:_GetTargetByName(target.name) - -- Target is not under fire any more. - self.targets[id].underfire=false + -- Increase engaged counter + if id then + -- Target was actually engaged. (Could happen that engagement was aborted while group was still aiming.) + if self.Nshots>0 then + self.targets[id].engaged=self.targets[id].engaged+1 + -- Clear the attack time. + self.targets[id].time=nil + end + -- Target is not under fire any more. + self.targets[id].underfire=false + end + -- Clear tasks. + self.Controllable:ClearTasks() + + -- Set number of shots to zero. + self.Nshots=0 + -- If number of engagements has been reached, the target is removed. if target.engaged >= target.maxengage then self:RemoveTarget(target.name) @@ -679,6 +772,21 @@ function ARTY:onafterCeaseFire(Controllable, From, Event, To, target) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Before "Winchester" event. Cease fire on current target. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function ARTY:onbeforeWinchester(Controllable, From, Event, To) + + -- Cease fire first. + if self.currentTarget then + self:CeaseFire(self.currentTarget) + end + + return true +end --- After "Winchester" event. Group is out of ammo. -- @param #ARTY self @@ -693,10 +801,7 @@ function ARTY:onafterWinchester(Controllable, From, Event, To) local text=string.format("%s, winchester.", Controllable:GetName()) self:T(ARTY.id..text) MESSAGE:New(text, 30):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) - - -- Cease fire first. - self:CeaseFire(self.currentTarget) - + -- Init rearming if possible. self:Rearm() @@ -741,11 +846,14 @@ function ARTY:onafterRearm(Controllable, From, Event, To) local vec2=coord:GetRandomVec2InRadius(20, 100) local pops=COORDINATE:NewFromVec2(vec2) + -- Remember the coordinates of the rearming unit. After rearming it will go back to this position. + self.RearmingUnitCoord=self.RearmingUnit:GetCoordinate() + -- Route unit to ARTY group. self.RearmingUnit:RouteGroundOnRoad(pops, 50, 5) -- Start scheduler to monitor ammo count until rearming is complete. - self.CheckRearmedSched=SCHEDULER:New(nil,self._CheckRearmed, {self}, 20, 20) + self.SchedIDCheckRearmed=self.scheduler:Schedule(self, ARTY._CheckRearmed, {self}, 20, 20) end @@ -757,8 +865,18 @@ function ARTY:_CheckRearmed() -- Get current ammo. local nammo,nshells,nrockets,nmissiles=self:_GetAmmo(self.Controllable) + -- Number of units still alive. + local units=self.Controllable:GetUnits() + local nunits=0 + if units then + nunits=#units + end + + -- Full Ammo count. + self.FullAmmo=self.Nammo0 * nunits / self.IniGroupStrength + -- Rearming status in per cent. - local _rearmpc=nammo/self.Nammo0*100 + local _rearmpc=nammo/self.FullAmmo*100 -- Send message. local text=string.format("%s, rearming %d %% complete.", self.Controllable:GetName(), _rearmpc) @@ -766,7 +884,7 @@ function ARTY:_CheckRearmed() MESSAGE:New(text, 10):ToCoalitionIf(self.Controllable:GetCoalition(), self.report or self.Debug) -- Rearming --> Rearmed --> CombatReady - if nammo==self.Nammo0 then + if nammo==self.FullAmmo then self:Rearmed() end @@ -784,17 +902,127 @@ function ARTY:onafterRearmed(Controllable, From, Event, To) -- Send message. local text=string.format("%s, rearming complete.", Controllable:GetName()) self:T(ARTY.id..text) - MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report) + MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) -- Stop scheduler. - self.CheckRearmedSched:Stop() + --self.SchedCheckRearmed:Stop() + if self.SchedIDCheckRearmed then + self.scheduler:Stop(self.SchedIDCheckRearmed) + end + + -- Route unit back to where it came from. + self.RearmingUnit:RouteGroundOnRoad(self.RearmingUnitCoord, 50, 5) end +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- After "Dead" event, when a unit has died. When all units of a group are dead, FSM is stopped and eventhandler removed. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function ARTY:onafterDead(Controllable, From, Event, To) + self:_EventFromTo("onafterDead", Event, From, To) + + -- Number of units left in the group. + local units=self.Controllable:GetUnits() + local nunits=0 + if units~=nil then + nunits=#units + end + + -- Adjust full ammo count + self.FullAmmo=self.Nammo0*nunits/self.IniGroupStrength + + -- Message. + local text=string.format("%s, one of our units just died! %d units left.", self.Controllable:GetName(), nunits) + MESSAGE:New(text, 10):ToAllIf(self.Debug) + self:T(ARTY.id..text) + + -- Go to stop state. + if nunits==0 then + self:Stop() + end + +end + +--- Before "Stop" event. Cease fire on current target. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function ARTY:onbeforeStop(Controllable, From, Event, To) + self:_EventFromTo("onbeforeStop", Event, From, To) + + -- Cease Fire on current target. + if self.currentTarget then + self:CeaseFire(self.currentTarget) + end + + return true +end + +--- After "Stop" event. Remove all target, stop schedulers, unhandle events and stop the FSM. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function ARTY:onafterStop(Controllable, From, Event, To) + self:_EventFromTo("onafterStop", Event, From, To) + + -- Debug info. + self:T(ARTY.id..string.format("Stopping ARTY FSM for group %s.", Controllable:GetName())) + -- Remove all targets. + --self:RemoveAllTargets() + -- Stop schedulers. + if self.SchedIDTargetQueue then + self.scheduler:Stop(self.SchedIDTargetQueue) + end + if self.SchedIDCheckShooting then + self.scheduler:Stop(self.SchedIDCheckShooting) + end + if self.SchedIDCheckRearmed then + self.scheduler:Stop(self.SchedIDCheckRearmed) + end + -- Unhandle event. + self:UnHandleEvent(EVENTS.Shot) + self:UnHandleEvent(EVENTS.Dead) +end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Set task for firing at a coordinate. +-- @param #ARTY self +-- @param Core.Point#COORDINATE coord Coordinates to fire upon. +-- @param #number radius Radius around coordinate. +-- @param #number nshells Number of shells to fire. +-- @param #number weapontype Type of weapon to use. +function ARTY:_FireAtCoord(coord, radius, nshells, weapontype) + self:E({coord=coord, radius=radius, nshells=nshells}) + + -- Controllable. + local group=self.Controllable --Wrapper.Controllable#CONTROLLABLE + + -- Set ROE to weapon free. + group:OptionROEOpenFire() + + -- Get Vec2 + local vec2=coord:GetVec2() + + -- Get task. + local fire=group:TaskFireAtPoint(vec2, radius, nshells, weapontype) + + -- Execute task. + group:SetTask(fire) + --group:PushTask(fire) +end + + --- Go through queue of assigned tasks. -- @param #ARTY self function ARTY:_TargetQueue() @@ -805,7 +1033,7 @@ function ARTY:_TargetQueue() -- No targets assigned at the moment. if #self.targets==0 then - self:T(ARTY.id..string.format("Group %s, no targets assigned at the moment. No need for _TargetQueue.", self.Controllable:GetName())) + self:T3(ARTY.id..string.format("Group %s, no targets assigned at the moment. No need for _TargetQueue.", self.Controllable:GetName())) return end @@ -904,15 +1132,18 @@ end -- @return Number of ALL shells left from the whole group. function ARTY:_GetAmmo(controllable) self:F2(controllable) - - -- Get all units. - local units=controllable:GetUnits() - + -- Init counter. local nammo=0 local nshells=0 local nrockets=0 local nmissiles=0 + + -- Get all units. + local units=controllable:GetUnits() + if units==nil then + return nammo, nshells, nrockets, nmissiles + end for _,unit in pairs(units) do @@ -1024,17 +1255,22 @@ function ARTY:_CheckShootingStarted() if self.currentTarget then -- Current time. - local Tnow=timer.getTime() + local Tnow=timer.getTime() + -- Get name and id of target. + local name=self.currentTarget.name + -- Time that passed after current target has been assigned. local dt=Tnow-self.currentTarget.Tassigned - + -- Debug info + if self.Nshots==0 then + self:T(ARTY.id..string.format("%s, waiting for %d seconds for first shot on target %s.", self.Controllable:GetName(), dt, name)) + end + + -- Check if we waited long enough and no shot was fired. if dt > self.WaitForShotTime and self.Nshots==0 then - -- Get name and id of target. - local name=self.currentTarget.name - -- Debug info. self:T(ARTY.id..string.format("%s, no shot event after %d seconds. Removing current target %s from list.", self.Controllable:GetName(), self.WaitForShotTime, name)) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index c595a5dbb..db2092e9e 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -264,6 +264,7 @@ function GROUP:Destroy( GenerateEvent ) if self:IsAir() then self:CreateEventCrash( timer.getTime(), UnitData ) else + env.info("FF create event dead") self:CreateEventDead( timer.getTime(), UnitData ) end end diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 2734279bd..1c4236dc3 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -161,18 +161,28 @@ end --- Destroys the UNIT. -- @param #UNIT self +-- @param #boolean GenerateEvent (Optional) true if you want to generate a crash or dead event for the unit. -- @return #nil The DCS Unit is not existing or alive. -function UNIT:Destroy() +function UNIT:Destroy( GenerateEvent ) self:F2( self.ObjectName ) local DCSObject = self:GetDCSObject() if DCSObject then + local UnitGroup = self:GetGroup() local UnitGroupName = UnitGroup:GetName() self:F( { UnitGroupName = UnitGroupName } ) + + if GenerateEvent and GenerateEvent == true then + if self:IsAir() then + self:CreateEventCrash( timer.getTime(), DCSObject ) + else + self:CreateEventDead( timer.getTime(), DCSObject ) + end + end + USERFLAG:New( UnitGroupName ):Set( 100 ) - --BASE:CreateEventCrash( timer.getTime(), DCSObject ) DCSObject:destroy() end From 810d900d7eed50de5e6d3e7ace0d324f6eda25c7 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Wed, 2 May 2018 22:18:07 +0200 Subject: [PATCH 072/420] Working with Transporting mode on or off. --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 369 ++++++++++++------ Moose Development/Moose/Cargo/Cargo.lua | 45 ++- Moose Development/Moose/Cargo/CargoGroup.lua | 249 ++++++++---- Moose Development/Moose/Cargo/CargoUnit.lua | 27 +- Moose Development/Moose/Core/Point.lua | 2 + .../Moose/Wrapper/Controllable.lua | 39 +- .../Moose/Wrapper/Positionable.lua | 9 + 7 files changed, 545 insertions(+), 195 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 68ffc7046..d5022f135 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -1,4 +1,4 @@ ---- **AI** -- (R2.3) - Models the intelligent transportation of infantry (cargo). +--- **AI** -- (R2.3) - Models the intelligent transportation of infantry and other cargo. -- -- === -- @@ -12,14 +12,69 @@ -- @extends Core.Fsm#FSM_CONTROLLABLE ---- # AI\_CARGO\_TROOPS class, extends @{Core.Base@BASE} +--- # AI\_CARGO\_APC class, extends @{Core.Base#BASE} -- -- === -- +-- AI\_CARGO\APC brings a dynamic cargo handling capability for AI groups. +-- +-- Armoured Personnel Carriers (APC), Trucks, Jeeps and other ground based carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. +-- +-- ## Cargo loading. +-- +-- The module will load automatically cargo when the APCs are within boarding or loading range. +-- The boarding or loading range is specified when the cargo is created in the simulation, and therefore, this range depends on the type of cargo +-- and the specified boarding range. +-- +-- ## Enemies nearby. +-- +-- When the APCs are approaching enemy units, something special is happening. +-- The APCs will stop moving, and the loaded infantry will unboard and follow the APCs and will help to defend the group. +-- The carrier will hold the route once the unboarded infantry is further than 50 meters from the APCs, +-- to ensure that the APCs are not too far away from the following running infantry. +-- Once all enemies are cleared, the infantry will board again automatically into the APCs. Once boarded, the APCs will follow its pre-defined route. +-- +-- A combat range needs to be specified in meters at the @{#AI_CARGO_APC.New}() method. +-- This combat range will trigger the unboarding of troops when enemies are within the combat range around the APCs. +-- During my tests, I've noticed that there is a balance between ensuring that the infantry is within sufficient hit range (effectiveness) versus +-- vulnerability of the infantry. It all depends on the kind of enemies that are expected to be encountered. +-- A combat range of 350 meters to 500 meters has been proven to be the most effective and efficient. +-- +-- ## Infantry health. +-- +-- When infantry is unboarded from the APCs, the infantry is actually respawned into the battlefield. +-- As a result, the unboarding infantry is very _healthy_ every time it unboards. +-- This is due to the limitation of the DCS simulator, which is not able to specify the health of new spawned units as a parameter. +-- However, infantry that was destroyed when unboarded and following the APCs, won't be respawned again. Destroyed is destroyed. +-- As a result, there is some additional strength that is gained when an unboarding action happens, but in terms of simulation balance this has +-- marginal impact on the overall battlefield simulation. Fortunately, the firing strength of infantry is limited, and thus, respacing healthy infantry every +-- time is not so much of an issue ... +-- +-- ## Control the APCs on the map. +-- +-- It is possible also as a human ground commander to influence the path of the APCs, by pointing a new path using the DCS user interface on the map. +-- In this case, the APCs will change the direction towards its new indicated route. However, there is a catch! +-- Once the APCs are near the enemy, and infantry is unboarded, the APCs won't be able to hold the route until the infantry could catch up. +-- The APCs will simply drive on and won't stop! This is a limitation in ED that prevents user actions being controlled by the scripting engine. +-- No workaround is possible on this. +-- +-- ## Cargo deployment. +-- +-- Using the @{#AI_CARGO_APC.Deploy}() method, you are able to direct the APCs towards a point on the battlefield to unboard/unload the cargo at the specific coordinate. +-- The APCs will follow nearby roads as much as possible, to ensure fast and clean cargo transportation between the objects and villages in the simulation environment. +-- +-- ## Cargo pickup. +-- +-- Using the @{#AI_CARGO_APC.Pickup}() method, you are able to direct the APCs towards a point on the battlefield to board/load the cargo at the specific coordinate. +-- The APCs will follow nearby roads as much as possible, to ensure fast and clean cargo transportation between the objects and villages in the simulation environment. +-- +-- +-- -- @field #AI_CARGO_APC AI_CARGO_APC = { ClassName = "AI_CARGO_APC", - Coordinate = nil -- Core.Point#COORDINATE, + Coordinate = nil, -- Core.Point#COORDINATE, + APC_Cargo = {}, } --- Creates a new AI_CARGO_APC object. @@ -45,7 +100,7 @@ function AI_CARGO_APC:New( CargoCarrier, CargoSet, CombatRadius ) self:AddTransition( "Boarding", "Loaded", "Loaded" ) self:AddTransition( "Loaded", "Unload", "Unboarding" ) self:AddTransition( "Unboarding", "Unboard", "Unboarding" ) - self:AddTransition( "Unboarding", "Unloaded", "Unloaded" ) + self:AddTransition( { "Unboarding", "Unloaded" }, "Unloaded", "Unloaded" ) self:AddTransition( "*", "Monitor", "*" ) self:AddTransition( "*", "Follow", "Following" ) @@ -131,6 +186,7 @@ function AI_CARGO_APC:New( CargoCarrier, CargoSet, CombatRadius ) self:__Monitor( 1 ) self:SetCarrier( CargoCarrier ) + self.Transporting = false return self end @@ -184,6 +240,11 @@ function AI_CARGO_APC:SetCarrier( CargoCarrier ) end +function AI_CARGO_APC:IsTransporting() + + return self.Transporting == true +end + --- Find a free Carrier within a range. -- @param #AI_CARGO_APC self -- @param Core.Point#COORDINATE Coordinate @@ -214,18 +275,20 @@ end --- Follow Infantry to the Carrier. -- @param #AI_CARGO_APC self -- @param #AI_CARGO_APC Me --- @param Wrapper.Group#GROUP CargoCarrier --- @param Wrapper.Group#GROUP InfantryGroup +-- @param Wrapper.Unit#UNIT APCUnit +-- @param Cargo.CargoGroup#CARGO_GROUP Cargo -- @return #AI_CARGO_APC -function AI_CARGO_APC:FollowToCarrier( Me, CargoCarrier, InfantryGroup ) +function AI_CARGO_APC:FollowToCarrier( Me, APCUnit, CargoGroup ) + + local InfantryGroup = CargoGroup:GetGroup() self:F( { self = self:GetClassNameAndID(), InfantryGroup = InfantryGroup:GetName() } ) --if self:Is( "Following" ) then - if CargoCarrier:IsAlive() then + if APCUnit:IsAlive() then -- We check if the Cargo is near to the CargoCarrier. - if InfantryGroup:IsPartlyInZone( ZONE_UNIT:New( "Radius", CargoCarrier, 5 ) ) then + if InfantryGroup:IsPartlyInZone( ZONE_UNIT:New( "Radius", APCUnit, 25 ) ) then -- The Cargo does not need to follow the Carrier. Me:Guard() @@ -246,12 +309,12 @@ function AI_CARGO_APC:FollowToCarrier( Me, CargoCarrier, InfantryGroup ) self:F({FromGround=FromGround}) table.insert( Waypoints, FromGround ) - local ToCoord = CargoCarrier:GetCoordinate():GetRandomCoordinateInRadius( 10, 5 ) + local ToCoord = APCUnit:GetCoordinate():GetRandomCoordinateInRadius( 10, 5 ) local ToGround = ToCoord:WaypointGround( 10, "Diamond" ) self:F({ToGround=ToGround}) table.insert( Waypoints, ToGround ) - local TaskRoute = InfantryGroup:TaskFunction( "AI_CARGO_APC.FollowToCarrier", Me, CargoCarrier, InfantryGroup ) + local TaskRoute = InfantryGroup:TaskFunction( "AI_CARGO_APC.FollowToCarrier", Me, APCUnit, CargoGroup ) self:F({Waypoints = Waypoints}) local Waypoint = Waypoints[#Waypoints] @@ -265,40 +328,43 @@ end --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP CargoCarrier -function AI_CARGO_APC:onafterMonitor( CargoCarrier, From, Event, To ) - self:F( { CargoCarrier, From, Event, To } ) +-- @param Wrapper.Group#GROUP APC +function AI_CARGO_APC:onafterMonitor( APC, From, Event, To ) + self:F( { APC, From, Event, To } ) - if CargoCarrier and CargoCarrier:IsAlive() then + if APC and APC:IsAlive() then if self.CarrierCoordinate then - local Coordinate = CargoCarrier:GetCoordinate() - self.Zone:Scan( { Object.Category.UNIT } ) - if self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then - if self:Is( "Unloaded" ) or self:Is( "Following" ) then - -- There are no enemies within combat range. Load the CargoCarrier. - self:Load() - end - else - if self:Is( "Loaded" ) then - -- There are enemies within combat range. Unload the CargoCarrier. - self:__Unload( 1 ) + if self:IsTransporting() then + local Coordinate = APC:GetCoordinate() + self.Zone:Scan( { Object.Category.UNIT } ) + if self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then + if self:Is( "Unloaded" ) or self:Is( "Following" ) then + -- There are no enemies within combat range. Load the CargoCarrier. + self:Load() + end else - if self:Is( "Unloaded" ) then - if not self.Cargo:IsNear( CargoCarrier, 5 ) then + if self:Is( "Loaded" ) then + -- There are enemies within combat range. Unload the CargoCarrier. + self:__Unload( 1 ) + else + if self:Is( "Unloaded" ) then self:Follow() end - end - if self:Is( "Following" ) then - local Distance = Coordinate:Get2DDistance( self.Cargo:GetCoordinate() ) - self:F( { Distance = Distance } ) - if Distance > 40 then - CargoCarrier:RouteStop() - self.CarrierStopped = true - else - if self.CarrierStopped then - if self.Cargo:IsNear( CargoCarrier, 10 ) then - CargoCarrier:RouteResume() - self.CarrierStopped = nil + if self:Is( "Following" ) then + for APCUnit, Cargo in pairs( self.APC_Cargo ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + if Cargo:IsAlive() then + if not Cargo:IsNear( APCUnit, 40 ) then + APCUnit:RouteStop() + self.CarrierStopped = true + else + if self.CarrierStopped then + if Cargo:IsNear( APCUnit, 25 ) then + APCUnit:RouteResume() + self.CarrierStopped = nil + end + end + end end end end @@ -307,7 +373,7 @@ function AI_CARGO_APC:onafterMonitor( CargoCarrier, From, Event, To ) end end - self.CarrierCoordinate = CargoCarrier:GetCoordinate() + self.CarrierCoordinate = APC:GetCoordinate() end self:__Monitor( -5 ) @@ -316,25 +382,40 @@ end --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP Carrier -function AI_CARGO_APC:onbeforeLoad( Carrier, From, Event, To ) - self:F( { Carrier, From, Event, To } ) +-- @param Wrapper.Group#GROUP APC +function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To ) + self:F( { APC, From, Event, To } ) - if Carrier and Carrier:IsAlive() then - for _, Cargo in pairs( self.CargoSet:GetSet() ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - self:F( Cargo ) - if Cargo:IsInLoadRadius( Carrier:GetCoordinate() ) then - self:F( "In radius" ) - Carrier:RouteStop() - self:__Board( 1, Cargo ) - Cargo:Board( Carrier:GetUnit(1), 25 ) - return true + local Boarding = false + self.BoardingCount = 0 + + if APC and APC:IsAlive() then + self.APC_Cargo = {} + for _, APCUnit in pairs( APC:GetUnits() ) do + local APCUnit = APCUnit -- Wrapper.Unit#UNIT + for _, Cargo in pairs( self.CargoSet:GetSet() ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + self:F( { IsUnLoaded = Cargo:IsUnLoaded() } ) + if Cargo:IsUnLoaded() then + if Cargo:IsInLoadRadius( APCUnit:GetCoordinate() ) then + self:F( { "In radius", APCUnit:GetName() } ) + APC:RouteStop() + --Cargo:Ungroup() + Cargo:Board( APCUnit, 25 ) + self:__Board( 1, Cargo ) + Boarding = true + + -- So now this APCUnit has Cargo that is being loaded. + -- This will be used further in the logic to follow and to check cargo status. + self.APC_Cargo[APCUnit] = Cargo + break + end + end end end end - - return false + + return Boarding end @@ -348,147 +429,203 @@ function AI_CARGO_APC:onafterBoard( Carrier, From, Event, To, Cargo ) if not Cargo:IsLoaded() then self:__Board( 10, Cargo ) else - self:__Loaded( 1, Cargo ) + self:__Loaded( 1 ) end end end --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP CargoCarrier -function AI_CARGO_APC:onafterLoaded( CargoCarrier, From, Event, To, Cargo ) - self:F( { CargoCarrier, From, Event, To, Cargo } ) +-- @param Wrapper.Group#GROUP APC +function AI_CARGO_APC:onbeforeLoaded( APC, From, Event, To ) + self:F( { APC, From, Event, To } ) - if CargoCarrier and CargoCarrier:IsAlive() then - CargoCarrier:RouteResume() - self.Cargo = Cargo + local Loaded = true + + if APC and APC:IsAlive() then + for APCUnit, Cargo in pairs( self.APC_Cargo ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + self:F( { IsLoaded = Cargo:IsLoaded(), IsDestroyed = Cargo:IsDestroyed() } ) + if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then + Loaded = false + end + end + end + if Loaded == true then + APC:RouteResume() + end + + return Loaded + end --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP CargoCarrier -function AI_CARGO_APC:onafterUnload( CargoCarrier, From, Event, To ) - self:F( { CargoCarrier, From, Event, To } ) +-- @param Wrapper.Group#GROUP APC +function AI_CARGO_APC:onafterUnload( APC, From, Event, To ) + self:F( { APC, From, Event, To } ) - if CargoCarrier and CargoCarrier:IsAlive() then - CargoCarrier:RouteStop() - self.Cargo:UnBoard() - self:__Unboard( 10 ) + if APC and APC:IsAlive() then + for _, APCUnit in pairs( APC:GetUnits() ) do + local APCUnit = APCUnit -- Wrapper.Unit#UNIT + APC:RouteStop() + for _, Cargo in pairs( APCUnit:GetCargo() ) do + Cargo:UnBoard() + self:__Unboard( 10, Cargo ) + end + end end end --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP CargoCarrier -function AI_CARGO_APC:onafterUnboard( CargoCarrier, From, Event, To ) - self:F( { CargoCarrier, From, Event, To } ) +-- @param Wrapper.Group#GROUP APC +function AI_CARGO_APC:onafterUnboard( APC, From, Event, To, Cargo ) + self:F( { APC, From, Event, To, Cargo:GetName() } ) - if CargoCarrier and CargoCarrier:IsAlive() then - if not self.Cargo:IsUnLoaded() then - self:__Unboard( 10 ) + if APC and APC:IsAlive() then + if not Cargo:IsUnLoaded() then + self:__Unboard( 10, Cargo ) else - self:__Unloaded( 1 ) + self:__Unloaded( 1, Cargo ) end end end --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP CargoCarrier -function AI_CARGO_APC:onafterUnloaded( CargoCarrier, From, Event, To ) - self:F( { CargoCarrier, From, Event, To } ) +-- @param Wrapper.Group#GROUP APC +function AI_CARGO_APC:onbeforeUnloaded( APC, From, Event, To, Cargo ) + self:F( { APC, From, Event, To, Cargo:GetName() } ) - if CargoCarrier and CargoCarrier:IsAlive() then - self:Guard() - self.CargoCarrier = CargoCarrier - CargoCarrier:RouteResume() + local AllUnloaded = true + + --Cargo:Regroup() + + if APC and APC:IsAlive() then + for _, CargoCheck in pairs( self.CargoSet:GetSet() ) do + local CargoCheck = CargoCheck -- Cargo.Cargo#CARGO + self:F( { CargoCheck:GetName(), IsUnLoaded = CargoCheck:IsUnLoaded() } ) + if CargoCheck:IsUnLoaded() == false then + AllUnloaded = false + break + end + end + + if AllUnloaded == true then + self:Guard() + self.CargoCarrier = APC + self.APC_Cargo = {} + end end + self:F( { AllUnloaded = AllUnloaded } ) + return AllUnloaded + end --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP CargoCarrier -function AI_CARGO_APC:onafterFollow( CargoCarrier, From, Event, To ) - self:F( { CargoCarrier, From, Event, To } ) +-- @param Wrapper.Group#GROUP APC +function AI_CARGO_APC:onafterFollow( APC, From, Event, To ) + self:F( { APC, From, Event, To } ) self:F( "Follow" ) - if CargoCarrier and CargoCarrier:IsAlive() then - self.Cargo.CargoSet:ForEach( - --- @param Core.Cargo#CARGO Cargo - function( Cargo ) - self:F( { "Follow", Cargo.CargoObject:GetName() } ) - if Cargo.CargoObject:IsAlive() == true then - self:F( { "Follow", Cargo.CargoObject:GetID() } ) - self:FollowToCarrier( self, CargoCarrier, Cargo.CargoObject:GetGroup() ) - end + if APC and APC:IsAlive() then + for APCUnit, Cargo in pairs( self.APC_Cargo ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + if Cargo:IsUnLoaded() then + self:FollowToCarrier( self, APCUnit, Cargo ) + APCUnit:RouteResume() end - ) + end end end --- @param #AI_CARGO_APC --- @param Wrapper.Group#GROUP Carrier -function AI_CARGO_APC._Pickup( Carrier ) +-- @param Wrapper.Group#GROUP APC +function AI_CARGO_APC._Pickup( APC, self ) - Carrier:F( { "AI_CARGO_APC._Pickup:", Carrier:GetName() } ) + APC:F( { "AI_CARGO_APC._Pickup:", APC:GetName() } ) - if Carrier:IsAlive() then - Carrier:__Load( 1 ) + if APC:IsAlive() then + self:Load() + self.Transporting = true end end --- @param #AI_CARGO_APC --- @param Wrapper.Group#GROUP Carrier -function AI_CARGO_APC._Deploy( Carrier ) +-- @param Wrapper.Group#GROUP APC +function AI_CARGO_APC._Deploy( APC, self ) - Carrier:F( { "AI_CARGO_APC._Deploy:", Carrier:GetName() } ) + APC:F( { "AI_CARGO_APC._Deploy:", APC } ) - if Carrier:IsAlive() then - Carrier:__Unload( 1 ) + if APC:IsAlive() then + self:Unload() + self.Transporting = false end end --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP Carrier +-- @param Wrapper.Group#GROUP APC -- @param From -- @param Event -- @param To -- @param Core.Point#COORDINATE Coordinate -- @param #number Speed -function AI_CARGO_APC:onafterPickup( Carrier, From, Event, To, Coordinate, Speed ) +function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed ) - if Carrier and Carrier:IsAlive() then + if APC and APC:IsAlive() then - self.RoutePickup = true + if Coordinate then + self.RoutePickup = true + + local Waypoints = APC:TaskGroundOnRoad( Coordinate, Speed ) + + local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Pickup", self ) + + self:F({Waypoints = Waypoints}) + local Waypoint = Waypoints[#Waypoints] + APC:SetTaskWaypoint( Waypoint, TaskFunction ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone. - Carrier:RouteGroundOnRoad( Coordinate, Speed, 1 ) + APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. + else + AI_CARGO_APC._Pickup( APC, self ) + end end end --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP Carrier +-- @param Wrapper.Group#GROUP APC -- @param From -- @param Event -- @param To -- @param Core.Point#COORDINATE Coordinate -- @param #number Speed -function AI_CARGO_APC:onafterDeploy( Carrier, From, Event, To, Coordinate, Speed ) +function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed ) - if Carrier and Carrier:IsAlive() then + if APC and APC:IsAlive() then self.RouteDeploy = true - Carrier:RouteGroundOnRoad( Coordinate, Speed, 1 ) + local Waypoints = APC:TaskGroundOnRoad( Coordinate, Speed ) + + local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Deploy", self ) + + self:F({Waypoints = Waypoints}) + local Waypoint = Waypoints[#Waypoints] + APC:SetTaskWaypoint( Waypoint, TaskFunction ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone. + + APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. end end diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 6c2bbe033..215b1cba7 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -293,7 +293,41 @@ do -- CARGO local CargoFound = _DATABASE:FindCargo( CargoName ) return CargoFound - end + end + + --- Get the x position of the cargo. + -- @param #CARGO self + -- @return #number + function CARGO:GetX() + if self:IsLoaded() then + return self.CargoCarrier:GetCoordinate().x + else + return self.CargoObject:GetCoordinate().x + end + end + + --- Get the y position of the cargo. + -- @param #CARGO self + -- @return #number + function CARGO:GetY() + if self:IsLoaded() then + return self.CargoCarrier:GetCoordinate().z + else + return self.CargoObject:GetCoordinate().z + end + end + + --- Get the heading of the cargo. + -- @param #CARGO self + -- @return #number + function CARGO:GetHeading() + if self:IsLoaded() then + return self.CargoCarrier:GetHeading() + else + return self.CargoObject:GetHeading() + end + end + --- Check if the cargo can be Slingloaded. -- @param #CARGO self @@ -431,7 +465,16 @@ do -- CARGO function CARGO:IsBoarding() return self:Is( "Boarding" ) end + + --- Check if cargo is unboarding. + -- @param #CARGO self + -- @return #boolean true if unboarding + function CARGO:IsUnboarding() + return self:Is( "UnBoarding" ) + end + + --- Check if cargo is alive. -- @param #CARGO self -- @return #boolean true if unloaded diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index d71c81a16..c4712c10e 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -53,47 +53,47 @@ do -- CARGO_GROUP self:F( { Type, Name, LoadRadius } ) self.CargoSet = SET_CARGO:New() + self.CargoGroup = CargoGroup + self.Grouped = true + self.CargoUnitTemplate = {} self:SetDeployed( false ) local WeightGroup = 0 + self.CargoGroup:Destroy() + local GroupName = CargoGroup:GetName() - local CargoName = GroupName:match("(.*)~CARGO") or GroupName + self.CargoName = GroupName:match("(.*)~CARGO") or GroupName self.CargoTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplate( GroupName ) ) + + local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate ) + GroupTemplate.name = self.CargoName .. "#CARGO" + GroupTemplate.groupId = nil - CargoGroup:Destroy() + GroupTemplate.units = {} - -- We iterate through the group template and for each unit in the template, we create a new group with one unit. for UnitID, UnitTemplate in pairs( self.CargoTemplate.units ) do - - local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate ) - --local GroupName = env.getValueDictByKey( GroupTemplate.name ) - - -- We create a new group object with one unit... - -- First we prepare the template... - GroupTemplate.name = CargoName .. "#CARGO#" .. UnitID - GroupTemplate.groupId = nil - GroupTemplate.units = {} - GroupTemplate.units[1] = UnitTemplate - local UnitName = UnitTemplate.name .. "#CARGO" - GroupTemplate.units[1].name = UnitTemplate.name .. "#CARGO" - - - -- Then we register the new group in the database - local CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID) - - -- Now we spawn the new group based on the template created. - _DATABASE:Spawn( GroupTemplate ) + UnitTemplate.name = UnitTemplate.name .. "#CARGO" + local CargoUnitName = UnitTemplate.name + self.CargoUnitTemplate[CargoUnitName] = UnitTemplate + + GroupTemplate.units[#GroupTemplate.units+1] = self.CargoUnitTemplate[CargoUnitName] + GroupTemplate.units[#GroupTemplate.units].unitId = nil -- And we register the spawned unit as part of the CargoSet. - local Unit = UNIT:FindByName( UnitName ) + local Unit = UNIT:Register( CargoUnitName ) --local WeightUnit = Unit:GetDesc().massEmpty --WeightGroup = WeightGroup + WeightUnit - local CargoUnit = CARGO_UNIT:New( Unit, Type, UnitName, 10 ) - self.CargoSet:Add( UnitName, CargoUnit ) + local CargoUnit = CARGO_UNIT:New( Unit, Type, CargoUnitName, 10 ) + self.CargoSet:Add( CargoUnitName, CargoUnit ) end - + + -- Then we register the new group in the database + self.CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID) + + -- Now we spawn the new group based on the template created. + _DATABASE:Spawn( GroupTemplate ) self:SetWeight( WeightGroup ) self.CargoLimit = 10 @@ -112,13 +112,108 @@ do -- CARGO_GROUP return self end + + --- Ungroup the cargo group into individual groups with one unit. + -- This is required because by default a group will move in formation and this is really an issue for group control. + -- Therefore this method is made to be able to ungroup a group. + -- This works for ground only groups. + -- @param #CARGO_GROUP self + function CARGO_GROUP:Ungroup() + + if self.Grouped == true then + + self.Grouped = false + + self.CargoGroup:Destroy() + + for CargoUnitName, CargoUnit in pairs( self.CargoSet:GetSet() ) do + local CargoUnit = CargoUnit -- Cargo.CargoUnit#CARGO_UNIT + + if CargoUnit:IsUnLoaded() then + local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate ) + --local GroupName = env.getValueDictByKey( GroupTemplate.name ) + + -- We create a new group object with one unit... + -- First we prepare the template... + GroupTemplate.name = self.CargoName .. "#CARGO#" .. CargoUnitName + GroupTemplate.groupId = nil + + if CargoUnit:IsUnLoaded() then + GroupTemplate.units = {} + GroupTemplate.units[1] = self.CargoUnitTemplate[CargoUnitName] + GroupTemplate.units[#GroupTemplate.units].unitId = nil + GroupTemplate.units[#GroupTemplate.units].x = CargoUnit:GetX() + GroupTemplate.units[#GroupTemplate.units].y = CargoUnit:GetY() + GroupTemplate.units[#GroupTemplate.units].heading = CargoUnit:GetHeading() + end + + + -- Then we register the new group in the database + local CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID) + + -- Now we spawn the new group based on the template created. + _DATABASE:Spawn( GroupTemplate ) + end + end + end + + end + + --- Regroup the cargo group into one group with multiple unit. + -- This is required because by default a group will move in formation and this is really an issue for group control. + -- Therefore this method is made to be able to regroup a group. + -- This works for ground only groups. + -- @param #CARGO_GROUP self + function CARGO_GROUP:Regroup() + + self:F("Regroup") + + if self.Grouped == false then + + self.Grouped = true + + local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate ) + GroupTemplate.name = self.CargoName .. "#CARGO" + GroupTemplate.groupId = nil + GroupTemplate.units = {} + + for CargoUnitName, CargoUnit in pairs( self.CargoSet:GetSet() ) do + local CargoUnit = CargoUnit -- Cargo.CargoUnit#CARGO_UNIT + + self:F( { CargoUnit:GetName(), UnLoaded = CargoUnit:IsUnLoaded() } ) + + if CargoUnit:IsUnLoaded() then + + CargoUnit.CargoObject:Destroy() + + GroupTemplate.units[#GroupTemplate.units+1] = self.CargoUnitTemplate[CargoUnitName] + GroupTemplate.units[#GroupTemplate.units].unitId = nil + GroupTemplate.units[#GroupTemplate.units].x = CargoUnit:GetX() + GroupTemplate.units[#GroupTemplate.units].y = CargoUnit:GetY() + GroupTemplate.units[#GroupTemplate.units].heading = CargoUnit:GetHeading() + end + end + + -- Then we register the new group in the database + self.CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID) + + self:F( { "Regroup", GroupTemplate } ) + + -- Now we spawn the new group based on the template created. + _DATABASE:Spawn( GroupTemplate ) + end + + end + + --- @param #CARGO_GROUP self -- @param Core.Event#EVENTDATA EventData function CARGO_GROUP:OnEventCargoDead( EventData ) + self:I( EventData ) - local Destroyed = false + local Destroyed = false - if self:IsDestroyed() or self:IsUnLoaded() or self:IsBoarding() then + if self:IsDestroyed() or self:IsUnLoaded() or self:IsBoarding() or self:IsUnboarding() then Destroyed = true for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do local Cargo = CargoData -- #CARGO @@ -171,10 +266,10 @@ do -- CARGO_GROUP --- Enter Loaded State. -- @param #CARGO_GROUP self - -- @param Wrapper.Unit#UNIT CargoCarrier -- @param #string Event -- @param #string From -- @param #string To + -- @param Wrapper.Unit#UNIT CargoCarrier function CARGO_GROUP:onenterLoaded( From, Event, To, CargoCarrier, ... ) --self:F( { From, Event, To, CargoCarrier, ...} ) @@ -187,6 +282,7 @@ do -- CARGO_GROUP --self.CargoObject:Destroy() self.CargoCarrier = CargoCarrier + self.CargoCarrier:AddCargo( self ) end @@ -244,14 +340,6 @@ do -- CARGO_GROUP end end - - --- Get the amount of cargo units in the group. - -- @param #CARGO_GROUP self - -- @return #CARGO_GROUP - function CARGO_GROUP:GetCount() - return self.CargoSet:Count() - end - --- Enter UnBoarding State. -- @param #CARGO_GROUP self @@ -274,10 +362,12 @@ do -- CARGO_GROUP -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 self.CargoSet:ForEach( + --- @param Cargo.Cargo#CARGO Cargo function( Cargo, NearRadius ) - - Cargo:__UnBoard( Timer, ToPointVec2, NearRadius ) - Timer = Timer + 3 + if not Cargo:IsDestroyed() then + Cargo:__UnBoard( Timer, ToPointVec2, NearRadius ) + Timer = Timer + 3 + end end, { NearRadius } ) @@ -307,7 +397,7 @@ do -- CARGO_GROUP -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do - self:T( Cargo.current ) + self:T( { Cargo:GetName(), Cargo.current } ) if not Cargo:is( "UnLoaded" ) and not Cargo:IsDestroyed() then UnBoarded = false end @@ -362,6 +452,9 @@ do -- CARGO_GROUP end + self.CargoCarrier:RemoveCargo( self ) + self.CargoCarrier = nil + end @@ -371,8 +464,8 @@ do -- CARGO_GROUP -- @return #nil There is no valid Cargo in the CargoGroup. function CARGO_GROUP:GetCoordinate() self:F() - - local Cargo = self.CargoSet:GetFirst() + + local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO if Cargo then return Cargo.CargoObject:GetCoordinate() @@ -387,26 +480,46 @@ do -- CARGO_GROUP -- @return #boolean false if the CargoGroup is dead. function CARGO_GROUP:IsAlive() - local Alive = true - - -- For each Cargo within the CargoSet, check if the Cargo is Alive. - -- When the Cargo is Loaded, the Cargo is in the CargoCarrier, so we check if the CargoCarrier is alive. - -- When the Cargo is not Loaded, the Cargo is the CargoObject, so we check if the CargoObject is alive. - self.CargoSet:ForEach( - function( Cargo ) - if self:IsLoaded() then - Alive = Alive == true and Cargo.CargoCarrier:IsAlive() - else - Alive = Alive == true and Cargo.CargoObject:IsAlive() - end - end - ) - - return Alive + local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO + return Cargo ~= nil end + --- Get the first alive Cargo Unit of the Cargo Group. + -- @param #CARGO_GROUP self + -- @return #CARGO_GROUP + function CARGO_GROUP:GetFirstAlive() + + local CargoFirstAlive = nil + + for _, Cargo in pairs( self.CargoSet:GetSet() ) do + if not Cargo:IsDestroyed() then + CargoFirstAlive = Cargo + break + end + end + return CargoFirstAlive + end + + + --- Get the amount of cargo units in the group. + -- @param #CARGO_GROUP self + -- @return #CARGO_GROUP + function CARGO_GROUP:GetCount() + return self.CargoSet:Count() + end + + + --- Get the amount of cargo units in the group. + -- @param #CARGO_GROUP self + -- @return #CARGO_GROUP + function CARGO_GROUP:GetGroup( Cargo ) + local Cargo = Cargo or self:GetFirstAlive() -- Cargo.Cargo#CARGO + return Cargo.CargoObject:GetGroup() + end + + --- Route Cargo to Coordinate and randomize locations. -- @param #CARGO_GROUP self -- @param Core.Point#COORDINATE Coordinate @@ -430,12 +543,16 @@ do -- CARGO_GROUP -- @return #boolean The Cargo is near to the Carrier. -- @return #nil The Cargo is not near to the Carrier. function CARGO_GROUP:IsNear( CargoCarrier, NearRadius ) - --self:F( {NearRadius = NearRadius } ) + self:F( {NearRadius = NearRadius } ) - local Cargo = self.CargoSet:GetFirst() -- #CARGO - - if Cargo then - return Cargo:IsNear( CargoCarrier:GetCoordinate(), NearRadius ) + for _, Cargo in pairs( self.CargoSet:GetSet() ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + if Cargo:IsAlive() then + if Cargo:IsNear( CargoCarrier:GetCoordinate(), NearRadius ) then + self:F( "Near" ) + return true + end + end end return nil @@ -448,7 +565,7 @@ do -- CARGO_GROUP function CARGO_GROUP:IsInLoadRadius( Coordinate ) --self:F( { Coordinate } ) - local Cargo = self.CargoSet:GetFirst() -- #CARGO + local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO if Cargo then local Distance = 0 @@ -478,7 +595,7 @@ do -- CARGO_GROUP function CARGO_GROUP:IsInReportRadius( Coordinate ) --self:F( { Coordinate } ) - local Cargo = self.CargoSet:GetFirst() -- #CARGO + local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO if Cargo then self:F( { Cargo } ) diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index d28d113a7..85b6ea6cc 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -146,13 +146,13 @@ do -- CARGO_UNIT local Distance = 5 if From == "UnBoarding" then - if self:IsNear( ToPointVec2, NearRadius ) then + --if self:IsNear( ToPointVec2, NearRadius ) then return true - else + --else - self:__UnBoarding( 1, ToPointVec2, NearRadius ) - end - return false + --self:__UnBoarding( 1, ToPointVec2, NearRadius ) + --end + --return false end end @@ -258,14 +258,17 @@ do -- CARGO_UNIT local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( Distance, CargoDeployHeading ) - + + -- Set the CargoObject to state Green to ensure it is boarding! + self.CargoObject:OptionAlarmStateGreen() + local Points = {} local PointStartVec2 = self.CargoObject:GetPointVec2() Points[#Points+1] = PointStartVec2:WaypointGround( Speed ) Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed ) - + local TaskRoute = self.CargoObject:TaskRoute( Points ) self.CargoObject:SetTask( TaskRoute, 2 ) self:__Boarding( -1, CargoCarrier, NearRadius ) @@ -295,7 +298,7 @@ do -- CARGO_UNIT else self:__Boarding( -1, CargoCarrier, NearRadius, ... ) self.RunCount = self.RunCount + 1 - if self.RunCount >= 60 then + if self.RunCount >= 40 then self.RunCount = 0 local Speed = 90 local Angle = 180 @@ -308,12 +311,15 @@ do -- CARGO_UNIT local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) local CargoDeployPointVec2 = CargoCarrierPointVec2:Translate( Distance, CargoDeployHeading ) + -- Set the CargoObject to state Green to ensure it is boarding! + self.CargoObject:OptionAlarmStateGreen() + local Points = {} local PointStartVec2 = self.CargoObject:GetPointVec2() - Points[#Points+1] = PointStartVec2:WaypointGround( Speed ) - Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed ) + Points[#Points+1] = PointStartVec2:WaypointGround( Speed, "Off road" ) + Points[#Points+1] = CargoDeployPointVec2:WaypointGround( Speed, "Off road" ) local TaskRoute = self.CargoObject:TaskRoute( Points ) self.CargoObject:SetTask( TaskRoute, 0.2 ) @@ -367,6 +373,7 @@ do -- CARGO_UNIT if self.CargoObject then self:T("Destroying") self.CargoObject:Destroy() + --self.CargoObject:ReSpawnAt( COORDINATE:NewFromVec2( {x=0,y=0} ), 0 ) end end diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 15d9cf436..c49f02994 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -893,11 +893,13 @@ do -- COORDINATE function COORDINATE:WaypointGround( Speed, Formation ) self:F2( { Formation, Speed } ) + local RoutePoint = {} RoutePoint.x = self.x RoutePoint.y = self.z RoutePoint.action = Formation or "" + --RoutePoint.formation_template = Formation and "" or nil RoutePoint.speed = ( Speed or 20 ) / 3.6 diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index c63a94af8..6f55ff3d6 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -359,7 +359,7 @@ function CONTROLLABLE:SetTask( DCSTask, WaitTime ) local Controller = self:_GetController() Controller:setTask( DCSTask ) else - BASE:E( DCSControllableName .. " is not alive anymore. Cannot set DCSTask " .. DCSTask ) + BASE:E( { DCSControllableName .. " is not alive anymore.", DCSTask = DCSTask } ) end end @@ -1964,11 +1964,46 @@ do -- Route methods end -- Route controllable to destination. - self:Route(route, DelaySeconds) + self:Route( route, DelaySeconds ) return self end + + --- Make a task for a GROUND Controllable to drive towards a specific point using (only) roads. + -- @param #CONTROLLABLE self + -- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to. + -- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h. + -- @return Task + function CONTROLLABLE:TaskGroundOnRoad( ToCoordinate, Speed ) + + -- Current coordinate. + local FromCoordinate = self:GetCoordinate() + + -- Formation is set to on road. + local Formation="On Road" + + -- Path on road from current position to destination coordinate. + local path=FromCoordinate:GetPathOnRoad( ToCoordinate ) + + -- Route, ground waypoints along roads. + local Route = {} + table.insert( Route, FromCoordinate:WaypointGround( Speed, Formation ) ) + + -- Convert coordinates to ground waypoints and insert into table. + for _, coord in ipairs(path) do + table.insert( Route, coord:WaypointGround( Speed, Formation ) ) + end + + -- Add the final coordinate because the final coordinate in path is last point on road. + local dist=ToCoordinate:Get2DDistance(path[#path]) + if dist>10 then + table.insert( Route, ToCoordinate:WaypointGround( Speed, "Vee" ) ) + end + + return Route + end + --- Make the AIR Controllable fly towards a specific point. -- @param #CONTROLLABLE self diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 2a6605bb9..d9308e8bb 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -808,6 +808,15 @@ function POSITIONABLE:AddCargo( Cargo ) return self end +--- Get all contained cargo. +-- @param #POSITIONABLE self +-- @return #POSITIONABLE +function POSITIONABLE:GetCargo() + return self.__.Cargo +end + + + --- Remove cargo. -- @param #POSITIONABLE self -- @param Core.Cargo#CARGO Cargo From 531c1d7e9018ef5d9b968d2f16e69f1aeb94fd29 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 3 May 2018 00:24:23 +0200 Subject: [PATCH 073/420] ARTY v0.6 (WIP) Added documentation Added user FSM functions. Adjusted FSM (untested) Added task route function. --- .../Moose/Functional/Artillery.lua | 714 +++++++++++++++--- 1 file changed, 604 insertions(+), 110 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 10c5282eb..d54830eb0 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -1,12 +1,20 @@ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- **Functional** - Control artillery units. +--- **Functional** - (R2.4) Control artillery units. +-- +-- === -- -- ![Banner Image](..\Presentations\ARTY\Artillery_Main.png) -- -- ==== -- --- The ARTY class can be used to easily assign targets for artillery units. Multiple targets can be assigned. +-- The ARTY class can be used to easily assign targets for artillery units. -- +-- ## Features: +-- +-- * Multiple targets can be assigned. No restriction on number of targets. +-- * Targets can be given a priority. Engagement of targets is executed a according to their priority. +-- * Engagements can be scheduled, i.e. will be executed at a certain time of the day. +-- * Special weapon types can be selected. -- -- ==== -- @@ -24,13 +32,12 @@ -- -- ### Author: **[funkyfranky](https://forums.eagle.ru/member.php?u=115026)** -- --- ### Contributions: **Sven van de Velde ([FlightControl](https://forums.eagle.ru/member.php?u=89536))** +-- ### Contributions: **[FlightControl](https://forums.eagle.ru/member.php?u=89536)** -- -- ==== -- @module Arty ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - --- ARTY class -- @type ARTY -- @field #string ClassName Name of the class. @@ -45,7 +52,7 @@ -- @field Core.Scheduler#SCHEDULER scheduler Scheduler object handling various timed functions. -- @field #number SchedIDTargetQueue Scheduler ID for updating the target queue and calling OpenFire event. -- @field #number TargetQueueUpdate Interval between updates of the target queue. --- @field #number SchedIDCheckRearmed Scheduler ID responsible for checking whether reaming of the ARTY group is complete. +-- @field #number SchedIDCheckRearmed Scheduler ID responsible for checking whether rearming of the ARTY group is complete. -- @field #number SchedIDCheckShooting Scheduler ID for checking whether a group startet firing within a certain time after the fire at point task was assigned. -- @field #number WaitForShotTime Max time in seconds to wait until fist shot event occurs after target is assigned. If time is passed without shot, the target is deleted. Default is 300 seconds. -- @field #number SchedIDStatusReport Scheduler ID for status report messages. The scheduler is only launched in debug mode. @@ -57,6 +64,8 @@ -- @field #number Speed Max speed of ARTY group. -- @field Wrapper.Unit#UNIT RearmingUnit Unit designated to rearm the ARTY group. -- @field Wrapper.Point#COORDINATE RearmingUnitCoord Initial coordinates of the rearming unit. After rearming complete, the unit will return to this position. +-- @field Wrapper.Point#COORDINATE RearmingPlaceCoord Coordinates of the rearming place. If the place is more than 100 m away from the ARTY group, the group will go there. +-- @field Wrapper.Point#COORDINATE InitialCoord Initial coordinates of the ARTY group. -- @field #boolean report Arty group sends messages about their current state or target to its coaliton. -- @field #table ammoshells Table holding names of the shell types which are included when counting the ammo. Default is {"weapons.shells"} which include most shells. -- @field #table ammorockets Table holding names of the rocket types which are included when counting the ammo. Default is {"weapons.nurs"} which includes most unguided rockets. @@ -65,18 +74,152 @@ -- @field #number minrange Minimum firing range in kilometers. Targets closer than this distance are not engaged. Default 0 km. -- @field #number maxrange Maximum firing range in kilometers. Targets further away than this distance are not engaged. Default 10000 km. -- @extends Core.Fsm#FSM_CONTROLLABLE --- ---# ARTY class, extends @{Core.Fsm#FSM_CONTROLLABLE} --- Artillery class.. +-- +-- The ARTY class enables mission designers easily to assign targets for artillery units. Since the implementation is based on a Finite State Model (FSM), the mission designer can +-- interact with the process at certain events or states. -- --- ## Target aquisition... +-- A new ARTY object can be created with the @{#ARTY.New}(*group*) contructor. +-- The parameter *group* has to be a MOOSE Group object and defines ARTY group. +-- +-- The ARTY FSM process can be started by the @{#ARTY.Start}() command. +-- +-- ## The ARTY Process -- -- ![Process](..\Presentations\ARTY\Artillery_Process.png) -- --- The arty process can be described as follows. +-- After the FMS process is started the ARTY group will be in the state **CombatReady**. Once a target is assigned the **OpenFire** event will be triggered and the group starts +-- firing. At this point the group in in the state **Firing**. -- --- ### Submenu +-- When the defined number of shots has been fired on the current target the event **CeaseFire** is triggered. The group will stop firing and go back to the state **CombatReady**. +-- If another target is defined (or multiple engagements of the same target), the cycle starts anew. +-- +-- When the ARTY group runs out of ammunition, the event **Winchester** is triggered and the group enters the state **OutOfAmmo**. +-- In this state, the group is unable to engage further targets. +-- +-- With the @{#ARTY.SetRearmingUnit}(*unit*) command, a special unit can be defined to rearm the ARTY group. If this unit has been assigned and the group has entered the state +-- **OutOfAmmo** the event **Rearm** is triggered followed by a transition to the state **Rearming**. +-- If the rearming unit is less than 100 meters away from the ARTY group, the rearming process starts. If the rearming unit is more than 100 meters away from the ARTY unit, the +-- rearming unit is routed to a point 20 to 100 m from the ARTY group. +-- +-- Once the rearming is complete, the **Rearmed** event is triggered and the group enters the state **CombatReady**. At this point targeted can be engaged again. +-- +-- ## Assigning Targets +-- Assigning targets is a central point of the ARTY class. Multiple targets can be assigned simultanioulsly and are put into a queue. +-- Of course, targets can be added at any time during the mission. For example, once they are detected by a reconnaissance unit. +-- +-- In order to add a target, the function @{#ARTY.AssignTargetCoord}(*coord*, *prio*, *radius*, *nshells*, *maxengage*, *time*, *weapontype*, *name*) has to be used. +-- Only the first parameter *coord* is mandatory while all remaining parameters are all optional. +-- +-- ### Parameters: +-- +-- * *coord*: Coordinates of the target, given as @{Point#COORDINATE} object. +-- * *prio*: Priority of the target. This a number between 1 (high prio) and 100 (low prio). Targets with higher priority are engaged before targets with lower priority. +-- * *radius*: Radius in meters which defines the area the ARTY group will attempt to be hitting. Default is 100 meters. +-- * *nshells*: Number of shots (shells, rockets, missiles) fired by the group at each engagement of a target. Default is 5. +-- * *maxengage*: Number of times a target is engaged. +-- * *time*: Time of day the engagement is schedule in the format "hh:mm:ss" for hh=hours, mm=minutes, ss=seconds. +-- For example "10:15:35". In the case the attack will be executed at a quarter past ten in the morning at the day the mission started. +-- If the engagement should start on the following day the format can be specified as "10:15:35+1", where the +1 denots the following day. +-- This is useful for longer running missions or if the mission starts at 23:00 hours and the attack should be scheduled at 01:00 hours on the following day. +-- Of course, later days are also possible by appending "+2", "+3", etc. +-- **Note** that the time has to be given as a string. So the enclosing quotation marks "" are important. +-- * *weapontype*: Specified the weapon type that should be used for this attack if the ARTY group has multiple weapons to engage the target. +-- For example, this is useful for naval units which carry a bigger arsenal (cannons and missiles). Default is Auto, i.e. DCS logic selects the appropriate weapon type. +-- *name*: A special name can be defined for this target. Default name are the coordinates of the target in LL DMS format. If a name is already given for another target +-- or the same target should be attacked two or more times with different parameters a suffix "#01", "#02", "#03" is automatically appended to the specified name. +-- +-- ## Target Queue +-- In case, multiple targets have been defined, it is important to understand how the target queue works. +-- +-- Here, the important parameters are the priority *prio*, the number of engagements *maxengage* and the scheduled *time* as described above. +-- +-- For example, we have assigned two targets one with *prio*=10 and the other with *prio*=50 and both targets should be engaged three times (*maxengage*=3). +-- Let's first consider the case that none of the targets is scheduled to be executed at a certain time (*time*=nil). +-- The ARTY group will first engage the target with higher priority (*prio*=10). After the engagement is finished, the target with lower priority is attacked. +-- This is because the target with lower prio has been attacked one time less. After the attack on the lower priority task is finished and both targets +-- have been engaged equally often, the target with the higher priority is engaged again. This coninues until a target has engaged three times. +-- Once the maximum number of engagements is reached, the target is deleted from the queue. +-- +-- In other works, the queue is first sorted with respect to the number of engagements and targets with the same number of engagements are sorted with +-- respect to their priority. +-- +-- ### Timed Engagements +-- +-- As mentioned above, targets can be engaged at a specific time of the day via the *time* parameter. +-- +-- If the *time* parameter is specified for a target, the first engagement of that target will happen at that time of the day and not before. +-- This also applies when multiple engagements are requested via the *maxengage* parameter. The first attack will not happen before the specifed time. +-- When that timed attack is finished, the *time* parameter is deleted and the remaining engagements are carried out in the same manner as for untimed targets (described above). +-- +-- Of course, it can happen that a scheduled task should be executed at a time, when another target is already under attack. +-- If the priority of the target is higher than the priority of the current target, then the current attack is cancelled and the engagement of the target with the higher +-- priority is started. +-- +-- By contrast, if the current target has a higher priority than the target scheduled at that time, the current attack is finished before the scheduled attack is started. +-- +-- ## Determining the Amount of Ammo +-- +-- In order to determin when a unit is out of ammo and possible initiate the rearming process it is necessary to know which types of weapons have to be counted. +-- For most artillery unit types, this is simple because they only have one type of weapon and hence ammunition. +-- +-- However, there are more complex scenarios. For example, naval units carry a big arsenal of different ammunition types ranging from various cannon shell types +-- over surface-to-air missiles to cruise missiles. Obviously, not all of these ammo types can be employed for artillery tasks. +-- +-- Unfortunately, there is no easy way to count only those ammo types useable as artillery. Therefore, to keep the implementation general the user +-- can specify the names of the ammo types by the following functions: +-- +-- * @{#ARTY.SetShellTypes}(*tableofnames*): Defines the ammo types for unguided cannons. Default is *tableofnames*={"weapons.shells"}, i.e. **all** types of shells are counted. +-- * @{#ARTY.SetRocketTypes}(*tableofnames*): Defines the ammo types of unguided rockets. Default is *tableofnames*={"weapons.nurs"}, i.e. **all** types of rockets are counted. +-- * @{#ARTY.SetMissileTypes}(*tableofnames*): Defines the ammo types of guided missiles. Default is *tableofnames*={"weapons.missiles"}, i.e. **all** types of missiles are counted. +-- +-- **Note** that the default parameters "weapons.shells", "weapons.nurs", "weapons.missiles" **should in priciple** capture all the corresponding ammo types. +-- However, the logic searches for the string "weapon.missies" in the ammo type. Especially for missiles, this string is often not contained in the ammo type descriptor. +-- +-- One way to determin which types of ammo the unit carries, one can use the debug mode of the arty class via @{#ARTY.SetDebugON}(). +-- In debug mode, the all ammo types of the group are printed to the monitor as message and can be found in the DCS.log file. +-- +-- ## Empoying Selected Weapons +-- +-- If an ARTY group carries multiple weapons, which can be used for artillery task, a certain weapon type can be selected to attack the target. +-- This is done via the *weapontype* parameter of the @{#ARTY.AssignTargetCoord}(..., *weapontype*, ...) function. +-- +-- The enumerator @{#ARTY.WeaponType} has been defined to select a certain weapon type. Supported values are: +-- +-- * @{#ARTY.WeaponType}.Auto: Automatic weapon selection by the DCS logic. This is the default setting. +-- * @{#ARTY.WeaponType}.Cannon: Only cannons are used during the attack. Corresponding ammo type are shells and can be defined by @{#ARTY.SetShellTypes}. +-- * @{#ARTY.WeaponType}.Rockets: Only unguided are used during the attack. Corresponding ammo type are rockets/nurs and can be defined by @{#ARTY.SetRocketTypes}. +-- * @{#ARTY.WeaponType}.UnguidedAny: Any unguided weapon (cannons or rockes) will be used. +-- * @{#ARTY.WeaponType}.GuidedMissile: Any guided missiles are used during the attack. Corresponding ammo type are missiles and can be defined by @{#ARTY.SetMissileTypes}. +-- * @{#ARTY.WeaponType}.CruiseMissile: Only cruise missiles are used during the attack. Corresponding ammo type are missiles and can be defined by @{#ARTY.SetMissileTypes}. +-- +-- ## Fine Tuning +-- +-- The mission designer has a few options to tailor the ARTY object according to his needs. +-- +-- * @{#ARTY.RemoveAllTargets}() removes all targets from the target queue. +-- * @{#ARTY.RemoveTarget}(*name*) deletes the target with *name* from the target queue. +-- * @{#ARTY.SetMaxFiringRange}(*range*) defines the maximum firing range. Targets further away than this distance are not engaged. +-- * @{#ARTY.SetMinFiringRange}(*range*) defines the minimum firing range. Targets closer than this distance are not engaged. +-- * @{#ARTY.SetRearmingUnit}(*unit*) sets the unit resposible for rearming of the ARTY group once it is out of ammo. +-- * @{#ARTY.SetReportON}() and @{#ARTY.SetReportOFF}() can be used to enable/disable status reports of the ARTY group send to all coalition members. +-- * @{#ARTY.SetTargetQueueUpdateInterval}(*interval*) sets the interval (in seconds) at which the target queue is updated. Default is every 5 seconds. +-- * @{#ARTY.SetWaitForShotTime}(*waittime*) sets the time after which a target is deleted from the queue if no shooting event occured after the target engagement started. +-- Default is 300 seconds. Note that this can for example happen, when the assigned target is out of range. +-- * @{#ARTY.SetDebugON}() and @{#ARTY.SetDebugOFF}() can be used to enable/disable the debug mode. +-- +-- ## Examples +-- +-- ### Assigning Multiple Targets +-- This basic example illustrates how to assign multiple targets. +-- +-- ### Scheduled Engagements +-- This example shows how to execute an engagement at a certain time. +-- +-- ### Specific Weapons +-- This example demonstrates how to use specific weapons during an engagement. +-- -- -- @field #ARTY ARTY={ @@ -103,6 +246,8 @@ ARTY={ IsArtillery=nil, RearmingUnit=nil, RearmingUnitCoord=nil, + RearmingPlaceCoord=nil, + InitialCoord=nil, report=true, ammoshells={"weapons.shells"}, ammorockets={"weapons.nurs"}, @@ -116,10 +261,9 @@ ARTY={ -- @list WeaponType ARTY.WeaponType={ Auto=1073741822, + Cannon=805306368, + Rockets=30720, UnguidedAny=805339120, - UnguidedCannon=805306368, - UnguidedRockets=30720, - GuidedAny=268402702, GuidedMissile=268402688, CruiseMissile=2097152, } @@ -128,26 +272,26 @@ ARTY.WeaponType={ -- @field #string id ARTY.id="ARTY | " ---- Range script version. +--- Arty script version. -- @field #number version -ARTY.version="0.5.0" +ARTY.version="0.6.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO list: -- DONE: Delete targets from queue user function. -- DONE: Delete entire target queue user function. --- TODO: Add weapon types. Done but needs improvements. +-- DONE: Add weapon types. Done but needs improvements. -- DONE: Add user defined rearm weapon types. --- TODO: Check if target is in range. Maybe this requires a data base with the ranges of all arty units. Pfff... --- TODO: Make ARTY move to reaming position. --- TODO: Check that right reaming vehicle is specified. Blue M818, Red Ural-375. Are there more? --- TODO: Check if ARTY group is still alive. +-- DONE: Check if target is in range. Maybe this requires a data base with the ranges of all arty units. +-- DONE: Make ARTY move to rearming position. +-- DONE: Check that right rearming vehicle is specified. Blue M818, Red Ural-375. Are there more? +-- DONE: Check if ARTY group is still alive. -- DONE: Handle dead events. -- DONE: Abort firing task if no shooting event occured with 5(?) minutes. Something went wrong then. Min/max range for example. -- DONE: Improve assigned time for engagement. Next day? --- TODO: Improve documentation. --- TODO: Add pseudo user transitions. OnAfter... +-- DONE: Improve documentation. +-- DONE: Add pseudo user transitions. OnAfter... ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -179,6 +323,9 @@ function ARTY:New(group) -- Set the controllable for the FSM. self:SetControllable(group) + -- Set the initial coordinates of the ARTY group. + self.InitialCoord=group:GetCoordinate() + -- Create scheduler object. self.scheduler=SCHEDULER:New(self) @@ -211,13 +358,177 @@ function ARTY:New(group) -- Transitions self:AddTransition("*", "Start", "CombatReady") self:AddTransition("CombatReady", "OpenFire", "Firing") - self:AddTransition("Firing", "OpenFire", "Firing") -- Other target assigned + self:AddTransition("CombatReady", "Winchester", "OutOfAmmo") + self:AddTransition("Firing", "OpenFire", "Firing") self:AddTransition("Firing", "CeaseFire", "CombatReady") - self:AddTransition("*", "Winchester", "OutOfAmmo") - self:AddTransition("*", "Rearm", "Rearming") + self:AddTransition("OutOfAmmo", "Rearm", "Rearming") self:AddTransition("Rearming", "Rearmed", "CombatReady") + self:AddTransition("CombatReady", "Move", "Moving") + self:AddTransition("Moving", "Arrived", "CombatReady") self:AddTransition("*", "Dead", "*") + --- User function for OnBefore "OpenFire" event. + -- @function [parent=#ARTY] OnBeforeOpenFire + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #table target Array holding the target info. + -- @return #boolean If true, allow transition to OnAfterOpenFire. + + --- User function for OnAfter "OpenFire" event. + -- @function [parent=#ARTY] OnAfterOpenFire + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #table target Array holding the target info. + + + --- User function for OnBefore "CeaseFire" event. + -- @function [parent=#ARTY] OnBeforeCeaseFire + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #table target Array holding the target info. + -- @return #boolean If true, allow transition to OnAfterCeaseFire. + + --- User function for OnAfter "CeaseFire" event. + -- @function [parent=#ARTY] OnAfterCeaseFire + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #table target Array holding the target info. + + + --- User function for OnBefore "Winchester" event. + -- @function [parent=#ARTY] OnBeforeWinchester + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @return #boolean If true, allow transition to OnAfterWinchester. + + --- User function for OnAfter "Winchester" event. + -- @function [parent=#ARTY] OnAfterWinchester + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + + --- User function for OnBefore "Rearm" event. + -- @function [parent=#ARTY] OnBeforeRearm + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @return #boolean If true, allow transition to OnAfterRearm. + + --- User function for OnAfter "Rearm" event. + -- @function [parent=#ARTY] OnAfterRearm + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + + --- User function for OnBefore "Rearmed" event. + -- @function [parent=#ARTY] OnBeforeRearmed + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @return #boolean If true, allow transition to OnAfterRearmed. + + --- User function for OnAfter "Rearmed" event. + -- @function [parent=#ARTY] OnAfterRearmed + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + + --- User function for OnBefore "Start" event. + -- @function [parent=#ARTY] OnBeforeStart + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @return #boolean If true, allow transition to OnAfterStart. + + --- User function for OnAfter "Start" event. + -- @function [parent=#ARTY] OnAfterStart + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + + --- User function for OnBefore "Dead" event. + -- @function [parent=#ARTY] OnBeforeDead + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @return #boolean If true, allow transition to OnAfterDead. + + --- User function for OnAfter "Dead" event. + -- @function [parent=#ARTY] OnAfterDead + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + + --- User function for OnEnter "CombatReady" state. + -- @function [parent=#ARTY] OnEnterCombatReady + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- User function for OnEnter "Firing" state. + -- @function [parent=#ARTY] OnEnterFiring + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- User function for OnEnter "OutOfAmmo" state. + -- @function [parent=#ARTY] OnEnterOutOfAmmo + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- User function for OnEnter "Rearming" state. + -- @function [parent=#ARTY] OnEnterRearming + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + return self end @@ -306,6 +617,14 @@ function ARTY:SetRearmingUnit(unit) self.RearmingUnit=unit end +--- Defines the rearming place of the ARTY group. If the place is too far away from the ARTY group it will be routed to the place. +-- @param #ARTY self +-- @param Wrapper.Point#COORDINATE coord Coordinates of the rearming place. +function ARTY:SetRearmingPlace(coord) + self:F({coord=coord}) + self.RearmingPlaceCoord=coord +end + --- Report messages of ARTY group turned on. This is the default. -- @param #ARTY self function ARTY:SetReportON() @@ -318,6 +637,18 @@ function ARTY:SetReportOFF() self.report=false end +--- Turn debug mode on. Information is printed to screen. +-- @param #ARTY self +function ARTY:SetDebugON() + self.Debug=true +end + +--- Turn debug mode off. This is the default setting. +-- @param #ARTY self +function ARTY:SetDebugOFF() + self.Debug=false +end + --- Set target queue update time interval. -- @param #ARTY self -- @param #number interval Time interval in seconds. Default is 5 seconds. @@ -407,13 +738,20 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("Type = %s\n", self.Type) text=text..string.format("Display Name = %s\n", self.DisplayName) text=text..string.format("Number of units = %d\n", self.IniGroupStrength) - text=text..string.format("Max Speed [km/h] = %d\n", self.Speed) - text=text..string.format("Min range [km] = %d\n", self.minrange/1000) - text=text..string.format("Max range [km] = %d\n", self.maxrange/1000) + text=text..string.format("Max Speed = %d km/h\n", self.Speed) + text=text..string.format("Min range = %d km\n", self.minrange/1000) + text=text..string.format("Max range = %d km\n", self.maxrange/1000) text=text..string.format("Total ammo count = %d\n", self.Nammo0) text=text..string.format("Number of shells = %d\n", self.Nshells0) text=text..string.format("Number of rockets = %d\n", self.Nrockets0) text=text..string.format("Number of missiles = %d\n", self.Nmissiles0) + if self.RearmingUnit then + text=text..string.format("Reaming unit = %s\n", self.RearmingUnit:GetName()) + end + if self.RearmingPlaceCoord then + local dist=self.InitialCoord:Get2DDistance(self.RearmingPlaceCoord) + text=text..string.format("Reaming coord dist. = %d m\n", dist) + end text=text..string.format("******************************************************\n") text=text..string.format("Targets:\n") for _, target in pairs(self.targets) do @@ -460,6 +798,7 @@ function ARTY:_StatusReport() -- Get Ammo. local Nammo, Nshells, Nrockets, Nmissiles=self:_GetAmmo(self.Controllable) + local Tnow=timer.getTime() local text=string.format("\n******************************************************\n") text=text..string.format("Status of ARTY = %s\n", self.Controllable:GetName()) @@ -469,7 +808,8 @@ function ARTY:_StatusReport() text=text..string.format("Number of rockets = %d\n", Nrockets) text=text..string.format("Number of missiles = %d\n", Nmissiles) if self.currentTarget then - text=text..string.format("Current Target = %s\n", tostring(self.currentTarget.name)) + text=text..string.format("Current Target = %s\n", tostring(self.currentTarget.name)) + text=text..string.format("Curr. Tgt assigned = %d\n", Tnow-self.currentTarget.Tassigned) else text=text..string.format("Current Target = %s\n", "none") end @@ -528,7 +868,9 @@ function ARTY:_OnEventShot(EventData) if _nammo==0 then - self:E(ARTY.id..string.format("Group %s completely out of ammo.", self.Controllable:GetName())) + self:T(ARTY.id..string.format("Group %s completely out of ammo.", self.Controllable:GetName())) + -- Cease fire first. + self:CeaseFire(self.currentTarget) self:Winchester() -- Current target is deallocated ==> return @@ -537,17 +879,17 @@ function ARTY:_OnEventShot(EventData) -- Weapon type name for current target. local _weapontype=self:_WeaponTypeName(self.currentTarget.weapontype) - self:E(ARTY.id..string.format("nammo=%d, nshells=%d, nrockets=%d, nmissiles=%d", _nammo, _nshells, _nrockets, _nmissiles)) - self:E(ARTY.id..string.format("Weapontype = %s", _weapontype)) + self:T(ARTY.id..string.format("nammo=%d, nshells=%d, nrockets=%d, nmissiles=%d", _nammo, _nshells, _nrockets, _nmissiles)) + self:T(ARTY.id..string.format("Weapontype = %s", _weapontype)) -- Special weapon type requested ==> Check if corresponding ammo is empty. - if self.currentTarget.weapontype==ARTY.WeaponType.UnguidedCannon and _nshells==0 then + if self.currentTarget.weapontype==ARTY.WeaponType.Cannon and _nshells==0 then self:T(ARTY.id.."Cannons requested but shells empty.") self:CeaseFire(self.currentTarget) return - elseif self.currentTarget.weapontype==ARTY.WeaponType.UnguidedRockets and _nrockets==0 then + elseif self.currentTarget.weapontype==ARTY.WeaponType.Rockets and _nrockets==0 then self:T(ARTY.id.."Rockets requested but rockets empty.") self:CeaseFire(self.currentTarget) @@ -559,9 +901,9 @@ function ARTY:_OnEventShot(EventData) self:CeaseFire(self.currentTarget) return - elseif self.currentTarget.weapontype==ARTY.WeaponType.CruiseMissile and _nmissiles==0 then + elseif (self.currentTarget.weapontype==ARTY.WeaponType.CruiseMissile or self.currentTarget.weapontype==ARTY.WeaponType.CruiseMissile) and _nmissiles==0 then - self:E(ARTY.id.."Cruise missiles requested and missiles empty.") + self:T(ARTY.id.."Guided or Cruise missiles requested but all missiles empty.") self:CeaseFire(self.currentTarget) return end @@ -588,9 +930,7 @@ end -- @param Core.Event#EVENTDATA EventData function ARTY:_OnEventDead(EventData) self:F(EventData) - - env.info("FF event dead") - + -- Name of controllable. local _name=self.Controllable:GetName() @@ -610,18 +950,17 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Before "OpenFire" event. +--- Before "OpenFire" event. Checks if group already has a target. Checks for valid min/max range and removes the target if necessary. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param #table target Array holding the target info. --- @return #boolean If true proceed to onafterOpenfire. +-- @return #boolean If true, proceed to onafterOpenfire. function ARTY:onbeforeOpenFire(Controllable, From, Event, To, target) self:_EventFromTo("onbeforeOpenFire", Event, From, To) - - + -- If this target has an attack time and it's prio is higher than the current task, we allow the transition. if target.time~=nil and self.currentTarget~=nil and self.currentTarget.prio > target.prio then -- Debug info. @@ -637,7 +976,7 @@ function ARTY:onbeforeOpenFire(Controllable, From, Event, To, target) -- Check that group has no current target already. if self.currentTarget then -- Debug info. - self:T(ARTY.id..string.format("Group %s already has a target %s.", self.Controllable:GetName(), self.currentTarget.name)) + self:T2(ARTY.id..string.format("Group %s already has a target %s.", self.Controllable:GetName(), self.currentTarget.name)) -- Deny transition. return false @@ -669,18 +1008,17 @@ function ARTY:onbeforeOpenFire(Controllable, From, Event, To, target) return true end ---- After "OpenFire" event. +--- After "OpenFire" event. Sets the current target and starts the fire at point task. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param #table target Array holding the target info. _target={coord=coord, radius=radius, nshells=nshells, engaged=0, underattack=false} +-- @param #table target Array holding the target info. function ARTY:onafterOpenFire(Controllable, From, Event, To, target) self:_EventFromTo("onafterOpenFire", Event, From, To) - local _coord=target.coord --Core.Point#COORDINATE - + --local _coord=target.coord --Core.Point#COORDINATE --_coord:MarkToAll("Arty Target") -- Get target array index. @@ -711,21 +1049,7 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Before "CeaseFire" event. Nothing to do at the moment. --- @param #ARTY self --- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param #table target Array holding the target info. --- @return #boolean -function ARTY:onbeforeCeaseFire(Controllable, From, Event, To, target) - self:_EventFromTo("onbeforeCeaseFire", Event, From, To) - - return true -end - ---- After "CeaseFire" event. +--- After "CeaseFire" event. Clears task of the group and removes the target if max engagement was reached. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. -- @param #string From From state. @@ -778,17 +1102,15 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. +-- @return #boolean If true, proceed to onafterWinchester. function ARTY:onbeforeWinchester(Controllable, From, Event, To) - -- Cease fire first. - if self.currentTarget then - self:CeaseFire(self.currentTarget) - end + return true end ---- After "Winchester" event. Group is out of ammo. +--- After "Winchester" event. Group is out of ammo. Trigger "Rearm" event. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. -- @param #string From From state. @@ -815,18 +1137,20 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. +-- @return #boolean If true, proceed to onafterRearm. function ARTY:onbeforeRearm(Controllable, From, Event, To) self:_EventFromTo("onbeforeRearm", Event, From, To) if self.RearmingUnit and self.RearmingUnit:IsAlive() then return true + elseif self.RearmingPlaceCoord then + return true else return false end end - --- After "Rearm" event. Send message if reporting is on. Route rearming unit to ARTY group. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. @@ -836,28 +1160,95 @@ end function ARTY:onafterRearm(Controllable, From, Event, To) self:_EventFromTo("onafterRearm", Event, From, To) - -- Send message. - local text=string.format("%s, %s, request rearming.", Controllable:GetName(), self.RearmingUnit:GetName()) - self:T(ARTY.id..text) - MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) + -- Coordinate of ARTY unit. + local coordARTY=self.Controllable:GetCoordinate() + local coordRARM + if self.RearmingUnit then + -- Coordinate of the rearming unit. + coordRARM=self.RearmingUnit:GetCoordinate() + -- Remember the coordinates of the rearming unit. After rearming it will go back to this position. + self.RearmingUnitCoord=coordRARM + end - -- Random point 20-100 m away from unit. - local coord=self.Controllable:GetCoordinate() - local vec2=coord:GetRandomVec2InRadius(20, 100) - local pops=COORDINATE:NewFromVec2(vec2) + if self.RearmingUnit and self.RearmingPlaceCoord and self.Speed>0 then - -- Remember the coordinates of the rearming unit. After rearming it will go back to this position. - self.RearmingUnitCoord=self.RearmingUnit:GetCoordinate() + -- Rearming unit and ARTY group meet at rearming place. + local dA=coordARTY:Get2DDistance(self.RearmingPlaceCoord) + local dR=coordRARM:Get2DDistance(self.RearmingPlaceCoord) + + -- Route ARTY group to rearming place. + if dA>100 then + self.Controllable:RouteGroundOnRoad(self.RearmingPlaceCoord, self.Speed, 1) + end + + -- Route Rearming unit to rearming place + if dR>100 then + self.RearmingUnit:RouteGroundOnRoad(self.RearmingPlaceCoord, 50, 1) + end - -- Route unit to ARTY group. - self.RearmingUnit:RouteGroundOnRoad(pops, 50, 5) + elseif self.RearmingUnit then + + -- Send message. + local text=string.format("%s, %s, request rearming.", Controllable:GetName(), self.RearmingUnit:GetName()) + self:T(ARTY.id..text) + MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) + + -- Distance between ARTY group and rearming unit. + local distance=coordARTY:Get2DDistance(coordRARM) + + -- If distance is larger than 100 m, the Rearming unit is routed to the ARTY group. + if distance > 100 then + -- Random point 20-100 m away from unit. + local vec2=coord:GetRandomVec2InRadius(20, 100) + local pops=COORDINATE:NewFromVec2(vec2) + + -- Route unit to ARTY group. + self.RearmingUnit:RouteGroundOnRoad(pops, 50, 1) + end + end -- Start scheduler to monitor ammo count until rearming is complete. self.SchedIDCheckRearmed=self.scheduler:Schedule(self, ARTY._CheckRearmed, {self}, 20, 20) end +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Check if ARTY group is reamed. +--- After "Rearmed" event. Send message if reporting is on and stop the scheduler. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function ARTY:onafterRearmed(Controllable, From, Event, To) + self:_EventFromTo("onafterRearmed", Event, From, To) + + -- Send message. + local text=string.format("%s, rearming complete.", Controllable:GetName()) + self:T(ARTY.id..text) + MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) + + -- Stop scheduler. + if self.SchedIDCheckRearmed then + self.scheduler:Stop(self.SchedIDCheckRearmed) + end + + -- Route ARTY group backto where it came from (if distance is > 100 m). + local d1=self.Controllable:GetCoordinate():Get2DDistance(self.InitialCoord) + if d1>100 then + self.Controllable:RouteGroundOnRoad(self.InitialCoord, self.Speed, 5) + end + + -- Route unit back to where it came from (if distance is > 100 m). + if self.RearmingUnit and self.RearmingUnit:IsAlive() then + local d=self.RearmingUnit:GetCoordinate():Get2DDistance(self.RearmingUnitCoord) + if d>100 then + self.RearmingUnit:RouteGroundOnRoad(self.RearmingUnitCoord, 50, 1) + end + end + +end + +--- Check if ARTY group is rearmed. -- @param #ARTY self function ARTY:_CheckRearmed() self:F2() @@ -890,33 +1281,61 @@ function ARTY:_CheckRearmed() end ---- After "Rearmed" event. Send message if reporting is on and stop the scheduler. + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Before "Move" event. Check if a unit to rearm the ARTY group has been defined. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -function ARTY:onafterRearmed(Controllable, From, Event, To) - self:_EventFromTo("onafterRearmed", Event, From, To) +-- @param Wrapper.Point#COORDINATE ToCoord Coordinate to which the ARTY group should move. +-- @param #boolean OnRoad If true group should move on road mainly. +-- @return #boolean If true, proceed to onafterMove. +function ARTY:onbeforeMove(Controllable, From, Event, To, ToCoord, OnRoad) + self:_EventFromTo("onbeforeMove", Event, From, To) - -- Send message. - local text=string.format("%s, rearming complete.", Controllable:GetName()) - self:T(ARTY.id..text) - MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) - - -- Stop scheduler. - --self.SchedCheckRearmed:Stop() - if self.SchedIDCheckRearmed then - self.scheduler:Stop(self.SchedIDCheckRearmed) + -- Check if group can actually move... + if self.Speed==0 then + return false + end + + -- Cease fire first. + if self.currentTarget then + self:CeaseFire(self.currentTarget) + end + + return true +end + +--- After "Move" event. Route group to given coordinate. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Wrapper.Point#COORDINATE ToCoord Coordinate to which the ARTY group should move. +-- @param #boolean OnRoad If true group should move on road mainly. +function ARTY:onafterMove(Controllable, From, Event, To, ToCoord, OnRoad) + self:_EventFromTo("onafterMove", Event, From, To) + + -- Set alarm state to green and ROE to weapon hold. + self.Controllable:OptionAlarmStateGreen() + self.Controllable:OptionROEHoldFire() + + -- Route group to coodinate. + if OnRoad then + self.Controllable:RouteGroundOnRoad(ToCoord, self.Speed, 1) + else + self.Controllable:RouteGroundTo(ToCoord, self.Speed, "Vee", 1) end - -- Route unit back to where it came from. - self.RearmingUnit:RouteGroundOnRoad(self.RearmingUnitCoord, 50, 5) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- After "Dead" event, when a unit has died. When all units of a group are dead, FSM is stopped and eventhandler removed. +--- After "Dead" event, when a unit has died. When all units of a group are dead trigger "Stop" event. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. -- @param #string From From state. @@ -953,6 +1372,7 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. +-- @return #boolean If true, proceed to onafterStop. function ARTY:onbeforeStop(Controllable, From, Event, To) self:_EventFromTo("onbeforeStop", Event, From, To) @@ -964,7 +1384,7 @@ function ARTY:onbeforeStop(Controllable, From, Event, To) return true end ---- After "Stop" event. Remove all target, stop schedulers, unhandle events and stop the FSM. +--- After "Stop" event. Stop schedulers and unhandle events. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. -- @param #string From From state. @@ -1351,20 +1771,17 @@ function ARTY:_WeaponTypeName(tnumber) local name="unknown" if tnumber==ARTY.WeaponType.Auto then name="Auto (Cannon, Rockets, Missiles)" - elseif tnumber==ARTY.WeaponType.CruiseMissile then - name="Cruise Missile" - elseif tnumber==ARTY.WeaponType.GuidedAny then - name="Any Guided Missile" - elseif tnumber==ARTY.WeaponType.GuidedMissile then - name="Guided Missile" + elseif tnumber==ARTY.WeaponType.Cannon then + name="Cannon" + elseif tnumber==ARTY.WeaponType.Rockets then + name="Rockets" elseif tnumber==ARTY.WeaponType.UnguidedAny then name="Any Unguided Weapon (Cannon or Rockets)" - elseif tnumber==ARTY.WeaponType.UnguidedCannon then - name="Unguided Cannon" - elseif tnumber==ARTY.WeaponType.UnguidedRockets then - name="Unguided Rockets" + elseif tnumber==ARTY.WeaponType.CruiseMissile then + name="Cruise Missile" + elseif tnumber==ARTY.WeaponType.GuidedMissile then + name="Guided Missile" end - return name end @@ -1471,4 +1888,81 @@ function ARTY:_ClockToSeconds(clock) return seconds end -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- \ No newline at end of file +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Route group to a certain point. +-- @param #ARTY self +-- @param Wrapper.Group#GROUP group Group to route. +-- @param Core.Point#COORDINATE ToCoord Coordinate where we want to go. +-- @param #number Speed Speed in km/h. +-- @param #boolean OnRoad If true, use (mainly) roads. +function ARTY:_Move(group, ToCoord, Speed, OnRoad) + + -- Clear all tasks. + group:ClearTasks() + group:OptionAlarmStateGreen() + group:OptionROEHoldFire() + + -- Set formation. + local formation = "Off road" + + -- Current coordinates of group. + local cpini=group:GetCoordinate() + cpini:SmokeWhite() + + -- Distance between current and final point. + local dist=cpini:Get2DDistance(ToCoord) + + -- Waypoint and task arrays. + local path={} + local task={} + + -- First waypoint is the current position of the group. + path[#path+1]=cpini:WaypointGround(Speed, formation) + task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, 0, false) + + path[#path+1]=ToCoord:WaypointGround(Speed, formation) + task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, 1, true) + + -- Init waypoints of the group. + local Waypoints={} + + -- New points are added to the default route. + for i,p in ipairs(path) do + table.insert(Waypoints, i, path[i]) + end + + -- Set task for all waypoints. + for i,wp in ipairs(Waypoints) do + group:SetTaskWaypoint(Waypoints[i], task[i]) + end + + -- Submit task and route group along waypoints. + group:Route(Waypoints) + +end + +--- Function called when group is passing a waypoint. +-- @param Wrapper.Group#GROUP group Group for which waypoint passing should be monitored. +-- @param #ARTY arty ARTY object. +-- @param #number i Waypoint number that has been reached. +-- @param #boolean final True if it is the final waypoint. +function ARTY._PassingWaypoint(group, arty, i, final) + + -- Debug message. + local text=string.format("Group %s passing waypoint %d (final=%s)", group:GetName(), i, tostring(final)) + + local pos=group:GetCoordinate() + local MarkerID=pos:MarkToAll(string.format("Reached Waypoint %d of group %s", i, group:GetName())) + pos:SmokeRed() + + MESSAGE:New(text,10):ToAll() + env.info(ARTY.id..text) + + -- Move --> Moving --> Arrived --> CombatReady. + if final then + arty:Arrived() + end + +end + \ No newline at end of file From 4fccfa38d425967ab60347bccc36635e1a5ee8f2 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 3 May 2018 23:42:03 +0200 Subject: [PATCH 074/420] ARTY v0.7 Added "NewTarget" event. Improved task function for waypoints Removed TargetQueue scheduler. --- Moose Development/Moose/Core/Fsm.lua | 8 +- .../Moose/Functional/Artillery.lua | 221 +++++++++++++----- 2 files changed, 166 insertions(+), 63 deletions(-) diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index 4e959004f..62b80aee2 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -682,15 +682,15 @@ do -- FSM end if execute then + self:_call_handler("onafter", EventName, Params, EventName ) + self:_call_handler("OnAfter", EventName, Params, EventName ) + -- only execute the call if the From state is not equal to the To state! Otherwise this function should never execute! --if from ~= to then self:_call_handler("onenter", To, Params, EventName ) self:_call_handler("OnEnter", To, Params, EventName ) --end - - self:_call_handler("onafter", EventName, Params, EventName ) - self:_call_handler("OnAfter", EventName, Params, EventName ) - + self:_call_handler("onstate", "change", Params, EventName ) end else diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index d54830eb0..41e6e1f29 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -3,7 +3,7 @@ -- -- === -- --- ![Banner Image](..\Presentations\ARTY\Artillery_Main.png) +-- ![Banner Image](..\Presentations\ARTY\ARTY_Main.png) -- -- ==== -- @@ -50,8 +50,6 @@ -- @field #number Nmissiles0 Initial amount of missiles of the whole group. -- @field #number FullAmmo Full amount of all ammunition taking the number of alive units into account. -- @field Core.Scheduler#SCHEDULER scheduler Scheduler object handling various timed functions. --- @field #number SchedIDTargetQueue Scheduler ID for updating the target queue and calling OpenFire event. --- @field #number TargetQueueUpdate Interval between updates of the target queue. -- @field #number SchedIDCheckRearmed Scheduler ID responsible for checking whether rearming of the ARTY group is complete. -- @field #number SchedIDCheckShooting Scheduler ID for checking whether a group startet firing within a certain time after the fire at point task was assigned. -- @field #number WaitForShotTime Max time in seconds to wait until fist shot event occurs after target is assigned. If time is passed without shot, the target is deleted. Default is 300 seconds. @@ -87,7 +85,7 @@ -- -- ## The ARTY Process -- --- ![Process](..\Presentations\ARTY\Artillery_Process.png) +-- ![Process](..\Presentations\ARTY\ARTY_Process.png) -- -- After the FMS process is started the ARTY group will be in the state **CombatReady**. Once a target is assigned the **OpenFire** event will be triggered and the group starts -- firing. At this point the group in in the state **Firing**. @@ -204,7 +202,6 @@ -- * @{#ARTY.SetMinFiringRange}(*range*) defines the minimum firing range. Targets closer than this distance are not engaged. -- * @{#ARTY.SetRearmingUnit}(*unit*) sets the unit resposible for rearming of the ARTY group once it is out of ammo. -- * @{#ARTY.SetReportON}() and @{#ARTY.SetReportOFF}() can be used to enable/disable status reports of the ARTY group send to all coalition members. --- * @{#ARTY.SetTargetQueueUpdateInterval}(*interval*) sets the interval (in seconds) at which the target queue is updated. Default is every 5 seconds. -- * @{#ARTY.SetWaitForShotTime}(*waittime*) sets the time after which a target is deleted from the queue if no shooting event occured after the target engagement started. -- Default is 300 seconds. Note that this can for example happen, when the assigned target is out of range. -- * @{#ARTY.SetDebugON}() and @{#ARTY.SetDebugOFF}() can be used to enable/disable the debug mode. @@ -233,8 +230,6 @@ ARTY={ Nmissiles0=0, FullAmmo=0, scheduler=nil, - SchedIDTargetQueue=nil, - TargetQueueUpdate=5, SchedIDCheckRearmed=nil, SchedIDCheckShooting=nil, WaitForShotTime=300, @@ -274,7 +269,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #number version -ARTY.version="0.6.0" +ARTY.version="0.7.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -340,8 +335,8 @@ function ARTY:New(group) self:T3({id=id, desc=desc}) end - -- Set speed to maximum in km/h. - self.Speed=self.DCSdesc.speedMax*3.6 + -- Set speed to 1/2 of maximum in km/h. + self.Speed=self.DCSdesc.speedMax*3.6 * 0.5 -- Displayed name (similar to type name below) self.DisplayName=self.DCSdesc.displayName @@ -365,6 +360,7 @@ function ARTY:New(group) self:AddTransition("Rearming", "Rearmed", "CombatReady") self:AddTransition("CombatReady", "Move", "Moving") self:AddTransition("Moving", "Arrived", "CombatReady") + self:AddTransition("*", "NewTarget", "*") self:AddTransition("*", "Dead", "*") --- User function for OnBefore "OpenFire" event. @@ -582,6 +578,9 @@ function ARTY:AssignTargetCoord(coord, prio, radius, nshells, maxengage, time, w -- Debug info. self:T(ARTY.id..string.format("Added target %s, prio=%d, radius=%d, nshells=%d, maxengage=%d, time=%s, weapontype=%d", name, prio, radius, nshells, maxengage, tostring(_clock), weapontype)) + + -- Trigger new target event. + self:NewTarget(_target) end @@ -649,14 +648,6 @@ function ARTY:SetDebugOFF() self.Debug=false end ---- Set target queue update time interval. --- @param #ARTY self --- @param #number interval Time interval in seconds. Default is 5 seconds. -function ARTY:SetTargetQueueUpdateInterval(interval) - self:F2({interval=interval}) - self.TargetQueueUpdate=interval or 5 -end - --- Delete target from target list. -- @param #ARTY self -- @param #string name Name of the target. @@ -715,6 +706,17 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Before "Start" event. Initialized ROE and alarm state. Starts the event handler. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function ARTY:onbeforeStart(Controllable, From, Event, To) + self:_EventFromTo("onbeforeStart", Event, From, To) + + env.info("FF: onbeforeStart") +end --- After "Start" event. Initialized ROE and alarm state. Starts the event handler. -- @param #ARTY self @@ -779,9 +781,6 @@ function ARTY:onafterStart(Controllable, From, Event, To) self:HandleEvent(EVENTS.Shot, self._OnEventShot) self:HandleEvent(EVENTS.Dead, self._OnEventDead) - -- Start scheduler to monitor task queue. - self.SchedIDTargetQueue=self.scheduler:Schedule(self, ARTY._TargetQueue, {self}, 5, self.TargetQueueUpdate) - -- Start scheduler to monitor if ARTY group started firing within a certain time. self.SchedIDCheckShooting=self.scheduler:Schedule(self, ARTY._CheckShootingStarted, {self}, 60, 60) @@ -950,6 +949,23 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- After "NewTarget" event. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #table target Array holding the target info. +-- @return #boolean If true, proceed to onafterOpenfire. +function ARTY:onafterNewTarget(Controllable, From, Event, To, target) + self:_EventFromTo("onafterNewTarget", Event, From, To) + + -- Debug message. + local text=string.format("Adding new target %s.", target.name) + MESSAGE:New(text, 30):ToAllIf(self.Debug) + self:T(ARTY.id..text) +end + --- Before "OpenFire" event. Checks if group already has a target. Checks for valid min/max range and removes the target if necessary. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. @@ -973,6 +989,7 @@ function ARTY:onbeforeOpenFire(Controllable, From, Event, To, target) return true end + -- Check that group has no current target already. if self.currentTarget then -- Debug info. @@ -1095,6 +1112,36 @@ function ARTY:onafterCeaseFire(Controllable, From, Event, To, target) end +--- Enter "CombatReady" state. Route the group back if necessary. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function ARTY:onenterCombatReady(Controllable, From, Event, To) + + env.info(string.format("FF: onenterComabReady, from=%s, event=%s, to=%s", From, Event, To)) + + if From=="Rearming" and Event=="Rearmed" then + env.info("FF: Comabatready after Rearmed") + + -- Distance to initial position. + local dist=Controllable:GetCoordinate():Get2DDistance(self.InitialCoord) + + if dist>100 then + -- Route group back to its original position, when rearming was at another place. + self:T(ARTY.id..string.format("%s is routed back to its initial position. Distance = %d m.", Controllable:GetName(), dist)) + self:__Move(30, self.InitialCoord, true) + end + + else + -- Update target queue and open fire. + env.info("FF: Comabatready ==> _openfireontarget.") + self:_openfireontarget() + end + +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Before "Winchester" event. Cease fire on current target. -- @param #ARTY self @@ -1105,8 +1152,6 @@ end -- @return #boolean If true, proceed to onafterWinchester. function ARTY:onbeforeWinchester(Controllable, From, Event, To) - - return true end @@ -1141,6 +1186,7 @@ end function ARTY:onbeforeRearm(Controllable, From, Event, To) self:_EventFromTo("onbeforeRearm", Event, From, To) + -- Check if a reaming unit or rearming place was specified. if self.RearmingUnit and self.RearmingUnit:IsAlive() then return true elseif self.RearmingPlaceCoord then @@ -1162,7 +1208,9 @@ function ARTY:onafterRearm(Controllable, From, Event, To) -- Coordinate of ARTY unit. local coordARTY=self.Controllable:GetCoordinate() - local coordRARM + + -- Coordinate of rearming unit. + local coordRARM=nil if self.RearmingUnit then -- Coordinate of the rearming unit. coordRARM=self.RearmingUnit:GetCoordinate() @@ -1172,22 +1220,28 @@ function ARTY:onafterRearm(Controllable, From, Event, To) if self.RearmingUnit and self.RearmingPlaceCoord and self.Speed>0 then - -- Rearming unit and ARTY group meet at rearming place. + -- CASE 1: Rearming unit and ARTY group meet at rearming place. + + -- Distances. local dA=coordARTY:Get2DDistance(self.RearmingPlaceCoord) local dR=coordRARM:Get2DDistance(self.RearmingPlaceCoord) -- Route ARTY group to rearming place. if dA>100 then - self.Controllable:RouteGroundOnRoad(self.RearmingPlaceCoord, self.Speed, 1) + --self.Controllable:RouteGroundOnRoad(self.RearmingPlaceCoord, self.Speed, 1) + self:_Move(self.Controllable, self.RearmingPlaceCoord, self.Speed, true) end -- Route Rearming unit to rearming place if dR>100 then self.RearmingUnit:RouteGroundOnRoad(self.RearmingPlaceCoord, 50, 1) + --self:_Move(self.RearmingUnit, self.RearmingPlaceCoord, 50, true) end elseif self.RearmingUnit then + -- CASE 2: Rearming unit drives to ARTY group. + -- Send message. local text=string.format("%s, %s, request rearming.", Controllable:GetName(), self.RearmingUnit:GetName()) self:T(ARTY.id..text) @@ -1199,12 +1253,26 @@ function ARTY:onafterRearm(Controllable, From, Event, To) -- If distance is larger than 100 m, the Rearming unit is routed to the ARTY group. if distance > 100 then -- Random point 20-100 m away from unit. - local vec2=coord:GetRandomVec2InRadius(20, 100) + local vec2=coordARTY:GetRandomVec2InRadius(20, 100) local pops=COORDINATE:NewFromVec2(vec2) -- Route unit to ARTY group. self.RearmingUnit:RouteGroundOnRoad(pops, 50, 1) end + + elseif self.RearmingPlaceCoord then + + -- CASE 3: ARTY drives to rearming place. + + -- Distance. + local dA=coordARTY:Get2DDistance(self.RearmingPlaceCoord) + + -- Route ARTY group to rearming place. + if dA>100 then + --self.Controllable:RouteGroundOnRoad(self.RearmingPlaceCoord, self.Speed, 1) + self:_Move(self.Controllable, self.RearmingPlaceCoord, self.Speed, true) + end + end -- Start scheduler to monitor ammo count until rearming is complete. @@ -1325,14 +1393,30 @@ function ARTY:onafterMove(Controllable, From, Event, To, ToCoord, OnRoad) self.Controllable:OptionROEHoldFire() -- Route group to coodinate. - if OnRoad then - self.Controllable:RouteGroundOnRoad(ToCoord, self.Speed, 1) - else - self.Controllable:RouteGroundTo(ToCoord, self.Speed, "Vee", 1) - end + self:_Move(self.Controllable, ToCoord, self.Speed, OnRoad) end +--- After "Arrived" event. Group has reached its destination. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function ARTY:onafterArrived(Controllable, From, Event, To) + self:_EventFromTo("onafterArrived", Event, From, To) + + -- Set alarm state to auto. + self.Controllable:OptionAlarmStateAuto() + + -- Send message + local text=string.format("%s, arrived at destination.", Controllable:GetName()) + self:T(ARTY.id..text) + MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) + +end + + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- After "Dead" event, when a unit has died. When all units of a group are dead trigger "Stop" event. @@ -1398,9 +1482,6 @@ function ARTY:onafterStop(Controllable, From, Event, To) -- Remove all targets. --self:RemoveAllTargets() -- Stop schedulers. - if self.SchedIDTargetQueue then - self.scheduler:Stop(self.SchedIDTargetQueue) - end if self.SchedIDCheckShooting then self.scheduler:Stop(self.SchedIDCheckShooting) end @@ -1443,9 +1524,9 @@ function ARTY:_FireAtCoord(coord, radius, nshells, weapontype) end ---- Go through queue of assigned tasks. +--- Go through queue of assigned tasks and trigger OpenFire event. -- @param #ARTY self -function ARTY:_TargetQueue() +function ARTY:_openfireontarget() self:F2() -- Debug info @@ -1453,7 +1534,7 @@ function ARTY:_TargetQueue() -- No targets assigned at the moment. if #self.targets==0 then - self:T3(ARTY.id..string.format("Group %s, no targets assigned at the moment. No need for _TargetQueue.", self.Controllable:GetName())) + self:T3(ARTY.id..string.format("Group %s, no targets assigned at the moment. No need for _OpenFire.", self.Controllable:GetName())) return end @@ -1471,7 +1552,7 @@ function ARTY:_TargetQueue() self:T(ARTY.id..string.format("Engaging timed target %s. Prio=%d, engaged=%d, time=%s, tnow=%s",_target.name,_target.prio,_target.engaged,_clock,_Cnow)) -- Call OpenFire event. - self:OpenFire(_target) + self:__OpenFire(1, _target) end end @@ -1491,9 +1572,8 @@ function ARTY:_TargetQueue() self:T(ARTY.id..string.format("Engaging target %s. Prio = %d, engaged = %d", _target.name, _target.prio, _target.engaged)) -- Call OpenFire event. - self:OpenFire(_target) - - break + self:__OpenFire(1, _target) + end end @@ -1548,8 +1628,11 @@ end --- Get the number of shells a unit or group currently has. For a group the ammo count of all units is summed up. -- @param #ARTY self --- @param Wrapper.Controllable#CONTROLLABLE controllable --- @return Number of ALL shells left from the whole group. +-- @param Wrapper.Controllable#CONTROLLABLE controllable Controllable for which the ammo is counted. +-- @return #number Total amount of ammo the whole group has left. +-- @return #number Number of shells the group has left. +-- @return #number Number of rockets the group has left. +-- @return #number Number of missiles the group has left. function ARTY:_GetAmmo(controllable) self:F2(controllable) @@ -1570,7 +1653,7 @@ function ARTY:_GetAmmo(controllable) if unit and unit:IsAlive() then local ammotable=unit:GetAmmo() - self:T({ammotable=ammotable}) + self:T2({ammotable=ammotable}) local name=unit:GetName() @@ -1768,6 +1851,7 @@ end -- @param #number tnumber Number of weapon type ARTY.WeaponType.XXX -- @return #number tnumber of weapon type. function ARTY:_WeaponTypeName(tnumber) + self:F2(tnumber) local name="unknown" if tnumber==ARTY.WeaponType.Auto then name="Auto (Cannon, Rockets, Missiles)" @@ -1823,7 +1907,6 @@ function ARTY:_SecondsToClock(seconds) if seconds==nil then return nil - --return "00:00:00" end -- Seconds @@ -1833,14 +1916,13 @@ function ARTY:_SecondsToClock(seconds) local _seconds=seconds%(60*60*24) if seconds <= 0 then - return "00:00:00" + return nil else local hours = string.format("%02.f", math.floor(_seconds/3600)) local mins = string.format("%02.f", math.floor(_seconds/60 - (hours*60))) local secs = string.format("%02.f", math.floor(_seconds - hours*3600 - mins *60)) - local days = string.format("%d", seconds/(60*60*24)) + local days = string.format("%d", seconds/(60*60*24)) return hours..":"..mins..":"..secs.."+"..days - --return hours, mins, secs end end @@ -1908,7 +1990,6 @@ function ARTY:_Move(group, ToCoord, Speed, OnRoad) -- Current coordinates of group. local cpini=group:GetCoordinate() - cpini:SmokeWhite() -- Distance between current and final point. local dist=cpini:Get2DDistance(ToCoord) @@ -1916,24 +1997,46 @@ function ARTY:_Move(group, ToCoord, Speed, OnRoad) -- Waypoint and task arrays. local path={} local task={} - + -- First waypoint is the current position of the group. path[#path+1]=cpini:WaypointGround(Speed, formation) - task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, 0, false) + task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, false) + + -- Route group on road if requested. + if OnRoad then + + --path[#path+1]=cpini:GetClosestPointToRoad():WaypointGround(Speed, "On road") + --task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, false) + local _first=cpini:GetClosestPointToRoad() + local _last=ToCoord:GetClosestPointToRoad() + local _onroad=_first:GetPathOnRoad(_last) + + -- Points on road. + for i=1,#_onroad do + path[#path+1]=_onroad[i]:WaypointGround(Speed, "On road") + task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, false) + end + + --path[#path+1]=ToCoord:GetClosestPointToRoad():WaypointGround(Speed, "On road") + --task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, false) + end + + -- Last waypoint at ToCoord. path[#path+1]=ToCoord:WaypointGround(Speed, formation) - task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, 1, true) + task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, true) + -- Init waypoints of the group. local Waypoints={} -- New points are added to the default route. - for i,p in ipairs(path) do + for i=1,#path do table.insert(Waypoints, i, path[i]) end -- Set task for all waypoints. - for i,wp in ipairs(Waypoints) do + for i=1,#Waypoints do group:SetTaskWaypoint(Waypoints[i], task[i]) end @@ -1952,15 +2055,15 @@ function ARTY._PassingWaypoint(group, arty, i, final) -- Debug message. local text=string.format("Group %s passing waypoint %d (final=%s)", group:GetName(), i, tostring(final)) - local pos=group:GetCoordinate() - local MarkerID=pos:MarkToAll(string.format("Reached Waypoint %d of group %s", i, group:GetName())) - pos:SmokeRed() + --local pos=group:GetCoordinate() + --local MarkerID=pos:MarkToAll(string.format("Reached Waypoint %d of group %s", i, group:GetName())) + --pos:SmokeRed() MESSAGE:New(text,10):ToAll() env.info(ARTY.id..text) -- Move --> Moving --> Arrived --> CombatReady. - if final then + if final and arty.Controllable:GetName()==group:GetName() then arty:Arrived() end From 0cf4b8845e83d7ebc16ff8b68bda01bd447fb561 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sat, 5 May 2018 08:44:16 +0200 Subject: [PATCH 075/420] ARTY v0.8.0 WIP version. Not functional. --- .../Moose/Functional/Artillery.lua | 698 +++++++++++------- 1 file changed, 411 insertions(+), 287 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 41e6e1f29..aaf1a77b9 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -60,10 +60,10 @@ -- @field #number IniGroupStrength Inital number of units in the ARTY group. -- @field #boolean IsArtillery If true, ARTY group has attribute "Artillery". -- @field #number Speed Max speed of ARTY group. --- @field Wrapper.Unit#UNIT RearmingUnit Unit designated to rearm the ARTY group. --- @field Wrapper.Point#COORDINATE RearmingUnitCoord Initial coordinates of the rearming unit. After rearming complete, the unit will return to this position. --- @field Wrapper.Point#COORDINATE RearmingPlaceCoord Coordinates of the rearming place. If the place is more than 100 m away from the ARTY group, the group will go there. --- @field Wrapper.Point#COORDINATE InitialCoord Initial coordinates of the ARTY group. +-- @field Wrapper.Group#GROUP RearmingGroup Unit designated to rearm the ARTY group. +-- @field Core.Point#COORDINATE RearmingGroupCoord Initial coordinates of the rearming unit. After rearming complete, the unit will return to this position. +-- @field Core.Point#COORDINATE RearmingPlaceCoord Coordinates of the rearming place. If the place is more than 100 m away from the ARTY group, the group will go there. +-- @field Core.Point#COORDINATE InitialCoord Initial coordinates of the ARTY group. -- @field #boolean report Arty group sends messages about their current state or target to its coaliton. -- @field #table ammoshells Table holding names of the shell types which are included when counting the ammo. Default is {"weapons.shells"} which include most shells. -- @field #table ammorockets Table holding names of the rocket types which are included when counting the ammo. Default is {"weapons.nurs"} which includes most unguided rockets. @@ -96,10 +96,10 @@ -- When the ARTY group runs out of ammunition, the event **Winchester** is triggered and the group enters the state **OutOfAmmo**. -- In this state, the group is unable to engage further targets. -- --- With the @{#ARTY.SetRearmingUnit}(*unit*) command, a special unit can be defined to rearm the ARTY group. If this unit has been assigned and the group has entered the state +-- With the @{#ARTY.SetRearmingGroup}(*group*) command, a special group can be defined to rearm the ARTY group. If this unit has been assigned and the group has entered the state -- **OutOfAmmo** the event **Rearm** is triggered followed by a transition to the state **Rearming**. --- If the rearming unit is less than 100 meters away from the ARTY group, the rearming process starts. If the rearming unit is more than 100 meters away from the ARTY unit, the --- rearming unit is routed to a point 20 to 100 m from the ARTY group. +-- If the rearming group is less than 100 meters away from the ARTY group, the rearming process starts. If the rearming group is more than 100 meters away from the ARTY unit, the +-- rearming group is routed to a point 20 to 100 m from the ARTY group. -- -- Once the rearming is complete, the **Rearmed** event is triggered and the group enters the state **CombatReady**. At this point targeted can be engaged again. -- @@ -200,7 +200,7 @@ -- * @{#ARTY.RemoveTarget}(*name*) deletes the target with *name* from the target queue. -- * @{#ARTY.SetMaxFiringRange}(*range*) defines the maximum firing range. Targets further away than this distance are not engaged. -- * @{#ARTY.SetMinFiringRange}(*range*) defines the minimum firing range. Targets closer than this distance are not engaged. --- * @{#ARTY.SetRearmingUnit}(*unit*) sets the unit resposible for rearming of the ARTY group once it is out of ammo. +-- * @{#ARTY.SetRearmingGroup}(*group*) sets the group resposible for rearming of the ARTY group once it is out of ammo. -- * @{#ARTY.SetReportON}() and @{#ARTY.SetReportOFF}() can be used to enable/disable status reports of the ARTY group send to all coalition members. -- * @{#ARTY.SetWaitForShotTime}(*waittime*) sets the time after which a target is deleted from the queue if no shooting event occured after the target engagement started. -- Default is 300 seconds. Note that this can for example happen, when the assigned target is out of range. @@ -239,8 +239,8 @@ ARTY={ DisplayName=nil, IniGroupStrength=0, IsArtillery=nil, - RearmingUnit=nil, - RearmingUnitCoord=nil, + RearmingGroup=nil, + RearmingGroupCoord=nil, RearmingPlaceCoord=nil, InitialCoord=nil, report=true, @@ -269,7 +269,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #number version -ARTY.version="0.7.0" +ARTY.version="0.8.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -287,6 +287,10 @@ ARTY.version="0.7.0" -- DONE: Improve assigned time for engagement. Next day? -- DONE: Improve documentation. -- DONE: Add pseudo user transitions. OnAfter... +-- TODO: Make reaming unit a group. +-- TODO: Adjust documenation again. +-- TODO: Add command move to make arty group move. +-- TODO: remove schedulers for status event. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -350,18 +354,35 @@ function ARTY:New(group) -- Initial group strength. self.IniGroupStrength=#group:GetUnits() - -- Transitions - self:AddTransition("*", "Start", "CombatReady") - self:AddTransition("CombatReady", "OpenFire", "Firing") - self:AddTransition("CombatReady", "Winchester", "OutOfAmmo") - self:AddTransition("Firing", "OpenFire", "Firing") - self:AddTransition("Firing", "CeaseFire", "CombatReady") - self:AddTransition("OutOfAmmo", "Rearm", "Rearming") - self:AddTransition("Rearming", "Rearmed", "CombatReady") - self:AddTransition("CombatReady", "Move", "Moving") - self:AddTransition("Moving", "Arrived", "CombatReady") - self:AddTransition("*", "NewTarget", "*") - self:AddTransition("*", "Dead", "*") + -- Transitions: + -- Entry + self:AddTransition("*", "Start", "CombatReady") + + -- Blue branch. + self:AddTransition("CombatReady", "OpenFire", "Firing") + self:AddTransition("Firing", "OpenFire", "Firing") + self:AddTransition("Firing", "CeaseFire", "CombatReady") + --self:AddTransition("CombatReady", "CeaseFire", "CombatReady") -- not in diagram yet. + + -- Violett branch. + self:AddTransition("Firing", "Winchester", "OutOfAmmo") + + -- Red branch. + self:AddTransition("OutOfAmmo", "Rearm", "Rearming") + self:AddTransition("Rearming", "Rearmed", "Rearmed") + + -- Green branch. + self:AddTransition("*", "Move", "Moving") + self:AddTransition("Moving", "Arrived", "Arrived") + + self:AddTransition("*", "CombatReady", "CombatReady") + + -- Yellow branch. + self:AddTransition("*", "NewTarget", "*") + -- Not in diagram. + self:AddTransition("*", "Status", "*") + self:AddTransition("*", "Dead", "*") + --- User function for OnBefore "OpenFire" event. -- @function [parent=#ARTY] OnBeforeOpenFire @@ -608,12 +629,12 @@ function ARTY:SetWaitForShotTime(waittime) self.WaitForShotTime=waittime or 300 end ---- Assign a unit which is responsible for rearming the ARTY group. If the unit is too far away from the ARTY group it will be guided towards the ARTY group. +--- Assign a group, which is responsible for rearming the ARTY group. If the group is too far away from the ARTY group it will be guided towards the ARTY group. -- @param #ARTY self --- @param Wrapper.Unit#UNIT unit Unit that is supposed to rearm the ARTY group. -function ARTY:SetRearmingUnit(unit) - self:F({unit=unit}) - self.RearmingUnit=unit +-- @param Wrapper.Group#GROUP unit Unit that is supposed to rearm the ARTY group. +function ARTY:SetRearmingGroup(group) + self:F({group=group}) + self.RearmingGroup=group end --- Defines the rearming place of the ARTY group. If the place is too far away from the ARTY group it will be routed to the place. @@ -747,8 +768,8 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("Number of shells = %d\n", self.Nshells0) text=text..string.format("Number of rockets = %d\n", self.Nrockets0) text=text..string.format("Number of missiles = %d\n", self.Nmissiles0) - if self.RearmingUnit then - text=text..string.format("Reaming unit = %s\n", self.RearmingUnit:GetName()) + if self.RearmingGroup then + text=text..string.format("Reaming group = %s\n", self.RearmingGroup:GetName()) end if self.RearmingPlaceCoord then local dist=self.InitialCoord:Get2DDistance(self.RearmingPlaceCoord) @@ -780,14 +801,16 @@ function ARTY:onafterStart(Controllable, From, Event, To) -- Add event handler. self:HandleEvent(EVENTS.Shot, self._OnEventShot) self:HandleEvent(EVENTS.Dead, self._OnEventDead) + + self:__Status(5) -- Start scheduler to monitor if ARTY group started firing within a certain time. self.SchedIDCheckShooting=self.scheduler:Schedule(self, ARTY._CheckShootingStarted, {self}, 60, 60) -- Start cheduler for status reports. - if self.Debug then - self.SchedIDStatusReport=self.scheduler:Schedule(self, ARTY._StatusReport, {self}, 30, 30) - end +-- if self.Debug then +-- self.SchedIDStatusReport=self.scheduler:Schedule(self, ARTY._StatusReport, {self}, 30, 30) +-- end end @@ -868,8 +891,6 @@ function ARTY:_OnEventShot(EventData) if _nammo==0 then self:T(ARTY.id..string.format("Group %s completely out of ammo.", self.Controllable:GetName())) - -- Cease fire first. - self:CeaseFire(self.currentTarget) self:Winchester() -- Current target is deallocated ==> return @@ -966,6 +987,242 @@ function ARTY:onafterNewTarget(Controllable, From, Event, To, target) self:T(ARTY.id..text) end +--- After "Status" event. Report status of group. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function ARTY:onafterStatus(Controllable, From, Event, To) + self:_EventFromTo("onafterStatus", Event, From, To) + + if self.Debug then + self:_StatusReport() + end + + local _engage=true + + if self:is("OutOfAmmo") then + + -- Coordinate of ARTY unit. + local coordARTY=self.Controllable:GetCoordinate() + + -- Coordinate of rearming group. + local coordRARM=nil + if self.RearmingGroup then + -- Coordinate of the rearming unit. + coordRARM=self.RearmingGroup:GetCoordinate() + -- Remember the coordinates of the rearming unit. After rearming it will go back to this position. + self.RearmingGroupCoord=coordRARM + end + + if self.RearmingGroup and self.RearmingPlaceCoord and self.Speed>0 then + + -- CASE 1: Rearming unit and ARTY group meet at rearming place. + + -- Distances. + local dA=coordARTY:Get2DDistance(self.RearmingPlaceCoord) + local dR=coordRARM:Get2DDistance(self.RearmingPlaceCoord) + + -- Route ARTY group to rearming place. + if dA>100 then + --self.Controllable:RouteGroundOnRoad(self.RearmingPlaceCoord, self.Speed, 1) + --self:_Move(self.Controllable, self.RearmingPlaceCoord, self.Speed, true) + self:Move(self.RearmingPlaceCoord, false) + end + + -- Route Rearming unit to rearming place + if dR>100 then + self.RearmingGroup:RouteGroundOnRoad(self.RearmingPlaceCoord, 50, 1) + --self:_Move(self.RearmingGroup, self.RearmingPlaceCoord, 50, true) + end + + elseif self.RearmingGroup then + + -- CASE 2: Rearming unit drives to ARTY group. + + -- Send message. + local text=string.format("%s, %s, request rearming.", Controllable:GetName(), self.RearmingGroup:GetName()) + self:T(ARTY.id..text) + MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) + + -- Distance between ARTY group and rearming unit. + local distance=coordARTY:Get2DDistance(coordRARM) + + -- If distance is larger than 100 m, the Rearming unit is routed to the ARTY group. + if distance > 100 then + -- Random point 20-100 m away from unit. + local vec2=coordARTY:GetRandomVec2InRadius(20, 100) + local pops=COORDINATE:NewFromVec2(vec2) + + -- Route unit to ARTY group. + self.RearmingGroup:RouteGroundOnRoad(pops, 50, 1) + end + + elseif self.RearmingPlaceCoord then + + -- CASE 3: ARTY drives to rearming place. + + -- Distance. + local dA=coordARTY:Get2DDistance(self.RearmingPlaceCoord) + + -- Route ARTY group to rearming place. + if dA>100 then + --self.Controllable:RouteGroundOnRoad(self.RearmingPlaceCoord, self.Speed, 1) + --self:_Move(self.Controllable, self.RearmingPlaceCoord, self.Speed, true) + self:Move(self.RearmingPlaceCoord, false) + end + + end + + _engage=false + + end + + if self:is("Moving") then + _engage=false + end + + if self:is("Rearming") then + _engage=false + end + + if self:is("Rearmed") then + local distance=self.Controllable:GetCoordinate():Get2DDistance(self.InitialCoord) + if distance > 100 then + self:Move(self.InitialCoord, false) + _engage=false + else + self:CombatReady() + end + end + + + if self:is("Arrived") then + + end + + -- Engage targets. + if _engage then + + -- Get a timed target if it is due to be attacked. + local _timedTarget=self:_CheckTimedTargets() + local _normalTarget=self:_CheckNormalTargets() + + -- Engage target. + if _timedTarget then + if self.currentTarget then + self:CeaseFire() + end + self:OpenFire(_timedTarget) + elseif _normalTarget then + self:OpenFire(_normalTarget) + end + + end + + -- Call status again in 5 sec. + self:__Status(5) +end + +--- Check all timed targets and return the target which should be attacked next. +-- @param #ARTY self +-- @return #table Target which is due to be attacked now. +function ARTY:_CheckTimedTargets() + + -- Current time. + local Tnow=timer.getAbsTime() + + -- Sort Targets wrt time. + self:_SortTargetQueueTime() + + for i=1,#self.targets do + local _target=self.targets[i] + + -- Check if target has an attack time which has already passed. + -- Also check that target is not under fire already and that it is in range. + if _target.time and _target.time>=Tnow and _target.underfire==false and self:_TargetInRange(_target) then + + -- Check if group currently has a target and whether its priorty is lower than the timed target. + if self.currentTarget then + if self.currentTarget.prio > _target.prio then + -- Current target under attack but has lower priority than this target. + self:T(ARTY.id..string.format("Group %s current target %s has lower prio than new target %s with attack time.", self.Controllable:GetName(), self.currentTarget.name, target.name)) + return _target + end + else + -- No current target. + return _target + end + end + end + + return nil +end + +--- Check all normal (untimed) targets and return the target with the highest priority which has been engaged the fewest times. +-- @param #ARTY self +-- @return #table Target which is due to be attacked now or nil if no target could be found. +function ARTY:_CheckNormalTargets() + + -- Sort targets w.r.t. prio and number times engaged already. + self:_SortTargetQueuePrio() + + -- Loop over all sorted targets. + for i=1,#self.targets do + local _target=self.targets[i] + + -- Check that target no time, is not under fire currently and in range. + if _target.underfire==false and _target.time==nil and _target.maxengage > _target.engaged and self:_TargetInRange(_target) then + + -- Debug info. + self:T(ARTY.id..string.format("Engaging target %s. Prio = %d, engaged = %d", _target.name, _target.prio, _target.engaged)) + + return _target + end + end + + return nil +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Enter "CombatReady" state. Route the group back if necessary. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function ARTY:onenterCombatReady(Controllable, From, Event, To) + + env.info(string.format("FF: onenterComabReady, from=%s, event=%s, to=%s", From, Event, To)) + +--[[ + if From=="Rearming" and Event=="Rearmed" then + env.info("FF: Comabatready after Rearmed") + + -- Distance to initial position. + local dist=Controllable:GetCoordinate():Get2DDistance(self.InitialCoord) + + if dist>100 then + -- Route group back to its original position, when rearming was at another place. + self:T(ARTY.id..string.format("%s is routed back to its initial position. Distance = %d m.", Controllable:GetName(), dist)) + self:__Move(30, self.InitialCoord, true) + end + + else + + -- Update target queue and open fire. + env.info("FF: Comabatready ==> _openfireontarget.") + self:_openfireontarget() + + end +]] + +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + --- Before "OpenFire" event. Checks if group already has a target. Checks for valid min/max range and removes the target if necessary. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. @@ -976,20 +1233,7 @@ end -- @return #boolean If true, proceed to onafterOpenfire. function ARTY:onbeforeOpenFire(Controllable, From, Event, To, target) self:_EventFromTo("onbeforeOpenFire", Event, From, To) - - -- If this target has an attack time and it's prio is higher than the current task, we allow the transition. - if target.time~=nil and self.currentTarget~=nil and self.currentTarget.prio > target.prio then - -- Debug info. - self:T(ARTY.id..string.format("Group %s current target %s has lower prio than new target %s with attack time.", self.Controllable:GetName(), self.currentTarget.name, target.name)) - - -- Stop firing on current target. - self:CeaseFire(self.currentTarget) - - -- Alow transition to onafterOpenfire. - return true - end - - + -- Check that group has no current target already. if self.currentTarget then -- Debug info. @@ -999,25 +1243,9 @@ function ARTY:onbeforeOpenFire(Controllable, From, Event, To, target) return false end --- Distance to target - local range=Controllable:GetCoordinate():Get2DDistance(target.coord) - - -- Check that distance to target is within range. - if rangeself.maxrange then - - -- Debug output. - local text - if rangeself.maxrange then - text=string.format("%s, target is out of range. Distance of %d km is greater than max range of %d km.", Controllable:GetName(), range/1000, self.maxrange/1000) - end - self:T(ARTY.id..text) - MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) - - -- Remove target. - self:RemoveTarget(target.name) - + -- Check if target is in range. + local _inrange=self:_TargetInRange(target) + if not _inrange then -- Deny transition. return false end @@ -1064,6 +1292,36 @@ function ARTY:onafterOpenFire(Controllable, From, Event, To, target) end +--- Go through queue of assigned tasks and trigger OpenFire event. +-- @param #ARTY self +function ARTY:_openfireontarget() + self:F2() + + -- Debug info + self:T2(ARTY.id..string.format("Group %s, number of targets = %d", self.Controllable:GetName(), #self.targets)) + + -- No targets assigned at the moment. + if #self.targets==0 then + self:T3(ARTY.id..string.format("Group %s, no targets assigned at the moment. No need for _OpenFire.", self.Controllable:GetName())) + return + end + + -- Check timed targets first. + local _target=self:_CheckTimedTargets() + if _target then + self:__OpenFire(1, _target) + return + end + + -- Check normal targets + local _target=self:_CheckNormalTargets() + if _target then + self:__OpenFire(1, _target) + return + end + +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- After "CeaseFire" event. Clears task of the group and removes the target if max engagement was reached. @@ -1075,85 +1333,48 @@ end -- @param #table target Array holding the target info. function ARTY:onafterCeaseFire(Controllable, From, Event, To, target) self:_EventFromTo("onafterCeaseFire", Event, From, To) - - -- Send message. - local text=string.format("%s, ceasing fire on target %s.", Controllable:GetName(), target.name) - self:T(ARTY.id..text) - MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report) - - -- Get target array index. - local id=self:_GetTargetByName(target.name) - -- Increase engaged counter - if id then - -- Target was actually engaged. (Could happen that engagement was aborted while group was still aiming.) - if self.Nshots>0 then - self.targets[id].engaged=self.targets[id].engaged+1 - -- Clear the attack time. - self.targets[id].time=nil + if target then + + -- Send message. + local text=string.format("%s, ceasing fire on target %s.", Controllable:GetName(), target.name) + self:T(ARTY.id..text) + MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report) + + -- Get target array index. + local id=self:_GetTargetByName(target.name) + + -- Increase engaged counter + if id then + -- Target was actually engaged. (Could happen that engagement was aborted while group was still aiming.) + if self.Nshots>0 then + self.targets[id].engaged=self.targets[id].engaged+1 + -- Clear the attack time. + self.targets[id].time=nil + end + -- Target is not under fire any more. + self.targets[id].underfire=false end - -- Target is not under fire any more. - self.targets[id].underfire=false - end - - -- Clear tasks. - self.Controllable:ClearTasks() + -- If number of engagements has been reached, the target is removed. + if target.engaged >= target.maxengage then + self:RemoveTarget(target.name) + end + + -- Clear tasks. + self.Controllable:ClearTasks() + + end + -- Set number of shots to zero. self.Nshots=0 - - -- If number of engagements has been reached, the target is removed. - if target.engaged >= target.maxengage then - self:RemoveTarget(target.name) - end -- ARTY group has no current target any more. self.currentTarget=nil end ---- Enter "CombatReady" state. Route the group back if necessary. --- @param #ARTY self --- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function ARTY:onenterCombatReady(Controllable, From, Event, To) - - env.info(string.format("FF: onenterComabReady, from=%s, event=%s, to=%s", From, Event, To)) - - if From=="Rearming" and Event=="Rearmed" then - env.info("FF: Comabatready after Rearmed") - - -- Distance to initial position. - local dist=Controllable:GetCoordinate():Get2DDistance(self.InitialCoord) - - if dist>100 then - -- Route group back to its original position, when rearming was at another place. - self:T(ARTY.id..string.format("%s is routed back to its initial position. Distance = %d m.", Controllable:GetName(), dist)) - self:__Move(30, self.InitialCoord, true) - end - - else - -- Update target queue and open fire. - env.info("FF: Comabatready ==> _openfireontarget.") - self:_openfireontarget() - end - -end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Before "Winchester" event. Cease fire on current target. --- @param #ARTY self --- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @return #boolean If true, proceed to onafterWinchester. -function ARTY:onbeforeWinchester(Controllable, From, Event, To) - - return true -end --- After "Winchester" event. Group is out of ammo. Trigger "Rearm" event. -- @param #ARTY self @@ -1170,7 +1391,7 @@ function ARTY:onafterWinchester(Controllable, From, Event, To) MESSAGE:New(text, 30):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) -- Init rearming if possible. - self:Rearm() + --self:Rearm() end @@ -1187,7 +1408,7 @@ function ARTY:onbeforeRearm(Controllable, From, Event, To) self:_EventFromTo("onbeforeRearm", Event, From, To) -- Check if a reaming unit or rearming place was specified. - if self.RearmingUnit and self.RearmingUnit:IsAlive() then + if self.RearmingGroup and self.RearmingGroup:IsAlive() then return true elseif self.RearmingPlaceCoord then return true @@ -1206,75 +1427,6 @@ end function ARTY:onafterRearm(Controllable, From, Event, To) self:_EventFromTo("onafterRearm", Event, From, To) - -- Coordinate of ARTY unit. - local coordARTY=self.Controllable:GetCoordinate() - - -- Coordinate of rearming unit. - local coordRARM=nil - if self.RearmingUnit then - -- Coordinate of the rearming unit. - coordRARM=self.RearmingUnit:GetCoordinate() - -- Remember the coordinates of the rearming unit. After rearming it will go back to this position. - self.RearmingUnitCoord=coordRARM - end - - if self.RearmingUnit and self.RearmingPlaceCoord and self.Speed>0 then - - -- CASE 1: Rearming unit and ARTY group meet at rearming place. - - -- Distances. - local dA=coordARTY:Get2DDistance(self.RearmingPlaceCoord) - local dR=coordRARM:Get2DDistance(self.RearmingPlaceCoord) - - -- Route ARTY group to rearming place. - if dA>100 then - --self.Controllable:RouteGroundOnRoad(self.RearmingPlaceCoord, self.Speed, 1) - self:_Move(self.Controllable, self.RearmingPlaceCoord, self.Speed, true) - end - - -- Route Rearming unit to rearming place - if dR>100 then - self.RearmingUnit:RouteGroundOnRoad(self.RearmingPlaceCoord, 50, 1) - --self:_Move(self.RearmingUnit, self.RearmingPlaceCoord, 50, true) - end - - elseif self.RearmingUnit then - - -- CASE 2: Rearming unit drives to ARTY group. - - -- Send message. - local text=string.format("%s, %s, request rearming.", Controllable:GetName(), self.RearmingUnit:GetName()) - self:T(ARTY.id..text) - MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) - - -- Distance between ARTY group and rearming unit. - local distance=coordARTY:Get2DDistance(coordRARM) - - -- If distance is larger than 100 m, the Rearming unit is routed to the ARTY group. - if distance > 100 then - -- Random point 20-100 m away from unit. - local vec2=coordARTY:GetRandomVec2InRadius(20, 100) - local pops=COORDINATE:NewFromVec2(vec2) - - -- Route unit to ARTY group. - self.RearmingUnit:RouteGroundOnRoad(pops, 50, 1) - end - - elseif self.RearmingPlaceCoord then - - -- CASE 3: ARTY drives to rearming place. - - -- Distance. - local dA=coordARTY:Get2DDistance(self.RearmingPlaceCoord) - - -- Route ARTY group to rearming place. - if dA>100 then - --self.Controllable:RouteGroundOnRoad(self.RearmingPlaceCoord, self.Speed, 1) - self:_Move(self.Controllable, self.RearmingPlaceCoord, self.Speed, true) - end - - end - -- Start scheduler to monitor ammo count until rearming is complete. self.SchedIDCheckRearmed=self.scheduler:Schedule(self, ARTY._CheckRearmed, {self}, 20, 20) end @@ -1307,10 +1459,10 @@ function ARTY:onafterRearmed(Controllable, From, Event, To) end -- Route unit back to where it came from (if distance is > 100 m). - if self.RearmingUnit and self.RearmingUnit:IsAlive() then - local d=self.RearmingUnit:GetCoordinate():Get2DDistance(self.RearmingUnitCoord) + if self.RearmingGroup and self.RearmingGroup:IsAlive() then + local d=self.RearmingGroup:GetCoordinate():Get2DDistance(self.RearmingGroupCoord) if d>100 then - self.RearmingUnit:RouteGroundOnRoad(self.RearmingUnitCoord, 50, 1) + self.RearmingGroup:RouteGroundOnRoad(self.RearmingGroupCoord, 50, 1) end end @@ -1349,7 +1501,6 @@ function ARTY:_CheckRearmed() end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Before "Move" event. Check if a unit to rearm the ARTY group has been defined. @@ -1416,7 +1567,6 @@ function ARTY:onafterArrived(Controllable, From, Event, To) end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- After "Dead" event, when a unit has died. When all units of a group are dead trigger "Stop" event. @@ -1450,24 +1600,6 @@ function ARTY:onafterDead(Controllable, From, Event, To) end ---- Before "Stop" event. Cease fire on current target. --- @param #ARTY self --- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @return #boolean If true, proceed to onafterStop. -function ARTY:onbeforeStop(Controllable, From, Event, To) - self:_EventFromTo("onbeforeStop", Event, From, To) - - -- Cease Fire on current target. - if self.currentTarget then - self:CeaseFire(self.currentTarget) - end - - return true -end - --- After "Stop" event. Stop schedulers and unhandle events. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. @@ -1479,8 +1611,15 @@ function ARTY:onafterStop(Controllable, From, Event, To) -- Debug info. self:T(ARTY.id..string.format("Stopping ARTY FSM for group %s.", Controllable:GetName())) + + -- Cease Fire on current target. + if self.currentTarget then + self:CeaseFire(self.currentTarget) + end + -- Remove all targets. --self:RemoveAllTargets() + -- Stop schedulers. if self.SchedIDCheckShooting then self.scheduler:Stop(self.SchedIDCheckShooting) @@ -1488,6 +1627,7 @@ function ARTY:onafterStop(Controllable, From, Event, To) if self.SchedIDCheckRearmed then self.scheduler:Stop(self.SchedIDCheckRearmed) end + -- Unhandle event. self:UnHandleEvent(EVENTS.Shot) self:UnHandleEvent(EVENTS.Dead) @@ -1524,61 +1664,6 @@ function ARTY:_FireAtCoord(coord, radius, nshells, weapontype) end ---- Go through queue of assigned tasks and trigger OpenFire event. --- @param #ARTY self -function ARTY:_openfireontarget() - self:F2() - - -- Debug info - self:T(ARTY.id..string.format("Group %s, number of targets = %d", self.Controllable:GetName(), #self.targets)) - - -- No targets assigned at the moment. - if #self.targets==0 then - self:T3(ARTY.id..string.format("Group %s, no targets assigned at the moment. No need for _OpenFire.", self.Controllable:GetName())) - return - end - - -- First check if there is a target with a certain time for attack. - for i=1,#self.targets do - local _target=self.targets[i] - if _target and _target.time then - if timer.getAbsTime() >= _target.time and _target.underfire==false then - - -- Clock time format. - local _clock=self:_SecondsToClock(_target.time) - local _Cnow=self:_SecondsToClock(timer.getAbsTime()) - - -- Debug info. - self:T(ARTY.id..string.format("Engaging timed target %s. Prio=%d, engaged=%d, time=%s, tnow=%s",_target.name,_target.prio,_target.engaged,_clock,_Cnow)) - - -- Call OpenFire event. - self:__OpenFire(1, _target) - - end - end - end - - -- Sort targets w.r.t. prio and number times engaged already. - self:_SortTargetQueuePrio() - - -- Loop over all sorted targets. - for i=1,#self.targets do - - local _target=self.targets[i] - - if _target.underfire==false and _target.time==nil and _target.maxengage > _target.engaged then - - -- Debug info. - self:T(ARTY.id..string.format("Engaging target %s. Prio = %d, engaged = %d", _target.name, _target.prio, _target.engaged)) - - -- Call OpenFire event. - self:__OpenFire(1, _target) - - end - end - -end - --- Sort targets with respect to priority and number of times it was already engaged. -- @param #ARTY self @@ -1594,7 +1679,9 @@ function ARTY:_SortTargetQueuePrio() -- Debug output. self:T2(ARTY.id.."Sorted targets wrt prio and number of engagements:") for i=1,#self.targets do - self:T2(ARTY.id..string.format("Target %s, prio=%d, engaged=%d", self.targets[i].name, self.targets[i].prio, self.targets[i].engaged)) + local _target=self.targets[i] + local _clock=self:_SecondsToClock(_target.time) + self:T2(ARTY.id..string.format("Target %s, prio=%d, engaged=%d, time=%s", _target.name, _target.prio, _target.engaged, tostring(_clock))) end end @@ -1621,7 +1708,9 @@ function ARTY:_SortTargetQueueTime() -- Debug output. self:T2(ARTY.id.."Sorted targets wrt time:") for i=1,#self.targets do - self:T2(ARTY.id..string.format("Target %s, prio=%d, engaged=%d", self.targets[i].name, self.targets[i].prio, self.targets[i].engaged)) + local _target=self.targets[i] + local _clock=self:_SecondsToClock(_target.time) + self:T2(ARTY.id..string.format("Target %s, prio=%d, engaged=%d, time=%s", _target.name, _target.prio, _target.engaged, tostring(_clock))) end end @@ -1846,6 +1935,42 @@ function ARTY:_CheckTargetName(name) return newname end +--- Check if target is in range. +-- @param #ARTY self +-- @param #table target Target table. +-- @return #boolean True if target is in range, false otherwise. +function ARTY:_TargetInRange(target) + self:F3(target) + + -- Distance between ARTY group and target. + local _dist=self.Controllable:GetCoordinate():Get2DDistance(target.coord) + + -- Assume we are in range. + local _inrange=true + local text="" + + if _dist < self.minrange then + _inrange=false + text=string.format("%s, target is out of range. Distance of %d km is below min range of %d km.", self.Controllable:GetName(), _dist/1000, self.minrange/1000) + elseif _dist > self.maxrange then + _inrange=false + text=string.format("%s, target is out of range. Distance of %d km is greater than max range of %d km.", self.Controllable:GetName(), _dist/1000, self.maxrange/1000) + end + + -- Debug output. + if not _inrange then + self:T(ARTY.id..text) + MESSAGE:New(text, 10):ToCoalitionIf(self.Controllable:GetCoalition(), self.report or self.Debug) + end + + -- Remove target if ARTY group cannot move. No change to be ever in range. + if self.Speed==0 then + self:RemoveTarget(target.name) + end + + return _inrange +end + --- Get the weapon type name, which should be used to attack the target. -- @param #ARTY self -- @param #number tnumber Number of weapon type ARTY.WeaponType.XXX @@ -1880,7 +2005,6 @@ function ARTY:_EventFromTo(BA, Event, From, To) self:T3(ARTY.id..text) end - --- Split string. C.f. http://stackoverflow.com/questions/1426954/split-string-in-lua -- @param #ARTY self -- @param #string str Sting to split. From 57c5ab1ecd50e5035abecd192291f27480322452 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sat, 5 May 2018 21:45:07 +0200 Subject: [PATCH 076/420] ARTY v0.8.1 Improvements in FSM states. Still not quite functional. --- .../Moose/Functional/Artillery.lua | 508 ++++++------------ 1 file changed, 156 insertions(+), 352 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index aaf1a77b9..3ea6059c6 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -269,7 +269,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #number version -ARTY.version="0.8.0" +ARTY.version="0.8.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -339,8 +339,8 @@ function ARTY:New(group) self:T3({id=id, desc=desc}) end - -- Set speed to 1/2 of maximum in km/h. - self.Speed=self.DCSdesc.speedMax*3.6 * 0.5 + -- Set speed to 0.7 of maximum in km/h. + self.Speed=self.DCSdesc.speedMax*3.6 * 0.7 -- Displayed name (similar to type name below) self.DisplayName=self.DCSdesc.displayName @@ -353,199 +353,38 @@ function ARTY:New(group) -- Initial group strength. self.IniGroupStrength=#group:GetUnits() - - -- Transitions: - -- Entry + + --------------- + -- Transitions: + --------------- + + -- Entry. self:AddTransition("*", "Start", "CombatReady") -- Blue branch. self:AddTransition("CombatReady", "OpenFire", "Firing") - self:AddTransition("Firing", "OpenFire", "Firing") self:AddTransition("Firing", "CeaseFire", "CombatReady") - --self:AddTransition("CombatReady", "CeaseFire", "CombatReady") -- not in diagram yet. -- Violett branch. - self:AddTransition("Firing", "Winchester", "OutOfAmmo") + self:AddTransition("CombatReady", "Winchester", "OutOfAmmo") -- Red branch. - self:AddTransition("OutOfAmmo", "Rearm", "Rearming") - self:AddTransition("Rearming", "Rearmed", "Rearmed") + self:AddTransition({"CombatReady", "OutOfAmmo"}, "Rearm", "Rearming") + self:AddTransition("Rearming", "Move", "Rearming") + self:AddTransition("Rearming", "Rearmed", "Rearmed") -- Green branch. self:AddTransition("*", "Move", "Moving") self:AddTransition("Moving", "Arrived", "Arrived") - self:AddTransition("*", "CombatReady", "CombatReady") - -- Yellow branch. self:AddTransition("*", "NewTarget", "*") + -- Not in diagram. + self:AddTransition("*", "CombatReady", "CombatReady") self:AddTransition("*", "Status", "*") self:AddTransition("*", "Dead", "*") - - --- User function for OnBefore "OpenFire" event. - -- @function [parent=#ARTY] OnBeforeOpenFire - -- @param #ARTY self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - -- @param #table target Array holding the target info. - -- @return #boolean If true, allow transition to OnAfterOpenFire. - - --- User function for OnAfter "OpenFire" event. - -- @function [parent=#ARTY] OnAfterOpenFire - -- @param #ARTY self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - -- @param #table target Array holding the target info. - - - --- User function for OnBefore "CeaseFire" event. - -- @function [parent=#ARTY] OnBeforeCeaseFire - -- @param #ARTY self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - -- @param #table target Array holding the target info. - -- @return #boolean If true, allow transition to OnAfterCeaseFire. - - --- User function for OnAfter "CeaseFire" event. - -- @function [parent=#ARTY] OnAfterCeaseFire - -- @param #ARTY self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - -- @param #table target Array holding the target info. - - - --- User function for OnBefore "Winchester" event. - -- @function [parent=#ARTY] OnBeforeWinchester - -- @param #ARTY self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - -- @return #boolean If true, allow transition to OnAfterWinchester. - - --- User function for OnAfter "Winchester" event. - -- @function [parent=#ARTY] OnAfterWinchester - -- @param #ARTY self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - - - --- User function for OnBefore "Rearm" event. - -- @function [parent=#ARTY] OnBeforeRearm - -- @param #ARTY self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - -- @return #boolean If true, allow transition to OnAfterRearm. - - --- User function for OnAfter "Rearm" event. - -- @function [parent=#ARTY] OnAfterRearm - -- @param #ARTY self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - - - --- User function for OnBefore "Rearmed" event. - -- @function [parent=#ARTY] OnBeforeRearmed - -- @param #ARTY self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - -- @return #boolean If true, allow transition to OnAfterRearmed. - - --- User function for OnAfter "Rearmed" event. - -- @function [parent=#ARTY] OnAfterRearmed - -- @param #ARTY self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - - - --- User function for OnBefore "Start" event. - -- @function [parent=#ARTY] OnBeforeStart - -- @param #ARTY self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - -- @return #boolean If true, allow transition to OnAfterStart. - - --- User function for OnAfter "Start" event. - -- @function [parent=#ARTY] OnAfterStart - -- @param #ARTY self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - - - --- User function for OnBefore "Dead" event. - -- @function [parent=#ARTY] OnBeforeDead - -- @param #ARTY self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - -- @return #boolean If true, allow transition to OnAfterDead. - - --- User function for OnAfter "Dead" event. - -- @function [parent=#ARTY] OnAfterDead - -- @param #ARTY self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - - - --- User function for OnEnter "CombatReady" state. - -- @function [parent=#ARTY] OnEnterCombatReady - -- @param #ARTY self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - - --- User function for OnEnter "Firing" state. - -- @function [parent=#ARTY] OnEnterFiring - -- @param #ARTY self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - - --- User function for OnEnter "OutOfAmmo" state. - -- @function [parent=#ARTY] OnEnterOutOfAmmo - -- @param #ARTY self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - - --- User function for OnEnter "Rearming" state. - -- @function [parent=#ARTY] OnEnterRearming - -- @param #ARTY self - -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - return self end @@ -598,13 +437,12 @@ function ARTY:AssignTargetCoord(coord, prio, radius, nshells, maxengage, time, w local _clock=self:_SecondsToClock(_target.time) -- Debug info. - self:T(ARTY.id..string.format("Added target %s, prio=%d, radius=%d, nshells=%d, maxengage=%d, time=%s, weapontype=%d", name, prio, radius, nshells, maxengage, tostring(_clock), weapontype)) + self:T(ARTY.id..string.format("Added target %s", self:_TargetInfo(_target))) -- Trigger new target event. self:NewTarget(_target) end - --- Set minimum firing range. Targets closer than this distance are not engaged. -- @param #ARTY self -- @param #number range Min range in kilometers. Default is 0 km. @@ -891,6 +729,7 @@ function ARTY:_OnEventShot(EventData) if _nammo==0 then self:T(ARTY.id..string.format("Group %s completely out of ammo.", self.Controllable:GetName())) + self:CeaseFire(self.currentTarget) self:Winchester() -- Current target is deallocated ==> return @@ -970,23 +809,6 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- After "NewTarget" event. --- @param #ARTY self --- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param #table target Array holding the target info. --- @return #boolean If true, proceed to onafterOpenfire. -function ARTY:onafterNewTarget(Controllable, From, Event, To, target) - self:_EventFromTo("onafterNewTarget", Event, From, To) - - -- Debug message. - local text=string.format("Adding new target %s.", target.name) - MESSAGE:New(text, 30):ToAllIf(self.Debug) - self:T(ARTY.id..text) -end - --- After "Status" event. Report status of group. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. @@ -1000,98 +822,27 @@ function ARTY:onafterStatus(Controllable, From, Event, To) self:_StatusReport() end - local _engage=true - if self:is("OutOfAmmo") then - - -- Coordinate of ARTY unit. - local coordARTY=self.Controllable:GetCoordinate() - - -- Coordinate of rearming group. - local coordRARM=nil - if self.RearmingGroup then - -- Coordinate of the rearming unit. - coordRARM=self.RearmingGroup:GetCoordinate() - -- Remember the coordinates of the rearming unit. After rearming it will go back to this position. - self.RearmingGroupCoord=coordRARM - end - - if self.RearmingGroup and self.RearmingPlaceCoord and self.Speed>0 then - - -- CASE 1: Rearming unit and ARTY group meet at rearming place. - - -- Distances. - local dA=coordARTY:Get2DDistance(self.RearmingPlaceCoord) - local dR=coordRARM:Get2DDistance(self.RearmingPlaceCoord) - - -- Route ARTY group to rearming place. - if dA>100 then - --self.Controllable:RouteGroundOnRoad(self.RearmingPlaceCoord, self.Speed, 1) - --self:_Move(self.Controllable, self.RearmingPlaceCoord, self.Speed, true) - self:Move(self.RearmingPlaceCoord, false) - end - - -- Route Rearming unit to rearming place - if dR>100 then - self.RearmingGroup:RouteGroundOnRoad(self.RearmingPlaceCoord, 50, 1) - --self:_Move(self.RearmingGroup, self.RearmingPlaceCoord, 50, true) - end - - elseif self.RearmingGroup then - - -- CASE 2: Rearming unit drives to ARTY group. - - -- Send message. - local text=string.format("%s, %s, request rearming.", Controllable:GetName(), self.RearmingGroup:GetName()) - self:T(ARTY.id..text) - MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) - - -- Distance between ARTY group and rearming unit. - local distance=coordARTY:Get2DDistance(coordRARM) - - -- If distance is larger than 100 m, the Rearming unit is routed to the ARTY group. - if distance > 100 then - -- Random point 20-100 m away from unit. - local vec2=coordARTY:GetRandomVec2InRadius(20, 100) - local pops=COORDINATE:NewFromVec2(vec2) - - -- Route unit to ARTY group. - self.RearmingGroup:RouteGroundOnRoad(pops, 50, 1) - end - - elseif self.RearmingPlaceCoord then - - -- CASE 3: ARTY drives to rearming place. - - -- Distance. - local dA=coordARTY:Get2DDistance(self.RearmingPlaceCoord) - - -- Route ARTY group to rearming place. - if dA>100 then - --self.Controllable:RouteGroundOnRoad(self.RearmingPlaceCoord, self.Speed, 1) - --self:_Move(self.Controllable, self.RearmingPlaceCoord, self.Speed, true) - self:Move(self.RearmingPlaceCoord, false) - end - - end - - _engage=false - + self:Rearm() end if self:is("Moving") then - _engage=false + --self.Controllable:GetVelocityKMH() end if self:is("Rearming") then - _engage=false + local _rearmed=self:_CheckRearmed() + env.info("FF: Rearming. _rearmed = ", tostring(_rearmed)) + if _rearmed then + self:Rearmed() + end end if self:is("Rearmed") then local distance=self.Controllable:GetCoordinate():Get2DDistance(self.InitialCoord) + env.info("FF: Rearmed. Distance ARTY to InitalCoord = ", distance) if distance > 100 then - self:Move(self.InitialCoord, false) - _engage=false + --self:Move(self.InitialCoord, false) else self:CombatReady() end @@ -1099,11 +850,11 @@ function ARTY:onafterStatus(Controllable, From, Event, To) if self:is("Arrived") then - + self:CombatReady() end -- Engage targets. - if _engage then + if self:is("CombatReady") then -- Get a timed target if it is due to be attacked. local _timedTarget=self:_CheckTimedTargets() @@ -1112,7 +863,7 @@ function ARTY:onafterStatus(Controllable, From, Event, To) -- Engage target. if _timedTarget then if self.currentTarget then - self:CeaseFire() + self:CeaseFire(self.currentTarget) end self:OpenFire(_timedTarget) elseif _normalTarget then @@ -1147,7 +898,7 @@ function ARTY:_CheckTimedTargets() if self.currentTarget then if self.currentTarget.prio > _target.prio then -- Current target under attack but has lower priority than this target. - self:T(ARTY.id..string.format("Group %s current target %s has lower prio than new target %s with attack time.", self.Controllable:GetName(), self.currentTarget.name, target.name)) + self:T(ARTY.id..string.format("Group %s current target %s has lower prio than new target %s with attack time.", self.Controllable:GetName(), self.currentTarget.name, _target.name)) return _target end else @@ -1197,28 +948,6 @@ function ARTY:onenterCombatReady(Controllable, From, Event, To) env.info(string.format("FF: onenterComabReady, from=%s, event=%s, to=%s", From, Event, To)) ---[[ - if From=="Rearming" and Event=="Rearmed" then - env.info("FF: Comabatready after Rearmed") - - -- Distance to initial position. - local dist=Controllable:GetCoordinate():Get2DDistance(self.InitialCoord) - - if dist>100 then - -- Route group back to its original position, when rearming was at another place. - self:T(ARTY.id..string.format("%s is routed back to its initial position. Distance = %d m.", Controllable:GetName(), dist)) - self:__Move(30, self.InitialCoord, true) - end - - else - - -- Update target queue and open fire. - env.info("FF: Comabatready ==> _openfireontarget.") - self:_openfireontarget() - - end -]] - end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1236,16 +965,16 @@ function ARTY:onbeforeOpenFire(Controllable, From, Event, To, target) -- Check that group has no current target already. if self.currentTarget then - -- Debug info. - self:T2(ARTY.id..string.format("Group %s already has a target %s.", self.Controllable:GetName(), self.currentTarget.name)) - + -- This should not happen. Some earlier check failed. + self:E(ARTY.id..string.format("ERROR: Group %s already has a target %s!", self.Controllable:GetName(), self.currentTarget.name)) -- Deny transition. return false end -- Check if target is in range. - local _inrange=self:_TargetInRange(target) - if not _inrange then + if not self:_TargetInRange(target) then + -- This should not happen. Some earlier check failed. + self:E(ARTY.id..string.format("ERROR: Group %s, target %s is out of range!", self.Controllable:GetName(), self.currentTarget.name)) -- Deny transition. return false end @@ -1292,36 +1021,6 @@ function ARTY:onafterOpenFire(Controllable, From, Event, To, target) end ---- Go through queue of assigned tasks and trigger OpenFire event. --- @param #ARTY self -function ARTY:_openfireontarget() - self:F2() - - -- Debug info - self:T2(ARTY.id..string.format("Group %s, number of targets = %d", self.Controllable:GetName(), #self.targets)) - - -- No targets assigned at the moment. - if #self.targets==0 then - self:T3(ARTY.id..string.format("Group %s, no targets assigned at the moment. No need for _OpenFire.", self.Controllable:GetName())) - return - end - - -- Check timed targets first. - local _target=self:_CheckTimedTargets() - if _target then - self:__OpenFire(1, _target) - return - end - - -- Check normal targets - local _target=self:_CheckNormalTargets() - if _target then - self:__OpenFire(1, _target) - return - end - -end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- After "CeaseFire" event. Clears task of the group and removes the target if max engagement was reached. @@ -1427,10 +1126,74 @@ end function ARTY:onafterRearm(Controllable, From, Event, To) self:_EventFromTo("onafterRearm", Event, From, To) - -- Start scheduler to monitor ammo count until rearming is complete. - self.SchedIDCheckRearmed=self.scheduler:Schedule(self, ARTY._CheckRearmed, {self}, 20, 20) + -- Coordinate of ARTY unit. + local coordARTY=self.Controllable:GetCoordinate() + + -- Coordinate of rearming group. + local coordRARM=nil + if self.RearmingGroup then + -- Coordinate of the rearming unit. + coordRARM=self.RearmingGroup:GetCoordinate() + -- Remember the coordinates of the rearming unit. After rearming it will go back to this position. + self.RearmingGroupCoord=coordRARM + end + + if self.RearmingGroup and self.RearmingPlaceCoord and self.Speed>0 then + + -- CASE 1: Rearming unit and ARTY group meet at rearming place. + + -- Distances. + local dA=coordARTY:Get2DDistance(self.RearmingPlaceCoord) + local dR=coordRARM:Get2DDistance(self.RearmingPlaceCoord) + + -- Route ARTY group to rearming place. + if dA>100 then + --self:_Move(self.Controllable, self.RearmingPlaceCoord, self.Speed, true) + self:Move(self:_VicinityCoord(self.RearmingPlaceCoord, 20, 50), false) + end + + -- Route Rearming unit to rearming place + if dR>100 then + self:_Move(self.RearmingGroup, self:_VicinityCoord(self.RearmingPlaceCoord, 20, 50), 50, false) + end + + elseif self.RearmingGroup then + + -- CASE 2: Rearming unit drives to ARTY group. + + -- Send message. + local text=string.format("%s, %s, request rearming.", Controllable:GetName(), self.RearmingGroup:GetName()) + self:T(ARTY.id..text) + MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) + + -- Distance between ARTY group and rearming unit. + local distance=coordARTY:Get2DDistance(coordRARM) + + -- If distance is larger than 100 m, the Rearming unit is routed to the ARTY group. + if distance > 100 then + + -- Route unit to ARTY group. + self:_Move(self.RearmingGroup, self:_VicinityCoord(coordARTY), 50, false) + end + + elseif self.RearmingPlaceCoord then + + -- CASE 3: ARTY drives to rearming place. + + -- Distance. + local dA=coordARTY:Get2DDistance(self.RearmingPlaceCoord) + + -- Route ARTY group to rearming place. + if dA>100 then + --self:_Move(self.Controllable, self.RearmingPlaceCoord, self.Speed, true) + self:Move(self:_VicinityCoord(self.RearmingPlaceCoord), false) + end + + end + end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- After "Rearmed" event. Send message if reporting is on and stop the scheduler. @@ -1447,29 +1210,25 @@ function ARTY:onafterRearmed(Controllable, From, Event, To) self:T(ARTY.id..text) MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) - -- Stop scheduler. - if self.SchedIDCheckRearmed then - self.scheduler:Stop(self.SchedIDCheckRearmed) - end - -- Route ARTY group backto where it came from (if distance is > 100 m). local d1=self.Controllable:GetCoordinate():Get2DDistance(self.InitialCoord) if d1>100 then - self.Controllable:RouteGroundOnRoad(self.InitialCoord, self.Speed, 5) + self:Move(self.InitialCoord, false) end -- Route unit back to where it came from (if distance is > 100 m). if self.RearmingGroup and self.RearmingGroup:IsAlive() then local d=self.RearmingGroup:GetCoordinate():Get2DDistance(self.RearmingGroupCoord) if d>100 then - self.RearmingGroup:RouteGroundOnRoad(self.RearmingGroupCoord, 50, 1) + self:_Move(self.RearmingGroup, self.RearmingGroupCoord, 50, false) end end end ---- Check if ARTY group is rearmed. +--- Check if ARTY group is rearmed, i.e. has its full amount of ammo. -- @param #ARTY self +-- @return #boolean True if rearming is complete, false otherwise. function ARTY:_CheckRearmed() self:F2() @@ -1490,13 +1249,17 @@ function ARTY:_CheckRearmed() local _rearmpc=nammo/self.FullAmmo*100 -- Send message. - local text=string.format("%s, rearming %d %% complete.", self.Controllable:GetName(), _rearmpc) - self:T(ARTY.id..text) - MESSAGE:New(text, 10):ToCoalitionIf(self.Controllable:GetCoalition(), self.report or self.Debug) - + if _rearmpc>1 then + local text=string.format("%s, rearming %d %% complete.", self.Controllable:GetName(), _rearmpc) + self:T(ARTY.id..text) + MESSAGE:New(text, 10):ToCoalitionIf(self.Controllable:GetCoalition(), self.report or self.Debug) + end + -- Rearming --> Rearmed --> CombatReady if nammo==self.FullAmmo then - self:Rearmed() + return true + else + return false end end @@ -1569,6 +1332,23 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- After "NewTarget" event. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #table target Array holding the target info. +-- @return #boolean If true, proceed to onafterOpenfire. +function ARTY:onafterNewTarget(Controllable, From, Event, To, target) + self:_EventFromTo("onafterNewTarget", Event, From, To) + + -- Debug message. + local text=string.format("Adding new target %s.", target.name) + MESSAGE:New(text, 30):ToAllIf(self.Debug) + self:T(ARTY.id..text) +end + --- After "Dead" event, when a unit has died. When all units of a group are dead trigger "Stop" event. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. @@ -1625,7 +1405,7 @@ function ARTY:onafterStop(Controllable, From, Event, To) self.scheduler:Stop(self.SchedIDCheckShooting) end if self.SchedIDCheckRearmed then - self.scheduler:Stop(self.SchedIDCheckRearmed) + --self.scheduler:Stop(self.SchedIDCheckRearmed) end -- Unhandle event. @@ -1994,6 +1774,22 @@ function ARTY:_WeaponTypeName(tnumber) return name end +--- After "Rearmed" event. Send message if reporting is on and stop the scheduler. +-- @param #ARTY self +-- @param Core.Point#COORDINATE coord Center coordinate. +-- @param #number rmin (Optional) Minimum distance in meters from center coordinate. Default 20 m. +-- @param #number rmax (Optional) Maximum distance in meters from center coordinate. Default 100 m. +-- @return Core.Point#COORDINATE Random coordinate in a certain distance from center coordinate. +function ARTY:_VicinityCoord(coord, rmin, rmax) + self:F2({coord=coord, rmin=rmin, rmax=rmax}) + rmin=rmin or 20 + rmax=rmax or 100 + -- Random point. + local vec2=coord:GetRandomVec2InRadius(rmin, rmax) + local pops=COORDINATE:NewFromVec2(vec2) + return pops +end + --- Print event-from-to string to DCS log file. -- @param #ARTY self -- @param #string BA Before/after info. @@ -2022,6 +1818,14 @@ function ARTY:_split(str, sep) return result end +--- Returns the target info as formatted string. +-- @param #ARTY self +-- @return #string name, prio, radius, nshells, engaged, maxengage, time, weapontype +function ARTY:_TargetInfo(target) + local clock=tostring(self:_SecondsToClock(target.time)) + return string.format("%s, prio=%d, radius=%d, nshells=%d, engaged=%d maxengage=%d, weapontype=%d, time=%s", target.name, target.prio, target.radius, target.nshells, target.engaged, target.maxengage, target.weapontype, clock) +end + --- Convert time in seconds to hours, minutes and seconds. -- @param #ARTY self -- @param #number seconds Time in seconds. From f33856cddd82bd8282c8010aed9f44b38e347419 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sat, 5 May 2018 22:51:43 +0200 Subject: [PATCH 077/420] ARTY v0.8.2 Fixes. Stil WIP --- .../Moose/Functional/Artillery.lua | 56 +++++++++---------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 3ea6059c6..94c24cff8 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -269,7 +269,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #number version -ARTY.version="0.8.1" +ARTY.version="0.8.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -618,7 +618,7 @@ function ARTY:onafterStart(Controllable, From, Event, To) for _, target in pairs(self.targets) do local _clock=self:_SecondsToClock(target.time) local _weapon=self:_WeaponTypeName(target.weapontype) - text=text..string.format("- %s, prio=%3d, radius=%5d, nshells=%4d, maxengage=%3d, time=%11s, weapon=%s\n", target.name, target.prio, target.radius, target.nshells, target.maxengage, tostring(_clock), _weapon) + text=text..string.format("- %s\n", self:_TargetInfo(target)) end text=text..string.format("******************************************************\n") text=text..string.format("Shell types:\n") @@ -676,10 +676,7 @@ function ARTY:_StatusReport() text=text..string.format("Nshots curr. Target = %d\n", self.Nshots) text=text..string.format("Targets:\n") for _, target in pairs(self.targets) do - local _clock=self:_SecondsToClock(target.time) - local _weapon=self:_WeaponTypeName(target.weapontype) - text=text..string.format("- %s, prio=%3d, radius=%5d, nshells=%4d, engaged=%3d, maxengage=%3d, weapon=%s, time=%s\n", - target.name, target.prio, target.radius, target.nshells, target.engaged, target.maxengage, _weapon, tostring(_clock)) + text=text..string.format("- %s\n", self:_TargetInfo(target)) end text=text..string.format("******************************************************") env.info(ARTY.id..text) @@ -890,19 +887,19 @@ function ARTY:_CheckTimedTargets() for i=1,#self.targets do local _target=self.targets[i] - -- Check if target has an attack time which has already passed. - -- Also check that target is not under fire already and that it is in range. - if _target.time and _target.time>=Tnow and _target.underfire==false and self:_TargetInRange(_target) then + -- Check if target has an attack time which has already passed. Also check that target is not under fire already and that it is in range. + if _target.time and Tnow>=_target.time and _target.underfire==false and self:_TargetInRange(_target) then -- Check if group currently has a target and whether its priorty is lower than the timed target. if self.currentTarget then if self.currentTarget.prio > _target.prio then -- Current target under attack but has lower priority than this target. - self:T(ARTY.id..string.format("Group %s current target %s has lower prio than new target %s with attack time.", self.Controllable:GetName(), self.currentTarget.name, _target.name)) + self:T(ARTY.id..string.format("Found TIMED HIGH PRIO target %s.", self:_TargetInRange(_target))) return _target end else -- No current target. + self:T(ARTY.id..string.format("Found TIMED target %s.", self:_TargetInfo(_target))) return _target end end @@ -927,7 +924,7 @@ function ARTY:_CheckNormalTargets() if _target.underfire==false and _target.time==nil and _target.maxengage > _target.engaged and self:_TargetInRange(_target) then -- Debug info. - self:T(ARTY.id..string.format("Engaging target %s. Prio = %d, engaged = %d", _target.name, _target.prio, _target.engaged)) + self:T(ARTY.id..string.format("Found NORMAL target %s", self:_TargetInfo(_target))) return _target end @@ -945,7 +942,9 @@ end -- @param #string Event Event. -- @param #string To To state. function ARTY:onenterCombatReady(Controllable, From, Event, To) - + self:_EventFromTo("onenterCombatReady", Event, From, To) + + -- Debug info env.info(string.format("FF: onenterComabReady, from=%s, event=%s, to=%s", From, Event, To)) end @@ -1088,9 +1087,6 @@ function ARTY:onafterWinchester(Controllable, From, Event, To) local text=string.format("%s, winchester.", Controllable:GetName()) self:T(ARTY.id..text) MESSAGE:New(text, 30):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) - - -- Init rearming if possible. - --self:Rearm() end @@ -1823,7 +1819,10 @@ end -- @return #string name, prio, radius, nshells, engaged, maxengage, time, weapontype function ARTY:_TargetInfo(target) local clock=tostring(self:_SecondsToClock(target.time)) - return string.format("%s, prio=%d, radius=%d, nshells=%d, engaged=%d maxengage=%d, weapontype=%d, time=%s", target.name, target.prio, target.radius, target.nshells, target.engaged, target.maxengage, target.weapontype, clock) + local weapon=self:_WeaponTypeName(target.weapontype) + local _underfire=tostring(target.underfire) + return string.format("%s, prio=%d, radius=%d, nshells=%d, engaged=%d/%d, weapontype=%s, time=%s, underfire=%s", + target.name, target.prio, target.radius, target.nshells, target.engaged, target.maxengage, weapon, clock,_underfire) end --- Convert time in seconds to hours, minutes and seconds. @@ -1932,10 +1931,7 @@ function ARTY:_Move(group, ToCoord, Speed, OnRoad) -- Route group on road if requested. if OnRoad then - - --path[#path+1]=cpini:GetClosestPointToRoad():WaypointGround(Speed, "On road") - --task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, false) - + local _first=cpini:GetClosestPointToRoad() local _last=ToCoord:GetClosestPointToRoad() local _onroad=_first:GetPathOnRoad(_last) @@ -1946,8 +1942,6 @@ function ARTY:_Move(group, ToCoord, Speed, OnRoad) task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, false) end - --path[#path+1]=ToCoord:GetClosestPointToRoad():WaypointGround(Speed, "On road") - --task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, false) end -- Last waypoint at ToCoord. @@ -1981,16 +1975,18 @@ end function ARTY._PassingWaypoint(group, arty, i, final) -- Debug message. - local text=string.format("Group %s passing waypoint %d (final=%s)", group:GetName(), i, tostring(final)) - - --local pos=group:GetCoordinate() - --local MarkerID=pos:MarkToAll(string.format("Reached Waypoint %d of group %s", i, group:GetName())) - --pos:SmokeRed() - - MESSAGE:New(text,10):ToAll() + local text=string.format("%s, passing waypoint %d.", group:GetName(), i) + if final then + text=string.format("%s, arrived at destination.", group:GetName()) + end env.info(ARTY.id..text) + if final then + MESSAGE:New(text, 10):ToCoalitionIf(group:GetCoalition(), arty.Debug or arty.report) + else + MESSAGE:New(text, 10):ToAllIf(arty.Debug) + end - -- Move --> Moving --> Arrived --> CombatReady. + -- Arrived event. if final and arty.Controllable:GetName()==group:GetName() then arty:Arrived() end From ba944444daa44b0bdffa1466be97704f597d1812 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sat, 5 May 2018 23:59:02 +0200 Subject: [PATCH 078/420] ARTY v0.8.3 Various improvements. Still WIP. --- .../Moose/Functional/Artillery.lua | 211 +++++++++--------- 1 file changed, 105 insertions(+), 106 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 94c24cff8..6a8b5d1d5 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -269,7 +269,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #number version -ARTY.version="0.8.2" +ARTY.version="0.8.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -287,7 +287,7 @@ ARTY.version="0.8.2" -- DONE: Improve assigned time for engagement. Next day? -- DONE: Improve documentation. -- DONE: Add pseudo user transitions. OnAfter... --- TODO: Make reaming unit a group. +-- DONE: Make reaming unit a group. -- TODO: Adjust documenation again. -- TODO: Add command move to make arty group move. -- TODO: remove schedulers for status event. @@ -384,12 +384,15 @@ function ARTY:New(group) self:AddTransition("*", "CombatReady", "CombatReady") self:AddTransition("*", "Status", "*") self:AddTransition("*", "Dead", "*") + + -- Unknown transitons. To be checked if adding these causes problems. + self:AddTransition("Rearming", "Arrived", "Rearming") return self end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- User Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Assign target coordinates to the ARTY group. Only the first parameter, i.e. the coordinate of the target is mandatory. The remaining parameters are optional and can be used to fine tune the engagement. @@ -563,19 +566,8 @@ function ARTY:SetMissileTypes(tableofnames) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- FSM Start Event ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Before "Start" event. Initialized ROE and alarm state. Starts the event handler. --- @param #ARTY self --- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function ARTY:onbeforeStart(Controllable, From, Event, To) - self:_EventFromTo("onbeforeStart", Event, From, To) - - env.info("FF: onbeforeStart") -end --- After "Start" event. Initialized ROE and alarm state. Starts the event handler. -- @param #ARTY self @@ -643,12 +635,8 @@ function ARTY:onafterStart(Controllable, From, Event, To) self:__Status(5) -- Start scheduler to monitor if ARTY group started firing within a certain time. + --TODO: move this to status checks self.SchedIDCheckShooting=self.scheduler:Schedule(self, ARTY._CheckShootingStarted, {self}, 60, 60) - - -- Start cheduler for status reports. --- if self.Debug then --- self.SchedIDStatusReport=self.scheduler:Schedule(self, ARTY._StatusReport, {self}, 30, 30) --- end end @@ -684,7 +672,7 @@ function ARTY:_StatusReport() end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Event Handling ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Eventhandler for shot event. @@ -803,7 +791,7 @@ function ARTY:_OnEventDead(EventData) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- FSM Events and States ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- After "Status" event. Report status of group. @@ -815,29 +803,36 @@ end function ARTY:onafterStatus(Controllable, From, Event, To) self:_EventFromTo("onafterStatus", Event, From, To) + -- Debug current status info. if self.Debug then self:_StatusReport() end + -- Group is out of ammo. if self:is("OutOfAmmo") then + env.info(string.format("FF: OutOfAmmo. ==> Rearm")) self:Rearm() end + -- Group is out of moving. if self:is("Moving") then - --self.Controllable:GetVelocityKMH() + local _speed=self.Controllable:GetVelocityKMH() + env.info(string.format("FF: Moving. Velocity = %d km/h", _speed)) end + -- Group is rearming. if self:is("Rearming") then local _rearmed=self:_CheckRearmed() - env.info("FF: Rearming. _rearmed = ", tostring(_rearmed)) + env.info(string.format("FF: Rearming. _rearmed = %s", tostring(_rearmed))) if _rearmed then self:Rearmed() end end + -- Group finished rearming. if self:is("Rearmed") then local distance=self.Controllable:GetCoordinate():Get2DDistance(self.InitialCoord) - env.info("FF: Rearmed. Distance ARTY to InitalCoord = ", distance) + env.info(string.format("FF: Rearmed. Distance ARTY to InitalCoord = %d", distance)) if distance > 100 then --self:Move(self.InitialCoord, false) else @@ -845,25 +840,31 @@ function ARTY:onafterStatus(Controllable, From, Event, To) end end - + -- Group arrived at destination. if self:is("Arrived") then + env.info(string.format("FF: Arrived. ==> CombatReady")) self:CombatReady() end - -- Engage targets. + -- Group is combat ready. if self:is("CombatReady") then + env.info(string.format("FF: Combatready. Looking for targets.")) - -- Get a timed target if it is due to be attacked. + -- Get a valid timed target if it is due to be attacked. local _timedTarget=self:_CheckTimedTargets() + -- Get a valid normal target (one that is not timed). local _normalTarget=self:_CheckNormalTargets() -- Engage target. if _timedTarget then + -- Cease fire on current target first. if self.currentTarget then self:CeaseFire(self.currentTarget) end + -- Open fire on timed target. self:OpenFire(_timedTarget) elseif _normalTarget then + -- Open fire on normal target. self:OpenFire(_normalTarget) end @@ -873,66 +874,6 @@ function ARTY:onafterStatus(Controllable, From, Event, To) self:__Status(5) end ---- Check all timed targets and return the target which should be attacked next. --- @param #ARTY self --- @return #table Target which is due to be attacked now. -function ARTY:_CheckTimedTargets() - - -- Current time. - local Tnow=timer.getAbsTime() - - -- Sort Targets wrt time. - self:_SortTargetQueueTime() - - for i=1,#self.targets do - local _target=self.targets[i] - - -- Check if target has an attack time which has already passed. Also check that target is not under fire already and that it is in range. - if _target.time and Tnow>=_target.time and _target.underfire==false and self:_TargetInRange(_target) then - - -- Check if group currently has a target and whether its priorty is lower than the timed target. - if self.currentTarget then - if self.currentTarget.prio > _target.prio then - -- Current target under attack but has lower priority than this target. - self:T(ARTY.id..string.format("Found TIMED HIGH PRIO target %s.", self:_TargetInRange(_target))) - return _target - end - else - -- No current target. - self:T(ARTY.id..string.format("Found TIMED target %s.", self:_TargetInfo(_target))) - return _target - end - end - end - - return nil -end - ---- Check all normal (untimed) targets and return the target with the highest priority which has been engaged the fewest times. --- @param #ARTY self --- @return #table Target which is due to be attacked now or nil if no target could be found. -function ARTY:_CheckNormalTargets() - - -- Sort targets w.r.t. prio and number times engaged already. - self:_SortTargetQueuePrio() - - -- Loop over all sorted targets. - for i=1,#self.targets do - local _target=self.targets[i] - - -- Check that target no time, is not under fire currently and in range. - if _target.underfire==false and _target.time==nil and _target.maxengage > _target.engaged and self:_TargetInRange(_target) then - - -- Debug info. - self:T(ARTY.id..string.format("Found NORMAL target %s", self:_TargetInfo(_target))) - - return _target - end - end - - return nil -end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Enter "CombatReady" state. Route the group back if necessary. @@ -943,10 +884,8 @@ end -- @param #string To To state. function ARTY:onenterCombatReady(Controllable, From, Event, To) self:_EventFromTo("onenterCombatReady", Event, From, To) - -- Debug info - env.info(string.format("FF: onenterComabReady, from=%s, event=%s, to=%s", From, Event, To)) - + self:T(string.format("FF: onenterComabReady, from=%s, event=%s, to=%s", From, Event, To)) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1189,7 +1128,6 @@ function ARTY:onafterRearm(Controllable, From, Event, To) end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- After "Rearmed" event. Send message if reporting is on and stop the scheduler. @@ -1206,7 +1144,7 @@ function ARTY:onafterRearmed(Controllable, From, Event, To) self:T(ARTY.id..text) MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) - -- Route ARTY group backto where it came from (if distance is > 100 m). + -- Route ARTY group back to where it came from (if distance is > 100 m). local d1=self.Controllable:GetCoordinate():Get2DDistance(self.InitialCoord) if d1>100 then self:Move(self.InitialCoord, false) @@ -1420,7 +1358,7 @@ end -- @param #number nshells Number of shells to fire. -- @param #number weapontype Type of weapon to use. function ARTY:_FireAtCoord(coord, radius, nshells, weapontype) - self:E({coord=coord, radius=radius, nshells=nshells}) + self:F({coord=coord, radius=radius, nshells=nshells}) -- Controllable. local group=self.Controllable --Wrapper.Controllable#CONTROLLABLE @@ -1436,7 +1374,6 @@ function ARTY:_FireAtCoord(coord, radius, nshells, weapontype) -- Execute task. group:SetTask(fire) - --group:PushTask(fire) end @@ -1491,6 +1428,66 @@ function ARTY:_SortTargetQueueTime() end +--- Check all timed targets and return the target which should be attacked next. +-- @param #ARTY self +-- @return #table Target which is due to be attacked now. +function ARTY:_CheckTimedTargets() + + -- Current time. + local Tnow=timer.getAbsTime() + + -- Sort Targets wrt time. + self:_SortTargetQueueTime() + + for i=1,#self.targets do + local _target=self.targets[i] + + -- Check if target has an attack time which has already passed. Also check that target is not under fire already and that it is in range. + if _target.time and Tnow>=_target.time and _target.underfire==false and self:_TargetInRange(_target) then + + -- Check if group currently has a target and whether its priorty is lower than the timed target. + if self.currentTarget then + if self.currentTarget.prio > _target.prio then + -- Current target under attack but has lower priority than this target. + self:T(ARTY.id..string.format("Found TIMED HIGH PRIO target %s.", self:_TargetInRange(_target))) + return _target + end + else + -- No current target. + self:T(ARTY.id..string.format("Found TIMED target %s.", self:_TargetInfo(_target))) + return _target + end + end + end + + return nil +end + +--- Check all normal (untimed) targets and return the target with the highest priority which has been engaged the fewest times. +-- @param #ARTY self +-- @return #table Target which is due to be attacked now or nil if no target could be found. +function ARTY:_CheckNormalTargets() + + -- Sort targets w.r.t. prio and number times engaged already. + self:_SortTargetQueuePrio() + + -- Loop over all sorted targets. + for i=1,#self.targets do + local _target=self.targets[i] + + -- Check that target no time, is not under fire currently and in range. + if _target.underfire==false and _target.time==nil and _target.maxengage > _target.engaged and self:_TargetInRange(_target) then + + -- Debug info. + self:T(ARTY.id..string.format("Found NORMAL target %s", self:_TargetInfo(_target))) + + return _target + end + end + + return nil +end + --- Get the number of shells a unit or group currently has. For a group the ammo count of all units is summed up. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE controllable Controllable for which the ammo is counted. @@ -1561,8 +1558,7 @@ function ARTY:_GetAmmo(controllable) _gotmissile=true end end - - + -- We are specifically looking for shells or rockets here. if _gotshell then @@ -1755,17 +1751,17 @@ function ARTY:_WeaponTypeName(tnumber) self:F2(tnumber) local name="unknown" if tnumber==ARTY.WeaponType.Auto then - name="Auto (Cannon, Rockets, Missiles)" + name="Auto" -- (Cannon, Rockets, Missiles) elseif tnumber==ARTY.WeaponType.Cannon then - name="Cannon" + name="Cannons" elseif tnumber==ARTY.WeaponType.Rockets then name="Rockets" elseif tnumber==ARTY.WeaponType.UnguidedAny then - name="Any Unguided Weapon (Cannon or Rockets)" + name="Unguided Weapons" -- (Cannon or Rockets) elseif tnumber==ARTY.WeaponType.CruiseMissile then - name="Cruise Missile" + name="Cruise Missiles" elseif tnumber==ARTY.WeaponType.GuidedMissile then - name="Guided Missile" + name="Guided Missiles" end return name end @@ -1774,15 +1770,18 @@ end -- @param #ARTY self -- @param Core.Point#COORDINATE coord Center coordinate. -- @param #number rmin (Optional) Minimum distance in meters from center coordinate. Default 20 m. --- @param #number rmax (Optional) Maximum distance in meters from center coordinate. Default 100 m. +-- @param #number rmax (Optional) Maximum distance in meters from center coordinate. Default 80 m. -- @return Core.Point#COORDINATE Random coordinate in a certain distance from center coordinate. function ARTY:_VicinityCoord(coord, rmin, rmax) self:F2({coord=coord, rmin=rmin, rmax=rmax}) + -- Set default if necessary. rmin=rmin or 20 - rmax=rmax or 100 - -- Random point. - local vec2=coord:GetRandomVec2InRadius(rmin, rmax) + rmax=rmax or 80 + -- Random point withing range. + local vec2=coord:GetRandomVec2InRadius(rmax, rmin) local pops=COORDINATE:NewFromVec2(vec2) + -- Debug info. + self:T(ARTY.id..string.format("Vicinity distance = %d (rmin=%d, rmax=%d)", pops:Get2DDistance(coord), rmin, rmax)) return pops end From 19b3dcec2194345a92d40fafbcc04f0897e8d542 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sun, 6 May 2018 07:36:58 +0200 Subject: [PATCH 079/420] # Conflicts: # Moose Development/Moose/AI/AI_Cargo_APC.lua # Moose Development/Moose/Wrapper/Controllable.lua --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 29 ++++--- .../Moose/AI/AI_Cargo_Helicopter.lua | 83 ++++++++++++++++--- .../Moose/Wrapper/Controllable.lua | 5 +- 3 files changed, 90 insertions(+), 27 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index d5022f135..be3b2b77e 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -395,7 +395,7 @@ function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To ) local APCUnit = APCUnit -- Wrapper.Unit#UNIT for _, Cargo in pairs( self.CargoSet:GetSet() ) do local Cargo = Cargo -- Cargo.Cargo#CARGO - self:F( { IsUnLoaded = Cargo:IsUnLoaded() } ) + self:F( { IsUnLoaded = Cargo:IsUnLoaded(), Cargo:GetName(), APC:GetName() } ) if Cargo:IsUnLoaded() then if Cargo:IsInLoadRadius( APCUnit:GetCoordinate() ) then self:F( { "In radius", APCUnit:GetName() } ) @@ -420,12 +420,12 @@ function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To ) end --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP Carrier -function AI_CARGO_APC:onafterBoard( Carrier, From, Event, To, Cargo ) - self:F( { Carrier, From, Event, To, Cargo } ) +-- @param Wrapper.Group#GROUP APC +function AI_CARGO_APC:onafterBoard( APC, From, Event, To, Cargo ) + self:F( { APC, From, Event, To, Cargo } ) - if Carrier and Carrier:IsAlive() then - self:F({ IsLoaded = Cargo:IsLoaded() } ) + if APC and APC:IsAlive() then + self:F({ IsLoaded = Cargo:IsLoaded(), Cargo:GetName(), APC:GetName() } ) if not Cargo:IsLoaded() then self:__Board( 10, Cargo ) else @@ -445,7 +445,7 @@ function AI_CARGO_APC:onbeforeLoaded( APC, From, Event, To ) if APC and APC:IsAlive() then for APCUnit, Cargo in pairs( self.APC_Cargo ) do local Cargo = Cargo -- Cargo.Cargo#CARGO - self:F( { IsLoaded = Cargo:IsLoaded(), IsDestroyed = Cargo:IsDestroyed() } ) + self:F( { IsLoaded = Cargo:IsLoaded(), IsDestroyed = Cargo:IsDestroyed(), Cargo:GetName(), APC:GetName() } ) if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then Loaded = false end @@ -505,8 +505,9 @@ function AI_CARGO_APC:onbeforeUnloaded( APC, From, Event, To, Cargo ) --Cargo:Regroup() if APC and APC:IsAlive() then - for _, CargoCheck in pairs( self.CargoSet:GetSet() ) do - local CargoCheck = CargoCheck -- Cargo.Cargo#CARGO + for _, APCUnit in pairs( APC:GetUnits() ) do + local APCUnit = APCUnit -- Wrapper.Unit#UNIT + local CargoCheck = self.APC_Cargo[APCUnit] self:F( { CargoCheck:GetName(), IsUnLoaded = CargoCheck:IsUnLoaded() } ) if CargoCheck:IsUnLoaded() == false then AllUnloaded = false @@ -580,14 +581,15 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate -- @param #number Speed -function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed ) +-- @param #string EndPointFormation The formation at the end point of the action. +function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed, EndPointFormation ) if APC and APC:IsAlive() then if Coordinate then self.RoutePickup = true - local Waypoints = APC:TaskGroundOnRoad( Coordinate, Speed ) + local Waypoints = APC:TaskGroundOnRoad( Coordinate, Speed, EndPointFormation ) local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Pickup", self ) @@ -611,13 +613,14 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate -- @param #number Speed -function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed ) +-- @param #string EndPointFormation The formation at the end point of the action. +function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed, EndPointFormation ) if APC and APC:IsAlive() then self.RouteDeploy = true - local Waypoints = APC:TaskGroundOnRoad( Coordinate, Speed ) + local Waypoints = APC:TaskGroundOnRoad( Coordinate, Speed, EndPointFormation ) local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Deploy", self ) diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index ad502c322..c993755ee 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -333,19 +333,41 @@ end --- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -function AI_CARGO_HELICOPTER:onafterLoad( Helicopter, From, Event, To, Coordinate ) +function AI_CARGO_HELICOPTER:onbeforeLoad( Helicopter, From, Event, To, Coordinate ) + + local Boarding = false if Helicopter and Helicopter:IsAlive() then - for _, Cargo in pairs( self.CargoSet:GetSet() ) do - if Cargo:IsInLoadRadius( Coordinate ) then - self:__Board( 5 ) - Cargo:Board( Helicopter:GetUnit(1), 25 ) - self.Cargo = Cargo - break + self.BoardingCount = 0 + + if Helicopter and Helicopter:IsAlive() then + self.Helicopter_Cargo = {} + for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do + local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT + for _, Cargo in pairs( self.CargoSet:GetSet() ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + self:F( { IsUnLoaded = Cargo:IsUnLoaded() } ) + if Cargo:IsUnLoaded() then + if Cargo:IsInLoadRadius( HelicopterUnit:GetCoordinate() ) then + self:F( { "In radius", HelicopterUnit:GetName() } ) + --Cargo:Ungroup() + Cargo:Board( HelicopterUnit, 25 ) + self:__Board( 1, Cargo ) + Boarding = true + + -- So now this APCUnit has Cargo that is being loaded. + -- This will be used further in the logic to follow and to check cargo status. + self.Helicopter_Cargo[HelicopterUnit] = Cargo + break + end + end + end end end end + + return Boarding end @@ -366,11 +388,23 @@ end --- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -function AI_CARGO_HELICOPTER:onafterLoaded( Helicopter, From, Event, To ) +function AI_CARGO_HELICOPTER:onbeforeLoaded( Helicopter, From, Event, To ) + + local Loaded = true if Helicopter and Helicopter:IsAlive() then + for HelicopterUnit, Cargo in pairs( self.APC_Cargo ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + self:F( { IsLoaded = Cargo:IsLoaded(), IsDestroyed = Cargo:IsDestroyed() } ) + if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then + Loaded = false + end + end + end + return Loaded + end @@ -379,9 +413,15 @@ end function AI_CARGO_HELICOPTER:onafterUnload( Helicopter, From, Event, To ) if Helicopter and Helicopter:IsAlive() then - self.Cargo:UnBoard() - self:__Unboard( 10 ) + for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do + local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT + for _, Cargo in pairs( HelicopterUnit:GetCargo() ) do + Cargo:UnBoard() + self:__Unboard( 10, Cargo ) + end + end end + end @@ -401,12 +441,31 @@ end --- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -function AI_CARGO_HELICOPTER:onafterUnloaded( Helicopter, From, Event, To ) +function AI_CARGO_HELICOPTER:onbeforeUnloaded( Helicopter, From, Event, To ) + + local AllUnloaded = true + + --Cargo:Regroup() if Helicopter and Helicopter:IsAlive() then - self.Helicopter = Helicopter + for _, CargoCheck in pairs( self.CargoSet:GetSet() ) do + local CargoCheck = CargoCheck -- Cargo.Cargo#CARGO + self:F( { CargoCheck:GetName(), IsUnLoaded = CargoCheck:IsUnLoaded() } ) + if CargoCheck:IsUnLoaded() == false then + AllUnloaded = false + break + end + end + + if AllUnloaded == true then + self.Helicopter = Helicopter + self.Helicopter_Cargo = {} + end end + self:F( { AllUnloaded = AllUnloaded } ) + return AllUnloaded + end diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 6f55ff3d6..11809e47a 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1974,8 +1974,9 @@ do -- Route methods -- @param #CONTROLLABLE self -- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to. -- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h. + -- @param #string EndPointFormation The formation to achieve at the end point. -- @return Task - function CONTROLLABLE:TaskGroundOnRoad( ToCoordinate, Speed ) + function CONTROLLABLE:TaskGroundOnRoad( ToCoordinate, Speed, EndPointFormation ) -- Current coordinate. local FromCoordinate = self:GetCoordinate() @@ -1998,7 +1999,7 @@ do -- Route methods -- Add the final coordinate because the final coordinate in path is last point on road. local dist=ToCoordinate:Get2DDistance(path[#path]) if dist>10 then - table.insert( Route, ToCoordinate:WaypointGround( Speed, "Vee" ) ) + table.insert( Route, ToCoordinate:WaypointGround( Speed, EndPointFormation ) ) end return Route From c36579f88a1e96a33a2c65110d9c8828755171eb Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sun, 6 May 2018 12:05:23 +0200 Subject: [PATCH 080/420] ARTY v0.8.4 Several improvments and fixes. --- .../Moose/Functional/Artillery.lua | 106 +++++++++--------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 6a8b5d1d5..410b083c1 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -269,7 +269,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #number version -ARTY.version="0.8.3" +ARTY.version="0.8.4" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -407,10 +407,10 @@ end -- @param #string name (Optional) Name of the target. Default is LL DMS coordinate of the target. If the name was already given, the numbering "#01", "#02",... is appended automatically. -- @return #string Name of the target. Can be used for further reference, e.g. deleting the target from the list. -- @usage paladin=ARTY:New(GROUP:FindByName("Blue Paladin")) --- paladin:AssignTargetCoord(GROUP:FindByName("Red Targets 1"):GetCoordinate(), 10, 300, 10, 1, "08:02:00", ARTY.WeaponType.Auto, "Red Targets 1") +-- paladin:AssignTargetCoord(GROUP:FindByName("Red Targets 1"):GetCoordinate(), 10, 300, 10, 1, "08:02:00", ARTY.WeaponType.Auto, "Target 1") -- paladin:Start() function ARTY:AssignTargetCoord(coord, prio, radius, nshells, maxengage, time, weapontype, name) - self:T({coord=coord, prio=prio, radius=radius, nshells=nshells, maxengage=maxengage, time=time, weapontype=weapontype, name=name}) + self:F({coord=coord, prio=prio, radius=radius, nshells=nshells, maxengage=maxengage, time=time, weapontype=weapontype, name=name}) -- Set default values. nshells=nshells or 5 @@ -436,12 +436,6 @@ function ARTY:AssignTargetCoord(coord, prio, radius, nshells, maxengage, time, w -- Add to table. table.insert(self.targets, _target) - -- Clock. - local _clock=self:_SecondsToClock(_target.time) - - -- Debug info. - self:T(ARTY.id..string.format("Added target %s", self:_TargetInfo(_target))) - -- Trigger new target event. self:NewTarget(_target) end @@ -632,12 +626,8 @@ function ARTY:onafterStart(Controllable, From, Event, To) self:HandleEvent(EVENTS.Shot, self._OnEventShot) self:HandleEvent(EVENTS.Dead, self._OnEventDead) + -- Start checking status. self:__Status(5) - - -- Start scheduler to monitor if ARTY group started firing within a certain time. - --TODO: move this to status checks - self.SchedIDCheckShooting=self.scheduler:Schedule(self, ARTY._CheckShootingStarted, {self}, 60, 60) - end --- After "Start" event. Initialized ROE and alarm state. Starts the event handler. @@ -648,8 +638,8 @@ function ARTY:_StatusReport() local Nammo, Nshells, Nrockets, Nmissiles=self:_GetAmmo(self.Controllable) local Tnow=timer.getTime() - local text=string.format("\n******************************************************\n") - text=text..string.format("Status of ARTY = %s\n", self.Controllable:GetName()) + local text=string.format("\n******************* STATUS ***************************\n") + text=text..string.format("ARTY group = %s\n", self.Controllable:GetName()) text=text..string.format("FSM state = %s\n", self:GetState()) text=text..string.format("Total ammo count = %d\n", Nammo) text=text..string.format("Number of shells = %d\n", Nshells) @@ -846,28 +836,41 @@ function ARTY:onafterStatus(Controllable, From, Event, To) self:CombatReady() end - -- Group is combat ready. - if self:is("CombatReady") then - env.info(string.format("FF: Combatready. Looking for targets.")) - - -- Get a valid timed target if it is due to be attacked. - local _timedTarget=self:_CheckTimedTargets() - -- Get a valid normal target (one that is not timed). - local _normalTarget=self:_CheckNormalTargets() + -- Group is firing on target. + if self:is("Firing") then + -- Check that firing started after ~5 min. If not, target is removed. + self:_CheckShootingStarted() + end + + + -- Get a valid timed target if it is due to be attacked. + local _timedTarget=self:_CheckTimedTargets() + + -- Get a valid normal target (one that is not timed). + local _normalTarget=self:_CheckNormalTargets() + + + -- Group is combat ready or firing but we have a high prio timed target. + if self:is("CombatReady") or (self:is("Firing") and _timedTarget) then + env.info(string.format("FF: Combatready or firing and high prio timed target.")) -- Engage target. if _timedTarget then + -- Cease fire on current target first. if self.currentTarget then self:CeaseFire(self.currentTarget) end + -- Open fire on timed target. self:OpenFire(_timedTarget) + elseif _normalTarget then + -- Open fire on normal target. self:OpenFire(_normalTarget) + end - end -- Call status again in 5 sec. @@ -1023,7 +1026,7 @@ function ARTY:onafterWinchester(Controllable, From, Event, To) self:_EventFromTo("onafterWinchester", Event, From, To) -- Send message. - local text=string.format("%s, winchester.", Controllable:GetName()) + local text=string.format("%s, winchester!", Controllable:GetName()) self:T(ARTY.id..text) MESSAGE:New(text, 30):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) @@ -1130,7 +1133,7 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- After "Rearmed" event. Send message if reporting is on and stop the scheduler. +--- After "Rearmed" event. Send ARTY and rearming group back to their inital positions. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. -- @param #string From From state. @@ -1189,7 +1192,7 @@ function ARTY:_CheckRearmed() MESSAGE:New(text, 10):ToCoalitionIf(self.Controllable:GetCoalition(), self.report or self.Debug) end - -- Rearming --> Rearmed --> CombatReady + -- Return if ammo is full. if nammo==self.FullAmmo then return true else @@ -1314,7 +1317,7 @@ function ARTY:onafterDead(Controllable, From, Event, To) end ---- After "Stop" event. Stop schedulers and unhandle events. +--- After "Stop" event. Unhandle events and cease fire on current target. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. -- @param #string From From state. @@ -1334,14 +1337,6 @@ function ARTY:onafterStop(Controllable, From, Event, To) -- Remove all targets. --self:RemoveAllTargets() - -- Stop schedulers. - if self.SchedIDCheckShooting then - self.scheduler:Stop(self.SchedIDCheckShooting) - end - if self.SchedIDCheckRearmed then - --self.scheduler:Stop(self.SchedIDCheckRearmed) - end - -- Unhandle event. self:UnHandleEvent(EVENTS.Shot) self:UnHandleEvent(EVENTS.Dead) @@ -1361,7 +1356,7 @@ function ARTY:_FireAtCoord(coord, radius, nshells, weapontype) self:F({coord=coord, radius=radius, nshells=nshells}) -- Controllable. - local group=self.Controllable --Wrapper.Controllable#CONTROLLABLE + local group=self.Controllable --Wrapper.Group#GROUP -- Set ROE to weapon free. group:OptionROEOpenFire() @@ -1393,14 +1388,14 @@ function ARTY:_SortTargetQueuePrio() self:T2(ARTY.id.."Sorted targets wrt prio and number of engagements:") for i=1,#self.targets do local _target=self.targets[i] - local _clock=self:_SecondsToClock(_target.time) - self:T2(ARTY.id..string.format("Target %s, prio=%d, engaged=%d, time=%s", _target.name, _target.prio, _target.engaged, tostring(_clock))) + self:T2(ARTY.id..string.format("Target %s", self:_TargetInfo(_target))) end end ---- Sort targets with respect to engage time. +--- Sort array with respect to time. Array elements must have a .time entry. -- @param #ARTY self -function ARTY:_SortTargetQueueTime() +-- @param #table queue Array to sort. Should have elemnt .time. +function ARTY:_SortQueueTime(queue) self:F2() -- Sort targets w.r.t attack time. @@ -1416,14 +1411,15 @@ function ARTY:_SortTargetQueueTime() end return a.time < b.time end - table.sort(self.targets, _sort) + table.sort(queue, _sort) -- Debug output. - self:T2(ARTY.id.."Sorted targets wrt time:") - for i=1,#self.targets do - local _target=self.targets[i] - local _clock=self:_SecondsToClock(_target.time) - self:T2(ARTY.id..string.format("Target %s, prio=%d, engaged=%d, time=%s", _target.name, _target.prio, _target.engaged, tostring(_clock))) + self:T(ARTY.id.."Sorted queue wrt time:") + for i=1,#queue do + local _queue=queue[i] + local _time=tostring(_queue.time) + local _clock=tostring(self:_SecondsToClock(_queue.time)) + self:T(ARTY.id..string.format("%s: time=%s, clock=%s", _queue.name, _time, _clock)) end end @@ -1437,11 +1433,14 @@ function ARTY:_CheckTimedTargets() local Tnow=timer.getAbsTime() -- Sort Targets wrt time. - self:_SortTargetQueueTime() + self:_SortQueueTime(self.targets) for i=1,#self.targets do local _target=self.targets[i] + -- Debug info. + self:T3(ARTY.id..string.format("Check TIMED target %d: %s", i, self:_TargetInfo(_target))) + -- Check if target has an attack time which has already passed. Also check that target is not under fire already and that it is in range. if _target.time and Tnow>=_target.time and _target.underfire==false and self:_TargetInRange(_target) then @@ -1449,7 +1448,7 @@ function ARTY:_CheckTimedTargets() if self.currentTarget then if self.currentTarget.prio > _target.prio then -- Current target under attack but has lower priority than this target. - self:T(ARTY.id..string.format("Found TIMED HIGH PRIO target %s.", self:_TargetInRange(_target))) + self:T(ARTY.id..string.format("Found TIMED HIGH PRIO target %s.", self:_TargetInfo(_target))) return _target end else @@ -1474,6 +1473,9 @@ function ARTY:_CheckNormalTargets() -- Loop over all sorted targets. for i=1,#self.targets do local _target=self.targets[i] + + -- Debug info. + self:T3(ARTY.id..string.format("Check NORMAL target %d: %s", i, self:_TargetInfo(_target))) -- Check that target no time, is not under fire currently and in range. if _target.underfire==false and _target.time==nil and _target.maxengage > _target.engaged and self:_TargetInRange(_target) then @@ -1802,14 +1804,12 @@ end -- @param #string sep Speparator for split. -- @return #table Split text. function ARTY:_split(str, sep) - self:F3({str=str, sep=sep}) - + self:F3({str=str, sep=sep}) local result = {} local regex = ("([^%s]+)"):format(sep) for each in str:gmatch(regex) do table.insert(result, each) end - return result end From a2c12dc05eec4304ef00d5fe608487e0b0dd73bc Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sun, 6 May 2018 12:51:51 +0200 Subject: [PATCH 081/420] ARTY v0.8.5 Improved GetAmmo function. Display of ammo table. Removed all schedulers. --- .../Moose/Functional/Artillery.lua | 90 +++++++++---------- 1 file changed, 43 insertions(+), 47 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 410b083c1..19b401e65 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -14,7 +14,9 @@ -- * Multiple targets can be assigned. No restriction on number of targets. -- * Targets can be given a priority. Engagement of targets is executed a according to their priority. -- * Engagements can be scheduled, i.e. will be executed at a certain time of the day. --- * Special weapon types can be selected. +-- * Special weapon types can be selected for each attack, e.g. cruise missiles for Naval units. +-- * Automatic rearming once the artillery is out of ammo. +-- * Finite state machine implementation. User can interact when certain events occur. -- -- ==== -- @@ -49,11 +51,7 @@ -- @field #number Nrockets0 Initial amount of rockets of the whole group. -- @field #number Nmissiles0 Initial amount of missiles of the whole group. -- @field #number FullAmmo Full amount of all ammunition taking the number of alive units into account. --- @field Core.Scheduler#SCHEDULER scheduler Scheduler object handling various timed functions. --- @field #number SchedIDCheckRearmed Scheduler ID responsible for checking whether rearming of the ARTY group is complete. --- @field #number SchedIDCheckShooting Scheduler ID for checking whether a group startet firing within a certain time after the fire at point task was assigned. -- @field #number WaitForShotTime Max time in seconds to wait until fist shot event occurs after target is assigned. If time is passed without shot, the target is deleted. Default is 300 seconds. --- @field #number SchedIDStatusReport Scheduler ID for status report messages. The scheduler is only launched in debug mode. -- @field #table DCSdesc DCS descriptors of the ARTY group. -- @field #string Type Type of the ARTY group. -- @field #string DisplayName Extended type name of the ARTY group. @@ -229,9 +227,6 @@ ARTY={ Nrockets0=0, Nmissiles0=0, FullAmmo=0, - scheduler=nil, - SchedIDCheckRearmed=nil, - SchedIDCheckShooting=nil, WaitForShotTime=300, SchedIDStatusReport=nil, DCSdesc=nil, @@ -269,7 +264,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #number version -ARTY.version="0.8.4" +ARTY.version="0.8.5" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -290,7 +285,7 @@ ARTY.version="0.8.4" -- DONE: Make reaming unit a group. -- TODO: Adjust documenation again. -- TODO: Add command move to make arty group move. --- TODO: remove schedulers for status event. +-- DONE: remove schedulers for status event. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -325,9 +320,6 @@ function ARTY:New(group) -- Set the initial coordinates of the ARTY group. self.InitialCoord=group:GetCoordinate() - -- Create scheduler object. - self.scheduler=SCHEDULER:New(self) - -- Get DCS descriptors of group. local DCSgroup=Group.getByName(group:GetName()) local DCSunit=DCSgroup:getUnit(1) @@ -577,7 +569,7 @@ function ARTY:onafterStart(Controllable, From, Event, To) MESSAGE:New(text, 10):ToAllIf(self.Debug) -- Get Ammo. - self.Nammo0, self.Nshells0, self.Nrockets0, self.Nmissiles0=self:_GetAmmo(self.Controllable) + self.Nammo0, self.Nshells0, self.Nrockets0, self.Nmissiles0=self:GetAmmo(self.Controllable, self.Debug) local text=string.format("\n******************************************************\n") text=text..string.format("Arty group = %s\n", Controllable:GetName()) @@ -635,7 +627,7 @@ end function ARTY:_StatusReport() -- Get Ammo. - local Nammo, Nshells, Nrockets, Nmissiles=self:_GetAmmo(self.Controllable) + local Nammo, Nshells, Nrockets, Nmissiles=self:GetAmmo(self.Controllable) local Tnow=timer.getTime() local text=string.format("\n******************* STATUS ***************************\n") @@ -699,7 +691,7 @@ function ARTY:_OnEventShot(EventData) MESSAGE:New(text, 5):ToAllIf(self.Debug) -- Get current ammo. - local _nammo,_nshells,_nrockets,_nmissiles=self:_GetAmmo(self.Controllable) + local _nammo,_nshells,_nrockets,_nmissiles=self:GetAmmo(self.Controllable) if _nammo==0 then @@ -1170,7 +1162,7 @@ function ARTY:_CheckRearmed() self:F2() -- Get current ammo. - local nammo,nshells,nrockets,nmissiles=self:_GetAmmo(self.Controllable) + local nammo,nshells,nrockets,nmissiles=self:GetAmmo(self.Controllable) -- Number of units still alive. local units=self.Controllable:GetUnits() @@ -1385,10 +1377,10 @@ function ARTY:_SortTargetQueuePrio() table.sort(self.targets, _sort) -- Debug output. - self:T2(ARTY.id.."Sorted targets wrt prio and number of engagements:") + self:T3(ARTY.id.."Sorted targets wrt prio and number of engagements:") for i=1,#self.targets do local _target=self.targets[i] - self:T2(ARTY.id..string.format("Target %s", self:_TargetInfo(_target))) + self:T3(ARTY.id..string.format("Target %s", self:_TargetInfo(_target))) end end @@ -1396,7 +1388,7 @@ end -- @param #ARTY self -- @param #table queue Array to sort. Should have elemnt .time. function ARTY:_SortQueueTime(queue) - self:F2() + self:F3({queue=queue}) -- Sort targets w.r.t attack time. local function _sort(a, b) @@ -1414,12 +1406,12 @@ function ARTY:_SortQueueTime(queue) table.sort(queue, _sort) -- Debug output. - self:T(ARTY.id.."Sorted queue wrt time:") + self:T3(ARTY.id.."Sorted queue wrt time:") for i=1,#queue do local _queue=queue[i] local _time=tostring(_queue.time) local _clock=tostring(self:_SecondsToClock(_queue.time)) - self:T(ARTY.id..string.format("%s: time=%s, clock=%s", _queue.name, _time, _clock)) + self:T3(ARTY.id..string.format("%s: time=%s, clock=%s", _queue.name, _time, _clock)) end end @@ -1428,7 +1420,8 @@ end -- @param #ARTY self -- @return #table Target which is due to be attacked now. function ARTY:_CheckTimedTargets() - + self:F3() + -- Current time. local Tnow=timer.getAbsTime() @@ -1448,12 +1441,12 @@ function ARTY:_CheckTimedTargets() if self.currentTarget then if self.currentTarget.prio > _target.prio then -- Current target under attack but has lower priority than this target. - self:T(ARTY.id..string.format("Found TIMED HIGH PRIO target %s.", self:_TargetInfo(_target))) + self:T2(ARTY.id..string.format("Found TIMED HIGH PRIO target %s.", self:_TargetInfo(_target))) return _target end else -- No current target. - self:T(ARTY.id..string.format("Found TIMED target %s.", self:_TargetInfo(_target))) + self:T2(ARTY.id..string.format("Found TIMED target %s.", self:_TargetInfo(_target))) return _target end end @@ -1481,7 +1474,7 @@ function ARTY:_CheckNormalTargets() if _target.underfire==false and _target.time==nil and _target.maxengage > _target.engaged and self:_TargetInRange(_target) then -- Debug info. - self:T(ARTY.id..string.format("Found NORMAL target %s", self:_TargetInfo(_target))) + self:T2(ARTY.id..string.format("Found NORMAL target %s", self:_TargetInfo(_target))) return _target end @@ -1493,12 +1486,17 @@ end --- Get the number of shells a unit or group currently has. For a group the ammo count of all units is summed up. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE controllable Controllable for which the ammo is counted. +-- @param #boolean display Display ammo table as message to all. Default false. -- @return #number Total amount of ammo the whole group has left. -- @return #number Number of shells the group has left. -- @return #number Number of rockets the group has left. -- @return #number Number of missiles the group has left. -function ARTY:_GetAmmo(controllable) - self:F2(controllable) +function ARTY:GetAmmo(controllable, display) + self:F3({controllable=controllable, display=display}) + + if display==nil then + display=false + end -- Init counter. local nammo=0 @@ -1515,18 +1513,19 @@ function ARTY:_GetAmmo(controllable) for _,unit in pairs(units) do if unit and unit:IsAlive() then + + -- Output. + local text=string.format("ARTY group %s - unit %s:\n", self.Controllable:GetName(), unit:GetName()) + -- Get ammo table. local ammotable=unit:GetAmmo() - self:T2({ammotable=ammotable}) - - local name=unit:GetName() - + if ammotable ~= nil then local weapons=#ammotable self:T2(ARTY.id..string.format("Number of weapons %d.", weapons)) - self:T2(ammotable) + self:T2({ammotable=ammotable}) -- Loop over all weapons. for w=1,weapons do @@ -1568,9 +1567,7 @@ function ARTY:_GetAmmo(controllable) nshells=nshells+Nammo -- Debug info. - local text=string.format("Unit %s has %d shells of type %s", name, Nammo, Tammo) - self:T2(ARTY.id..text) - MESSAGE:New(text, 10):ToAllIf(self.Debug and not self.report) + text=text..string.format("- %d shells of type %s\n", Nammo, Tammo) elseif _gotrocket then @@ -1578,9 +1575,7 @@ function ARTY:_GetAmmo(controllable) nrockets=nrockets+Nammo -- Debug info. - local text=string.format("Unit %s has %d rockets of type %s", name, Nammo, Tammo) - self:T2(ARTY.id..text) - MESSAGE:New(text, 10):ToAllIf(self.Debug and not self.report) + text=text..string.format("- %d rockets of type %s\n", Nammo, Tammo) elseif _gotmissile then @@ -1588,21 +1583,22 @@ function ARTY:_GetAmmo(controllable) nmissiles=nmissiles+Nammo -- Debug info. - local text=string.format("Unit %s has %d missiles of type %s", name, Nammo, Tammo) - self:T2(ARTY.id..text) - MESSAGE:New(text, 10):ToAllIf(self.Debug and not self.report) - + text=text..string.format("- %d missiles of type %s\n", name, Nammo, Tammo) + else -- Debug info. - local text=string.format("Unit %s has %d ammo of type %s", name, Nammo, Tammo) - self:T2(ARTY.id..text) - MESSAGE:New(text, 10):ToAllIf(self.Debug and not self.report) + text=text..string.format("- %d unknown ammo of type %s\n", Nammo, Tammo) end end end + + self:T2(ARTY.id..text) + MESSAGE:New(text, 10):ToAllIf(display) + + end end @@ -1768,7 +1764,7 @@ function ARTY:_WeaponTypeName(tnumber) return name end ---- After "Rearmed" event. Send message if reporting is on and stop the scheduler. +--- Find a random coordinate in the vicinity of another coordinate. -- @param #ARTY self -- @param Core.Point#COORDINATE coord Center coordinate. -- @param #number rmin (Optional) Minimum distance in meters from center coordinate. Default 20 m. From 16d4a65569724ffec23fdc5b38ed47aee0b390d6 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sun, 6 May 2018 15:13:36 +0200 Subject: [PATCH 082/420] ARTY v0.8.6 Added user function for rearming properties. Minor bug fixes. --- .../Moose/Functional/Artillery.lua | 136 +++++++++++++----- 1 file changed, 97 insertions(+), 39 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 19b401e65..1b2d15fdf 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -44,30 +44,36 @@ -- @type ARTY -- @field #string ClassName Name of the class. -- @field #boolean Debug Write Debug messages to DCS log file and send Debug messages to all players. --- @field #table targets Targets assigned. +-- @field #table targets All targets assigned. +-- @field #table moves All moves assigned. -- @field #table currentTarget Holds the current target, if there is one assigned. -- @field #number Nammo0 Initial amount total ammunition (shells+rockets+missiles) of the whole group. -- @field #number Nshells0 Initial amount of shells of the whole group. -- @field #number Nrockets0 Initial amount of rockets of the whole group. -- @field #number Nmissiles0 Initial amount of missiles of the whole group. -- @field #number FullAmmo Full amount of all ammunition taking the number of alive units into account. +-- @field #number StatusInterval Update interval in seconds between status updates. Default 10 seconds. -- @field #number WaitForShotTime Max time in seconds to wait until fist shot event occurs after target is assigned. If time is passed without shot, the target is deleted. Default is 300 seconds. -- @field #table DCSdesc DCS descriptors of the ARTY group. -- @field #string Type Type of the ARTY group. -- @field #string DisplayName Extended type name of the ARTY group. -- @field #number IniGroupStrength Inital number of units in the ARTY group. -- @field #boolean IsArtillery If true, ARTY group has attribute "Artillery". --- @field #number Speed Max speed of ARTY group. +-- @field #number Speed Maximum speed of ARTY group in km/h. +-- @field #number RearmingDistance Safe distance in meters between ARTY group and rearming group or place at which rearming is possible. Default 100 m. -- @field Wrapper.Group#GROUP RearmingGroup Unit designated to rearm the ARTY group. +-- @field #number RearmingGroupSpeed Speed in km/h the rearming unit moves at. Default 50 km/h. +-- @field #boolean RearmingGroupOnRoad If true, rearming group will move to ARTY group or rearming place using mainly roads. Default false. -- @field Core.Point#COORDINATE RearmingGroupCoord Initial coordinates of the rearming unit. After rearming complete, the unit will return to this position. -- @field Core.Point#COORDINATE RearmingPlaceCoord Coordinates of the rearming place. If the place is more than 100 m away from the ARTY group, the group will go there. +-- @field #boolean RearmingArtyOnRoad If true, ARTY group will move to rearming place using mainly roads. Default false. -- @field Core.Point#COORDINATE InitialCoord Initial coordinates of the ARTY group. -- @field #boolean report Arty group sends messages about their current state or target to its coaliton. -- @field #table ammoshells Table holding names of the shell types which are included when counting the ammo. Default is {"weapons.shells"} which include most shells. -- @field #table ammorockets Table holding names of the rocket types which are included when counting the ammo. Default is {"weapons.nurs"} which includes most unguided rockets. -- @field #table ammomissiles Table holding names of the missile types which are included when counting the ammo. Default is {"weapons.missiles"} which includes some guided missiles. -- @field #number Nshots Number of shots fired on current target. --- @field #number minrange Minimum firing range in kilometers. Targets closer than this distance are not engaged. Default 0 km. +-- @field #number minrange Minimum firing range in kilometers. Targets closer than this distance are not engaged. Default 0.5 km. -- @field #number maxrange Maximum firing range in kilometers. Targets further away than this distance are not engaged. Default 10000 km. -- @extends Core.Fsm#FSM_CONTROLLABLE @@ -221,29 +227,34 @@ ARTY={ ClassName = "ARTY", Debug = true, targets = {}, + moves = {}, currentTarget = nil, Nammo0=0, Nshells0=0, Nrockets0=0, Nmissiles0=0, FullAmmo=0, + StatusInterval=10, WaitForShotTime=300, - SchedIDStatusReport=nil, DCSdesc=nil, Type=nil, DisplayName=nil, IniGroupStrength=0, IsArtillery=nil, + RearmingDistance=100, RearmingGroup=nil, + RearmingGroupSpeed=50, + RearmingGroupOnRoad=false, RearmingGroupCoord=nil, RearmingPlaceCoord=nil, + RearmingArtyOnRoad=false, InitialCoord=nil, report=true, ammoshells={"weapons.shells"}, ammorockets={"weapons.nurs"}, ammomissiles={"weapons.missiles"}, Nshots=0, - minrange=0, + minrange=500, maxrange=1000000, } @@ -264,7 +275,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #number version -ARTY.version="0.8.5" +ARTY.version="0.8.6" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -434,10 +445,10 @@ end --- Set minimum firing range. Targets closer than this distance are not engaged. -- @param #ARTY self --- @param #number range Min range in kilometers. Default is 0 km. +-- @param #number range Min range in kilometers. Default is 0.5 km. function ARTY:SetMinFiringRange(range) self:F({range=range}) - self.minrange=range or 0 + self.minrange=range*1000 or 500 end --- Set maximum firing range. Targets further away than this distance are not engaged. @@ -448,6 +459,14 @@ function ARTY:SetMaxFiringRange(range) self.maxrange=range*1000 or 1000*1000 end +--- Set time interval between status updates. During the status check, new events are triggered. +-- @param #ARTY self +-- @param #number interval Time interval in seconds. Default 10 seconds. +function ARTY:SetStatusInterval(interval) + self:F({interval=interval}) + self.StatusInterval=interval or 10 +end + --- Set time how it is waited a unit the first shot event happens. If no shot is fired after this time, the task to fire is aborted and the target removed. -- @param #ARTY self -- @param #number waittime Time in seconds. Default 300 seconds. @@ -456,14 +475,52 @@ function ARTY:SetWaitForShotTime(waittime) self.WaitForShotTime=waittime or 300 end +--- Define the safe distance between ARTY group and rearming unit or rearming place at which rearming process is possible. +-- @param #ARTY self +-- @param #number distance Safe distance in meters. Default is 100 m. +function ARTY:SetRearmingDistance(distance) + self:F({distance=distance}) + self.RearmingDistance=distance or 100 +end + --- Assign a group, which is responsible for rearming the ARTY group. If the group is too far away from the ARTY group it will be guided towards the ARTY group. -- @param #ARTY self --- @param Wrapper.Group#GROUP unit Unit that is supposed to rearm the ARTY group. +-- @param Wrapper.Group#GROUP group Group that is supposed to rearm the ARTY group. function ARTY:SetRearmingGroup(group) self:F({group=group}) self.RearmingGroup=group end +--- Set the speed the rearming group moves at towards the ARTY group or the rearming place. +-- @param #ARTY self +-- @param #number speed Speed in km/h. Default 50 km/h. +function ARTY:SetRearmingGroupSpeed(speed) + self:F({speed=speed}) + self.RearmingGroupSpeed=speed or 50 +end + +--- Define if rearming group uses mainly roads to drive to the ARTY group or rearming place. +-- @param #ARTY self +-- @param #boolean onroad If true, rearming group uses mainly roads. If false, it drives directly to the ARTY group or rearming place. +function ARTY:SetRearmingGroupOnRoad(onroad) + self:F({onroad=onroad}) + if onroad==nil then + onroad=true + end + self.RearmingGroupOnRoad=onroad +end + +--- Define if ARTY group uses mainly roads to drive to the rearming place. +-- @param #ARTY self +-- @param #boolean onroad If true, ARTY group uses mainly roads. If false, it drives directly to the rearming place. +function ARTY:SetRearmingArtyOnRoad(onroad) + self:F({onroad=onroad}) + if onroad==nil then + onroad=true + end + self.RearmingArtyOnRoad=onroad +end + --- Defines the rearming place of the ARTY group. If the place is too far away from the ARTY group it will be routed to the place. -- @param #ARTY self -- @param Wrapper.Point#COORDINATE coord Coordinates of the rearming place. @@ -584,18 +641,22 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("Number of shells = %d\n", self.Nshells0) text=text..string.format("Number of rockets = %d\n", self.Nrockets0) text=text..string.format("Number of missiles = %d\n", self.Nmissiles0) + if self.RearmingGroup or self.RearmingPlaceCoord then + text=text..string.format("Rearming safe dist. = %d m\n", self.RearmingDistance) + end if self.RearmingGroup then - text=text..string.format("Reaming group = %s\n", self.RearmingGroup:GetName()) + text=text..string.format("Rearming group = %s\n", self.RearmingGroup:GetName()) + text=text..string.format("Rearming group speed= %d km/h\n", self.RearmingGroupSpeed) + text=text..string.format("Rearming group roads= %s\n", tostring(self.RearmingGroupOnRoad)) end if self.RearmingPlaceCoord then - local dist=self.InitialCoord:Get2DDistance(self.RearmingPlaceCoord) - text=text..string.format("Reaming coord dist. = %d m\n", dist) + local dist=self.InitialCoord:Get2DDistance(self.RearmingPlaceCoord) + text=text..string.format("Rearming coord dist = %d m\n", dist) + text=text..string.format("Rearming ARTY roads = %s\n", tostring(self.RearmingArtyOnRoad)) end text=text..string.format("******************************************************\n") text=text..string.format("Targets:\n") for _, target in pairs(self.targets) do - local _clock=self:_SecondsToClock(target.time) - local _weapon=self:_WeaponTypeName(target.weapontype) text=text..string.format("- %s\n", self:_TargetInfo(target)) end text=text..string.format("******************************************************\n") @@ -619,7 +680,7 @@ function ARTY:onafterStart(Controllable, From, Event, To) self:HandleEvent(EVENTS.Dead, self._OnEventDead) -- Start checking status. - self:__Status(5) + self:__Status(self.StatusInterval) end --- After "Start" event. Initialized ROE and alarm state. Starts the event handler. @@ -798,8 +859,9 @@ function ARTY:onafterStatus(Controllable, From, Event, To) -- Group is out of moving. if self:is("Moving") then - local _speed=self.Controllable:GetVelocityKMH() - env.info(string.format("FF: Moving. Velocity = %d km/h", _speed)) + --local _speed=self.Controllable:GetVelocityKMH() + --env.info(string.format("FF: Moving. Velocity = %d km/h", _speed)) + env.info(string.format("FF: Moving")) end -- Group is rearming. @@ -815,9 +877,7 @@ function ARTY:onafterStatus(Controllable, From, Event, To) if self:is("Rearmed") then local distance=self.Controllable:GetCoordinate():Get2DDistance(self.InitialCoord) env.info(string.format("FF: Rearmed. Distance ARTY to InitalCoord = %d", distance)) - if distance > 100 then - --self:Move(self.InitialCoord, false) - else + if distance <= self.RearmingDistance then self:CombatReady() end end @@ -841,7 +901,6 @@ function ARTY:onafterStatus(Controllable, From, Event, To) -- Get a valid normal target (one that is not timed). local _normalTarget=self:_CheckNormalTargets() - -- Group is combat ready or firing but we have a high prio timed target. if self:is("CombatReady") or (self:is("Firing") and _timedTarget) then env.info(string.format("FF: Combatready or firing and high prio timed target.")) @@ -865,8 +924,8 @@ function ARTY:onafterStatus(Controllable, From, Event, To) end end - -- Call status again in 5 sec. - self:__Status(5) + -- Call status again in ~10 sec. + self:__Status(self.StatusInterval) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1077,14 +1136,13 @@ function ARTY:onafterRearm(Controllable, From, Event, To) local dR=coordRARM:Get2DDistance(self.RearmingPlaceCoord) -- Route ARTY group to rearming place. - if dA>100 then - --self:_Move(self.Controllable, self.RearmingPlaceCoord, self.Speed, true) - self:Move(self:_VicinityCoord(self.RearmingPlaceCoord, 20, 50), false) + if dA > self.RearmingDistance then + self:Move(self:_VicinityCoord(self.RearmingPlaceCoord, self.RearmingDistance/4, self.RearmingDistance/2), self.RearmingArtyOnRoad) end - -- Route Rearming unit to rearming place - if dR>100 then - self:_Move(self.RearmingGroup, self:_VicinityCoord(self.RearmingPlaceCoord, 20, 50), 50, false) + -- Route Rearming group to rearming place. + if dR > self.RearmingDistance then + self:_Move(self.RearmingGroup, self:_VicinityCoord(self.RearmingPlaceCoord, self.RearmingDistance/4, self.RearmingDistance/2), self.RearmingGroupSpeed, self.RearmingGroupOnRoad) end elseif self.RearmingGroup then @@ -1099,11 +1157,11 @@ function ARTY:onafterRearm(Controllable, From, Event, To) -- Distance between ARTY group and rearming unit. local distance=coordARTY:Get2DDistance(coordRARM) - -- If distance is larger than 100 m, the Rearming unit is routed to the ARTY group. - if distance > 100 then + -- If distance is larger than ~100 m, the Rearming unit is routed to the ARTY group. + if distance > self.RearmingDistance then - -- Route unit to ARTY group. - self:_Move(self.RearmingGroup, self:_VicinityCoord(coordARTY), 50, false) + -- Route rearming group to ARTY group. + self:_Move(self.RearmingGroup, self:_VicinityCoord(coordARTY), self.RearmingGroupSpeed, self.RearmingGroupOnRoad) end elseif self.RearmingPlaceCoord then @@ -1114,7 +1172,7 @@ function ARTY:onafterRearm(Controllable, From, Event, To) local dA=coordARTY:Get2DDistance(self.RearmingPlaceCoord) -- Route ARTY group to rearming place. - if dA>100 then + if dA > self.RearmingDistance then --self:_Move(self.Controllable, self.RearmingPlaceCoord, self.Speed, true) self:Move(self:_VicinityCoord(self.RearmingPlaceCoord), false) end @@ -1141,15 +1199,15 @@ function ARTY:onafterRearmed(Controllable, From, Event, To) -- Route ARTY group back to where it came from (if distance is > 100 m). local d1=self.Controllable:GetCoordinate():Get2DDistance(self.InitialCoord) - if d1>100 then + if d1 > self.RearmingDistance then self:Move(self.InitialCoord, false) end -- Route unit back to where it came from (if distance is > 100 m). if self.RearmingGroup and self.RearmingGroup:IsAlive() then local d=self.RearmingGroup:GetCoordinate():Get2DDistance(self.RearmingGroupCoord) - if d>100 then - self:_Move(self.RearmingGroup, self.RearmingGroupCoord, 50, false) + if d > self.RearmingDistance then + self:_Move(self.RearmingGroup, self.RearmingGroupCoord, self.RearmingGroupSpeed, self.RearmingGroupOnRoad) end end @@ -1721,10 +1779,10 @@ function ARTY:_TargetInRange(target) if _dist < self.minrange then _inrange=false - text=string.format("%s, target is out of range. Distance of %d km is below min range of %d km.", self.Controllable:GetName(), _dist/1000, self.minrange/1000) + text=string.format("%s, target is out of range. Distance of %.1f km is below min range of %.1f km.", self.Controllable:GetName(), _dist/1000, self.minrange/1000) elseif _dist > self.maxrange then _inrange=false - text=string.format("%s, target is out of range. Distance of %d km is greater than max range of %d km.", self.Controllable:GetName(), _dist/1000, self.maxrange/1000) + text=string.format("%s, target is out of range. Distance of %.1f km is greater than max range of %.1f km.", self.Controllable:GetName(), _dist/1000, self.maxrange/1000) end -- Debug output. From d9222c23cb9ab419b9a716e6891d18f104fb3e6f Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sun, 6 May 2018 17:03:31 +0200 Subject: [PATCH 083/420] ARTY v0.8.7 Added first version of move implementation. Moves are not executed yet. --- .../Moose/Functional/Artillery.lua | 139 +++++++++++++++--- 1 file changed, 117 insertions(+), 22 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 1b2d15fdf..36a847036 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -59,7 +59,8 @@ -- @field #string DisplayName Extended type name of the ARTY group. -- @field #number IniGroupStrength Inital number of units in the ARTY group. -- @field #boolean IsArtillery If true, ARTY group has attribute "Artillery". --- @field #number Speed Maximum speed of ARTY group in km/h. +-- @field #number SpeedMax Maximum speed of ARTY group in km/h. This is determined from the DCS descriptor table. +-- @field #number Speed Default speed in km/h the ARTY group moves at. Maximum speed possible is 80% of maximum speed the group can do. -- @field #number RearmingDistance Safe distance in meters between ARTY group and rearming group or place at which rearming is possible. Default 100 m. -- @field Wrapper.Group#GROUP RearmingGroup Unit designated to rearm the ARTY group. -- @field #number RearmingGroupSpeed Speed in km/h the rearming unit moves at. Default 50 km/h. @@ -275,7 +276,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #number version -ARTY.version="0.8.6" +ARTY.version="0.8.7" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -297,6 +298,7 @@ ARTY.version="0.8.6" -- TODO: Adjust documenation again. -- TODO: Add command move to make arty group move. -- DONE: remove schedulers for status event. +-- TODO: Improve handling of special weapons. When winchester? ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -342,8 +344,11 @@ function ARTY:New(group) self:T3({id=id, desc=desc}) end - -- Set speed to 0.7 of maximum in km/h. - self.Speed=self.DCSdesc.speedMax*3.6 * 0.7 + -- Maximum speed in km/h. + self.SpeedMax=self.DCSdesc.speedMax*3.6 + + -- Set speed to 0.7 of maximum. + self.Speed=self.SpeedMax * 0.7 -- Displayed name (similar to type name below) self.DisplayName=self.DCSdesc.displayName @@ -386,6 +391,7 @@ function ARTY:New(group) -- Not in diagram. self:AddTransition("*", "CombatReady", "CombatReady") self:AddTransition("*", "Status", "*") + self:AddTransition("*", "NewMove", "*") self:AddTransition("*", "Dead", "*") -- Unknown transitons. To be checked if adding these causes problems. @@ -428,7 +434,7 @@ function ARTY:AssignTargetCoord(coord, prio, radius, nshells, maxengage, time, w local _name=name or coord:ToStringLLDMS() -- Check if the name has already been used for another target. If so, the function returns a new unique name. - _name=self:_CheckTargetName(_name) + _name=self:_CheckName(self.targets, _name) -- Time in seconds. local _time=self:_ClockToSeconds(time) @@ -441,6 +447,53 @@ function ARTY:AssignTargetCoord(coord, prio, radius, nshells, maxengage, time, w -- Trigger new target event. self:NewTarget(_target) + + return _name +end + +--- Assign coordinate to where the ARTY group should move. +-- @param #ARTY self +-- @param Wrapper.Point#COORDINATE coord Coordinates of the target. +-- @param #string time (Optional) Day time at which the group should start moving. Passed as a string in format "08:13:45". +-- @param #number speed (Optinal) Speed in km/h the group should move at. Default 50 km/h. +-- @param #boolean onroad (Optional) If true, group will mainly use roads. Default off, i.e. go directly towards the specified coordinate. +-- @param #boolean cancel (Optional) If true, cancel any running attack when move should begin. Default is false. +-- @param #string name (Optional) Name of the coordinate. Default is LL DMS string of the coordinate. If the name was already given, the numbering "#01", "#02",... is appended automatically. +-- @return #string Name of the move. Can be used for further reference, e.g. deleting the move from the list. +function ARTY:AssignMoveCoord(coord, time, speed, onroad, cancel, name) + self:F({coord=coord, time=time, speed=speed, onroad=onroad, cancel=cancel, name=name}) + + -- Name of the target. + local _name=name or coord:ToStringLLDMS() + + -- Check if the name has already been used for another target. If so, the function returns a new unique name. + _name=self:_CheckName(self.moves, _name) + + -- Default is current time if no time was specified. + time=time or self:_SecondsToClock(timer.getAbsTime()) + + -- Default speed is 50 km/h. + speed=speed or 50 + + -- Default is off road. + if onroad==nil then + onroad=false + end + + -- Default is not to cancel a running attack. + if cancel==nil then + cancel=false + end + + -- Time in seconds. + local _time=self:_ClockToSeconds(time) + + -- Prepare move array. + local _move={name=_name, coord=coord, time=_time, speed=speed, onroad=onroad, cancel=cancel} + + -- Add to table. + table.insert(self.moves, _move) + end --- Set minimum firing range. Targets closer than this distance are not engaged. @@ -634,7 +687,8 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("Type = %s\n", self.Type) text=text..string.format("Display Name = %s\n", self.DisplayName) text=text..string.format("Number of units = %d\n", self.IniGroupStrength) - text=text..string.format("Max Speed = %d km/h\n", self.Speed) + text=text..string.format("Speed max = %d km/h\n", self.SpeedMax) + text=text..string.format("Speed default = %d km/h\n", self.Speed) text=text..string.format("Min range = %d km\n", self.minrange/1000) text=text..string.format("Max range = %d km\n", self.maxrange/1000) text=text..string.format("Total ammo count = %d\n", self.Nammo0) @@ -659,6 +713,12 @@ function ARTY:onafterStart(Controllable, From, Event, To) for _, target in pairs(self.targets) do text=text..string.format("- %s\n", self:_TargetInfo(target)) end + text=text..string.format("Moves:\n") + for i=1,#self.moves do + local _move=self.moves[i] + local _clock=tostring(self:_SecondsToClock(_move.time)) + text=text..string.format("- %s: time=%s, speed=%d, onroad=%s, cancel=%s\n", _move.name, _clock, _move.speed, tostring(_move.onroad), tostring(_move.cancel)) + end text=text..string.format("******************************************************\n") text=text..string.format("Shell types:\n") for _,_type in pairs(self.ammoshells) do @@ -1127,17 +1187,22 @@ function ARTY:onafterRearm(Controllable, From, Event, To) self.RearmingGroupCoord=coordRARM end - if self.RearmingGroup and self.RearmingPlaceCoord and self.Speed>0 then + if self.RearmingGroup and self.RearmingPlaceCoord and self.SpeedMax>0 then -- CASE 1: Rearming unit and ARTY group meet at rearming place. + -- Send message. + local text=string.format("%s, %s, request rearming at rearming place.", Controllable:GetName(), self.RearmingGroup:GetName()) + self:T(ARTY.id..text) + MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) + -- Distances. local dA=coordARTY:Get2DDistance(self.RearmingPlaceCoord) local dR=coordRARM:Get2DDistance(self.RearmingPlaceCoord) -- Route ARTY group to rearming place. if dA > self.RearmingDistance then - self:Move(self:_VicinityCoord(self.RearmingPlaceCoord, self.RearmingDistance/4, self.RearmingDistance/2), self.RearmingArtyOnRoad) + self:Move(self:_VicinityCoord(self.RearmingPlaceCoord, self.RearmingDistance/4, self.RearmingDistance/2), self.Speed, self.RearmingArtyOnRoad) end -- Route Rearming group to rearming place. @@ -1167,14 +1232,18 @@ function ARTY:onafterRearm(Controllable, From, Event, To) elseif self.RearmingPlaceCoord then -- CASE 3: ARTY drives to rearming place. + + -- Send message. + local text=string.format("%s, moving to rearming place.", Controllable:GetName()) + self:T(ARTY.id..text) + MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) -- Distance. local dA=coordARTY:Get2DDistance(self.RearmingPlaceCoord) -- Route ARTY group to rearming place. if dA > self.RearmingDistance then - --self:_Move(self.Controllable, self.RearmingPlaceCoord, self.Speed, true) - self:Move(self:_VicinityCoord(self.RearmingPlaceCoord), false) + self:Move(self:_VicinityCoord(self.RearmingPlaceCoord), self.Speed, self.RearmingArtyOnRoad) end end @@ -1200,7 +1269,7 @@ function ARTY:onafterRearmed(Controllable, From, Event, To) -- Route ARTY group back to where it came from (if distance is > 100 m). local d1=self.Controllable:GetCoordinate():Get2DDistance(self.InitialCoord) if d1 > self.RearmingDistance then - self:Move(self.InitialCoord, false) + self:Move(self.InitialCoord, self.Speed, self.RearmingArtyOnRoad) end -- Route unit back to where it came from (if distance is > 100 m). @@ -1266,7 +1335,7 @@ function ARTY:onbeforeMove(Controllable, From, Event, To, ToCoord, OnRoad) self:_EventFromTo("onbeforeMove", Event, From, To) -- Check if group can actually move... - if self.Speed==0 then + if self.SpeedMax==0 then return false end @@ -1285,16 +1354,20 @@ end -- @param #string Event Event. -- @param #string To To state. -- @param Wrapper.Point#COORDINATE ToCoord Coordinate to which the ARTY group should move. +-- @param #number Speed Speed in km/h at which the grou p should move. -- @param #boolean OnRoad If true group should move on road mainly. -function ARTY:onafterMove(Controllable, From, Event, To, ToCoord, OnRoad) +function ARTY:onafterMove(Controllable, From, Event, To, ToCoord, Speed, OnRoad) self:_EventFromTo("onafterMove", Event, From, To) -- Set alarm state to green and ROE to weapon hold. self.Controllable:OptionAlarmStateGreen() self.Controllable:OptionROEHoldFire() + + -- Take care of max speed. + local _Speed=math.min(Speed, self.SpeedMax) -- Route group to coodinate. - self:_Move(self.Controllable, ToCoord, self.Speed, OnRoad) + self:_Move(self.Controllable, ToCoord, _Speed, OnRoad) end @@ -1325,7 +1398,7 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param #table target Array holding the target info. +-- @param #table target Array holding the target parameters. -- @return #boolean If true, proceed to onafterOpenfire. function ARTY:onafterNewTarget(Controllable, From, Event, To, target) self:_EventFromTo("onafterNewTarget", Event, From, To) @@ -1336,6 +1409,24 @@ function ARTY:onafterNewTarget(Controllable, From, Event, To, target) self:T(ARTY.id..text) end +--- After "NewMove" event. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #table move Array holding the move parameters. +-- @return #boolean If true, proceed to onafterOpenfire. +function ARTY:onafterNewMove(Controllable, From, Event, To, move) + self:_EventFromTo("onafterNewTarget", Event, From, To) + + -- Debug message. + local text=string.format("Adding new move %s.", move.name) + MESSAGE:New(text, 30):ToAllIf(self.Debug) + self:T(ARTY.id..text) +end + + --- After "Dead" event, when a unit has died. When all units of a group are dead trigger "Stop" event. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. @@ -1724,12 +1815,13 @@ function ARTY:_GetTargetByName(name) end ---- Get the weapon type name, which should be used to attack the target. +--- Check if a name is unique. If not, a new unique name is created by adding a running index #01, #02, ... -- @param #ARTY self --- @param #string name Desired target name. +-- @param #table givennames Table with entries of already given names. Must contain a .name item. +-- @param #string name Desired name. -- @return #string Unique name, which is not already given for another target. -function ARTY:_CheckTargetName(name) - self:F2(name) +function ARTY:_CheckName(givennames, name) + self:F2({givennames=givennames, name=name}) local newname=name local counter=1 @@ -1739,12 +1831,12 @@ function ARTY:_CheckTargetName(name) local unique=true -- Loop over all targets already defined. - for _,_target in pairs(self.targets) do + for _,_target in pairs(givennames) do -- Target name. - local _targetname=_target.name + local _givenname=givennames.name - if _targetname==newname then + if _givenname==newname then -- Define new name = "name #01" newname=string.format("%s #%02d", name, counter) @@ -1968,6 +2060,9 @@ function ARTY:_Move(group, ToCoord, Speed, OnRoad) -- Set formation. local formation = "Off road" + -- Default speed is 30 km/h. + Speed=Speed or 30 + -- Current coordinates of group. local cpini=group:GetCoordinate() From 3157a63b7e817d23c684167d7a00b667985d5ffc Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sun, 6 May 2018 20:40:14 +0200 Subject: [PATCH 084/420] ARTY v0.8.8 Added working implementation for moves. Other minor adjustments. --- .../Moose/Functional/Artillery.lua | 190 +++++++++++++++--- 1 file changed, 157 insertions(+), 33 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 36a847036..ee6ab8178 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -47,6 +47,7 @@ -- @field #table targets All targets assigned. -- @field #table moves All moves assigned. -- @field #table currentTarget Holds the current target, if there is one assigned. +-- @field #table currentMove Holds the current commanded move, if there is one assigned. -- @field #number Nammo0 Initial amount total ammunition (shells+rockets+missiles) of the whole group. -- @field #number Nshells0 Initial amount of shells of the whole group. -- @field #number Nrockets0 Initial amount of rockets of the whole group. @@ -225,11 +226,12 @@ -- -- @field #ARTY ARTY={ - ClassName = "ARTY", - Debug = true, - targets = {}, - moves = {}, - currentTarget = nil, + ClassName="ARTY", + Debug=true, + targets={}, + moves={}, + currentTarget=nil, + currentMove=nil, Nammo0=0, Nshells0=0, Nrockets0=0, @@ -276,7 +278,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #number version -ARTY.version="0.8.7" +ARTY.version="0.8.8" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -296,7 +298,7 @@ ARTY.version="0.8.7" -- DONE: Add pseudo user transitions. OnAfter... -- DONE: Make reaming unit a group. -- TODO: Adjust documenation again. --- TODO: Add command move to make arty group move. +-- DONE: Add command move to make arty group move. -- DONE: remove schedulers for status event. -- TODO: Improve handling of special weapons. When winchester? @@ -606,12 +608,12 @@ function ARTY:SetDebugOFF() self.Debug=false end ---- Delete target from target list. +--- Delete a target from target list. -- @param #ARTY self -- @param #string name Name of the target. function ARTY:RemoveTarget(name) self:F2(name) - local id=self:_GetTargetByName(name) + local id=self:_GetTargetIndexByName(name) if id then self:T(ARTY.id..string.format("Group %s: Removing target %s (id=%d).", self.Controllable:GetName(), name, id)) table.remove(self.targets, id) @@ -619,6 +621,19 @@ function ARTY:RemoveTarget(name) self:T(ARTY.id..string.format("Group %s: Number of targets = %d.", self.Controllable:GetName(), #self.targets)) end +--- Delete a move from move list. +-- @param #ARTY self +-- @param #string name Name of the target. +function ARTY:RemoveMove(name) + self:F2(name) + local id=self:_GetMoveIndexByName(name) + if id then + self:T(ARTY.id..string.format("Group %s: Removing move %s (id=%d).", self.Controllable:GetName(), name, id)) + table.remove(self.moves, id) + end + self:T(ARTY.id..string.format("Group %s: Number of moves = %d.", self.Controllable:GetName(), #self.moves)) +end + --- Delete ALL targets from current target list. -- @param #ARTY self function ARTY:RemoveAllTargets() @@ -679,7 +694,7 @@ function ARTY:onafterStart(Controllable, From, Event, To) MESSAGE:New(text, 10):ToAllIf(self.Debug) -- Get Ammo. - self.Nammo0, self.Nshells0, self.Nrockets0, self.Nmissiles0=self:GetAmmo(self.Controllable, self.Debug) + self.Nammo0, self.Nshells0, self.Nrockets0, self.Nmissiles0=self:GetAmmo(self.Debug) local text=string.format("\n******************************************************\n") text=text..string.format("Arty group = %s\n", Controllable:GetName()) @@ -715,9 +730,7 @@ function ARTY:onafterStart(Controllable, From, Event, To) end text=text..string.format("Moves:\n") for i=1,#self.moves do - local _move=self.moves[i] - local _clock=tostring(self:_SecondsToClock(_move.time)) - text=text..string.format("- %s: time=%s, speed=%d, onroad=%s, cancel=%s\n", _move.name, _clock, _move.speed, tostring(_move.onroad), tostring(_move.cancel)) + text=text..string.format("- %s\n", self:_MoveInfo(self.moves[i])) end text=text..string.format("******************************************************\n") text=text..string.format("Shell types:\n") @@ -748,7 +761,7 @@ end function ARTY:_StatusReport() -- Get Ammo. - local Nammo, Nshells, Nrockets, Nmissiles=self:GetAmmo(self.Controllable) + local Nammo, Nshells, Nrockets, Nmissiles=self:GetAmmo() local Tnow=timer.getTime() local text=string.format("\n******************* STATUS ***************************\n") @@ -766,8 +779,12 @@ function ARTY:_StatusReport() end text=text..string.format("Nshots curr. Target = %d\n", self.Nshots) text=text..string.format("Targets:\n") - for _, target in pairs(self.targets) do - text=text..string.format("- %s\n", self:_TargetInfo(target)) + for i=1,#self.targets do + text=text..string.format("- %s\n", self:_TargetInfo(self.targets[i])) + end + text=text..string.format("Moves:\n") + for i=1,#self.moves do + text=text..string.format("- %s\n", self:_MoveInfo(self.moves[i])) end text=text..string.format("******************************************************") env.info(ARTY.id..text) @@ -812,7 +829,7 @@ function ARTY:_OnEventShot(EventData) MESSAGE:New(text, 5):ToAllIf(self.Debug) -- Get current ammo. - local _nammo,_nshells,_nrockets,_nmissiles=self:GetAmmo(self.Controllable) + local _nammo,_nshells,_nrockets,_nmissiles=self:GetAmmo() if _nammo==0 then @@ -960,9 +977,18 @@ function ARTY:onafterStatus(Controllable, From, Event, To) -- Get a valid normal target (one that is not timed). local _normalTarget=self:_CheckNormalTargets() + + -- Get a commaned move to another location. + local _move=self:_CheckMoves() -- Group is combat ready or firing but we have a high prio timed target. - if self:is("CombatReady") or (self:is("Firing") and _timedTarget) then + if (self:is("CombatReady") or self:is("Firing")) and _move then + + -- Command to move. + self.currentMove=_move + self:Move(_move.coord, _move.speed, _move.onroad) + + elseif self:is("CombatReady") or (self:is("Firing") and _timedTarget) then env.info(string.format("FF: Combatready or firing and high prio timed target.")) -- Engage target. @@ -1048,7 +1074,7 @@ function ARTY:onafterOpenFire(Controllable, From, Event, To, target) --_coord:MarkToAll("Arty Target") -- Get target array index. - local id=self:_GetTargetByName(target.name) + local id=self:_GetTargetIndexByName(target.name) -- Target is now under fire and has been engaged once more. if id then @@ -1062,9 +1088,36 @@ function ARTY:onafterOpenFire(Controllable, From, Event, To, target) -- Distance to target local range=Controllable:GetCoordinate():Get2DDistance(target.coord) + + -- Get ammo. + local Nammo, Nshells, Nrockets, Nmissiles=self:GetAmmo() + local nfire=Nammo + local _type="shots" + if self.WeaponType==ARTY.WeaponType.Auto then + nfire=Nammo + _type="shots" + elseif self.WeaponType==ARTY.WeaponType.Cannon then + nfire=Nshells + _type="shells" + elseif self.WeaponType==ARTY.WeaponType.Rockets then + nfire=Nrockets + _type="rockets" + elseif self.WeaponType==ARTY.WeaponType.UnguidedAny then + nfire=Nshells+Nrockets + _type="shells or rockets" + elseif self.WeaponType==ARTY.WeaponType.GuidedMissile then + nfire=Nmissiles + _type="missiles" + elseif self.WeaponType==ARTY.WeaponType.CruiseMissile then + nfire=Nmissiles + _type="cruise missiles" + end + + -- Adjust if less than requested ammo is left. + local _n=math.min(target.nshells, nfire) -- Send message. - local text=string.format("%s, opening fire on target %s with %s shells. Distance %.1f km.", Controllable:GetName(), target.name, target.nshells, range/1000) + local text=string.format("%s, opening fire on target %s with %d %s. Distance %.1f km.", Controllable:GetName(), target.name, _n, _type, range/1000) self:T(ARTY.id..text) MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report) @@ -1093,7 +1146,7 @@ function ARTY:onafterCeaseFire(Controllable, From, Event, To, target) MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report) -- Get target array index. - local id=self:_GetTargetByName(target.name) + local id=self:_GetTargetIndexByName(target.name) -- Increase engaged counter if id then @@ -1289,7 +1342,7 @@ function ARTY:_CheckRearmed() self:F2() -- Get current ammo. - local nammo,nshells,nrockets,nmissiles=self:GetAmmo(self.Controllable) + local nammo,nshells,nrockets,nmissiles=self:GetAmmo() -- Number of units still alive. local units=self.Controllable:GetUnits() @@ -1353,7 +1406,7 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param Wrapper.Point#COORDINATE ToCoord Coordinate to which the ARTY group should move. +-- @param Core.Point#COORDINATE ToCoord Coordinate to which the ARTY group should move. -- @param #number Speed Speed in km/h at which the grou p should move. -- @param #boolean OnRoad If true group should move on road mainly. function ARTY:onafterMove(Controllable, From, Event, To, ToCoord, Speed, OnRoad) @@ -1365,6 +1418,11 @@ function ARTY:onafterMove(Controllable, From, Event, To, ToCoord, Speed, OnRoad) -- Take care of max speed. local _Speed=math.min(Speed, self.SpeedMax) + + -- Smoke coordinate + if self.Debug then + ToCoord:SmokeRed() + end -- Route group to coodinate. self:_Move(self.Controllable, ToCoord, _Speed, OnRoad) @@ -1388,6 +1446,12 @@ function ARTY:onafterArrived(Controllable, From, Event, To) self:T(ARTY.id..text) MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) + -- Remove executed move from queue. + if self.currentMove then + self:RemoveMove(self.currentMove.name) + self.currentMove=nil + end + end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1604,6 +1668,36 @@ function ARTY:_CheckTimedTargets() return nil end +--- Check all moves and return the one which should be executed next. +-- @param #ARTY self +-- @return #table Move which is due. +function ARTY:_CheckMoves() + self:F3() + + -- Current time. + local Tnow=timer.getAbsTime() + + -- Sort Targets wrt time. + self:_SortQueueTime(self.moves) + + -- Check if we are currently firing. + local firing=false + if self.currentTarget then + firing=true + end + + for i=1,#self.moves do + local _move=self.moves[i] + + -- Check if time for move is reached. + if Tnow >= _move.time and (firing==false or _move.cancel) then + return _move + end + end + + return nil +end + --- Check all normal (untimed) targets and return the target with the highest priority which has been engaged the fewest times. -- @param #ARTY self -- @return #table Target which is due to be attacked now or nil if no target could be found. @@ -1634,15 +1728,15 @@ end --- Get the number of shells a unit or group currently has. For a group the ammo count of all units is summed up. -- @param #ARTY self --- @param Wrapper.Controllable#CONTROLLABLE controllable Controllable for which the ammo is counted. -- @param #boolean display Display ammo table as message to all. Default false. -- @return #number Total amount of ammo the whole group has left. -- @return #number Number of shells the group has left. -- @return #number Number of rockets the group has left. -- @return #number Number of missiles the group has left. -function ARTY:GetAmmo(controllable, display) - self:F3({controllable=controllable, display=display}) +function ARTY:GetAmmo(display) + self:F3({display=display}) + -- Default is display false. if display==nil then display=false end @@ -1654,7 +1748,7 @@ function ARTY:GetAmmo(controllable, display) local nmissiles=0 -- Get all units. - local units=controllable:GetUnits() + local units=self.Controllable:GetUnits() if units==nil then return nammo, nshells, nrockets, nmissiles end @@ -1744,10 +1838,10 @@ function ARTY:GetAmmo(controllable, display) end end + -- Debug text and send message. self:T2(ARTY.id..text) MESSAGE:New(text, 10):ToAllIf(display) - - + end end @@ -1795,11 +1889,11 @@ function ARTY:_CheckShootingStarted() end end ---- Get a target by its name. +--- Get the index of a target by its name. -- @param #ARTY self -- @param #string name Name of target. -- @return #number Arrayindex of target. -function ARTY:_GetTargetByName(name) +function ARTY:_GetTargetIndexByName(name) self:F2(name) for i=1,#self.targets do @@ -1814,6 +1908,26 @@ function ARTY:_GetTargetByName(name) return nil end +--- Get the index of a move by its name. +-- @param #ARTY self +-- @param #string name Name of move. +-- @return #number Arrayindex of move. +function ARTY:_GetMoveIndexByName(name) + self:F2(name) + + for i=1,#self.moves do + local movename=self.moves[i].name + if movename==name then + self:T2(ARTY.id..string.format("Found move with name %s. Index = %d", name, i)) + return i + end + end + + self:E(ARTY.id..string.format("ERROR: Move with name %s could not be found!", name)) + return nil +end + + --- Check if a name is unique. If not, a new unique name is created by adding a running index #01, #02, ... -- @param #ARTY self @@ -1959,17 +2073,27 @@ function ARTY:_split(str, sep) return result end ---- Returns the target info as formatted string. +--- Returns the target parameters as formatted string. -- @param #ARTY self -- @return #string name, prio, radius, nshells, engaged, maxengage, time, weapontype function ARTY:_TargetInfo(target) local clock=tostring(self:_SecondsToClock(target.time)) local weapon=self:_WeaponTypeName(target.weapontype) local _underfire=tostring(target.underfire) - return string.format("%s, prio=%d, radius=%d, nshells=%d, engaged=%d/%d, weapontype=%s, time=%s, underfire=%s", + return string.format("%s: prio=%d, radius=%d, nshells=%d, engaged=%d/%d, weapontype=%s, time=%s, underfire=%s", target.name, target.prio, target.radius, target.nshells, target.engaged, target.maxengage, weapon, clock,_underfire) end +--- Returns a formatted string with information about all move parameters. +-- @param #ARTY self +-- @param #table move Move table item. +-- @return #string Info string. +function ARTY:_MoveInfo(move) + self:F3(move) + local _clock=self:_SecondsToClock(move.time) + return string.format("%s: time=%s, speed=%d, onroad=%s, cancel=%s", move.name, _clock, move.speed, tostring(move.onroad), tostring(move.cancel)) +end + --- Convert time in seconds to hours, minutes and seconds. -- @param #ARTY self -- @param #number seconds Time in seconds. From 6554fa55d16dfc85352b4bf1f39f90d26346aab4 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 7 May 2018 00:20:19 +0200 Subject: [PATCH 085/420] ARTY v0.8.8 Minor Changes. --- Moose Development/Moose/Functional/Artillery.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index ee6ab8178..227f67413 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -763,9 +763,11 @@ function ARTY:_StatusReport() -- Get Ammo. local Nammo, Nshells, Nrockets, Nmissiles=self:GetAmmo() local Tnow=timer.getTime() + local Clock=self:_SecondsToClock(timer.getAbsTime()) local text=string.format("\n******************* STATUS ***************************\n") text=text..string.format("ARTY group = %s\n", self.Controllable:GetName()) + text=text..string.format("Clock = %s\n", Clock) text=text..string.format("FSM state = %s\n", self:GetState()) text=text..string.format("Total ammo count = %d\n", Nammo) text=text..string.format("Number of shells = %d\n", Nshells) @@ -1469,7 +1471,7 @@ function ARTY:onafterNewTarget(Controllable, From, Event, To, target) -- Debug message. local text=string.format("Adding new target %s.", target.name) - MESSAGE:New(text, 30):ToAllIf(self.Debug) + --MESSAGE:New(text, 30):ToAllIf(self.Debug) self:T(ARTY.id..text) end From dd636939bb075863a978af615462013fa208819a Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 7 May 2018 06:11:58 +0200 Subject: [PATCH 086/420] Documentation and performance fix. --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 3 + Moose Development/Moose/Cargo/Cargo.lua | 70 ++++++------------- Moose Development/Moose/Core/Point.lua | 14 ++-- .../Moose/Wrapper/Controllable.lua | 4 +- 4 files changed, 38 insertions(+), 53 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index be3b2b77e..424f81d9c 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -19,6 +19,9 @@ -- AI\_CARGO\APC brings a dynamic cargo handling capability for AI groups. -- -- Armoured Personnel Carriers (APC), Trucks, Jeeps and other ground based carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. +-- The AI\_CARGO\APC module uses the @{Cargo} capabilities within the MOOSE framework. +-- CARGO derived objects must be declared within the mission to make the AI\_CARGO\APC object recognize the cargo. +-- Please consult the @{Cargo} module for more information. -- -- ## Cargo loading. -- diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 215b1cba7..8869a14cc 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -6,31 +6,11 @@ -- -- === -- --- Cargo can be of various forms, always are composed out of ONE object ( one unit or one static or one slingload crate ): --- --- * CARGO_UNIT, represented by a @{Unit} in a singleton @{Group}: Cargo can be represented by a Unit in a Group. a CARGO_UNIT is representable... --- * CARGO_GROUP, represented by a @{Group}. A CARGO_GROUP is reportable... -- -- This module is still under construction, but is described above works already, and will keep working ... -- -- === -- --- # Demo Missions --- --- ### [CARGO Demo Missions source code](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CGO%20-%20Cargo) --- --- ### [CARGO Demo Missions, only for beta testers](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CGO%20-%20Cargo) --- --- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) --- --- === --- --- # YouTube Channel --- --- ### [CARGO YouTube Channel](https://www.youtube.com/watch?v=tM00lTlkpYs&list=PL7ZUrU4zZUl2zUTuKrLW5RsO9zLMqUtbf) --- --- === --- -- ### Author: **FlightControl** -- ### Contributions: -- @@ -172,11 +152,31 @@ do -- CARGO -- @field #boolean Representable This flag defines if the cargo can be represented by a DCS Unit. -- @field #boolean Containable This flag defines if the cargo can be contained within a DCS Unit. - --- # (R2.1) CARGO class, extends @{Fsm#FSM_PROCESS} + --- # (R2.4) CARGO class, extends @{Fsm#FSM_PROCESS} -- -- The CARGO class defines the core functions that defines a cargo object within MOOSE. - -- A cargo is a logical object defined that is available for transport, and has a life status within a simulation. + -- A cargo is a **logical object** defined that is available for transport, and has a life status within a simulation. + -- + -- CARGO is not meant to be used directly by mission designers, but provides a base class for **concrete cargo implementation classes** to handle: + -- + -- * Cargo **group objects**, implemented by the @{Cargo.CargoGroup#CARGO_GROUP} class. + -- * Cargo **Unit objects**, implemented by the @{Cargo.CargoUnit#CARGO_UNIT} class. + -- * Cargo **Crate objects**, implemented by the @{Cargo.CargoCrate#CARGO_CRATE} class. + -- * Cargo **Sling Load objects**, implemented by the @{Cargo.CargoSlingload#CARGO_SLINGLOAD} class. -- + -- The above cargo classes are used by the AI\_CARGO\_ classes to allow AI groups to transport cargo: + -- + -- * AI Armoured Personnel Carriers to transport cargo and engage in battles, using the @{AI.AI_Cargo_APC#AI_CARGO_APC} class. + -- * AI Helicopters to transport cargo, using the @{AI.AI_Cargo_Helicopter#AI_CARGO_HELICOPTER} class. + -- * AI Planes to transport cargo, using the @{AI.AI_Cargo_Plane#AI_CARGO_PLANE} class. + -- * AI Ships is planned. + -- + -- The above cargo classes are also used by the TASK\_CARGO\_ classes to allow human players to transport cargo as part of a tasking: + -- + -- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT} to transport cargo by human players. + -- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_CSAR} to transport downed pilots by human players. + -- + -- -- The CARGO is a state machine: it manages the different events and states of the cargo. -- All derived classes from CARGO follow the same state machine, expose the same cargo event functions, and provide the same cargo states. -- @@ -186,32 +186,8 @@ do -- CARGO -- * @{#CARGO.Load}( ToCarrier ): Loads the cargo into a carrier, regardless of its position. -- * @{#CARGO.UnBoard}( ToPointVec2 ): UnBoard the cargo from a carrier. This will trigger a movement of the cargo to the option ToPointVec2. -- * @{#CARGO.UnLoad}( ToPointVec2 ): UnLoads the cargo from a carrier. - -- * @{#CARGO.Dead}( Controllable ): The cargo is dead. The cargo process will be ended. + -- * @{#CARGO.Destroyed}( Controllable ): The cargo is dead. The cargo process will be ended. -- - -- ## CARGO States: - -- - -- * **UnLoaded**: The cargo is unloaded from a carrier. - -- * **Boarding**: The cargo is currently boarding (= running) into a carrier. - -- * **Loaded**: The cargo is loaded into a carrier. - -- * **UnBoarding**: The cargo is currently unboarding (=running) from a carrier. - -- * **Dead**: The cargo is dead ... - -- * **End**: The process has come to an end. - -- - -- ## CARGO state transition methods: - -- - -- State transition functions can be set **by the mission designer** customizing or improving the behaviour of the state. - -- There are 2 moments when state transition methods will be called by the state machine: - -- - -- * **Leaving** the state. - -- The state transition method needs to start with the name **OnLeave + the name of the state**. - -- If the state transition method returns false, then the processing of the state transition will not be done! - -- If you want to change the behaviour of the AIControllable at this event, return false, - -- but then you'll need to specify your own logic using the AIControllable! - -- - -- * **Entering** the state. - -- The state transition method needs to start with the name **OnEnter + the name of the state**. - -- These state transition methods need to provide a return value, which is specified at the function description. - -- -- @field #CARGO CARGO = { ClassName = "CARGO", diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index c49f02994..79bacd0f6 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -942,11 +942,15 @@ do -- COORDINATE function COORDINATE:GetPathOnRoad(ToCoord) local Path={} local path = land.findPathOnRoads("roads", self.x, self.z, ToCoord.x, ToCoord.z) - for i, v in ipairs(path) do - --self:E(v) - local coord=COORDINATE:NewFromVec2(v) - Path[#Path+1]=COORDINATE:NewFromVec2(v) - end + Path[#Path+1]=COORDINATE:NewFromVec2(path[1]) + Path[#Path+1]=COORDINATE:NewFromVec2(path[#path]) + -- I've removed this stuff because it severely slows down DCS in case of paths with a lot of segments. + -- Just the beginning and the end point is sufficient. +-- for i, v in ipairs(path) do +-- self:I(v) +-- local coord=COORDINATE:NewFromVec2(v) +-- Path[#Path+1]=COORDINATE:NewFromVec2(v) +-- end return Path end diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 11809e47a..9667bd6fc 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -357,7 +357,9 @@ function CONTROLLABLE:SetTask( DCSTask, WaitTime ) local function SetTask( Controller, DCSTask ) if self and self:IsAlive() then local Controller = self:_GetController() + self:I( "Before SetTask" ) Controller:setTask( DCSTask ) + self:I( "After SetTask" ) else BASE:E( { DCSControllableName .. " is not alive anymore.", DCSTask = DCSTask } ) end @@ -496,7 +498,7 @@ function CONTROLLABLE:SetTaskWaypoint( Waypoint, Task ) Waypoint.task = self:TaskCombo( { Task } ) - self:T3( { Waypoint.task } ) + self:F( { Waypoint.task } ) return Waypoint.task end From 92c3b530cce1792c3e0ceadbcfda81079c2db2b2 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 7 May 2018 17:33:48 +0200 Subject: [PATCH 087/420] ARTY v0.8.9 Improved documentation. --- .../Moose/Functional/Artillery.lua | 123 ++++++++++++++++-- 1 file changed, 114 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 227f67413..2edf7dd89 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -7,7 +7,7 @@ -- -- ==== -- --- The ARTY class can be used to easily assign targets for artillery units. +-- The ARTY class can be used to easily assign and manage targets for artillery units. -- -- ## Features: -- @@ -16,7 +16,8 @@ -- * Engagements can be scheduled, i.e. will be executed at a certain time of the day. -- * Special weapon types can be selected for each attack, e.g. cruise missiles for Naval units. -- * Automatic rearming once the artillery is out of ammo. --- * Finite state machine implementation. User can interact when certain events occur. +-- * New targets can be added during the mission, e.g. when they are detected by recon units. +-- * Finite state machine implementation. Mission designer can interact when certain events occur. -- -- ==== -- @@ -93,15 +94,17 @@ -- -- ![Process](..\Presentations\ARTY\ARTY_Process.png) -- +-- ### Blue Branch -- After the FMS process is started the ARTY group will be in the state **CombatReady**. Once a target is assigned the **OpenFire** event will be triggered and the group starts -- firing. At this point the group in in the state **Firing**. --- -- When the defined number of shots has been fired on the current target the event **CeaseFire** is triggered. The group will stop firing and go back to the state **CombatReady**. -- If another target is defined (or multiple engagements of the same target), the cycle starts anew. -- +-- ### Violet Branch -- When the ARTY group runs out of ammunition, the event **Winchester** is triggered and the group enters the state **OutOfAmmo**. -- In this state, the group is unable to engage further targets. -- +-- ### Red Branch -- With the @{#ARTY.SetRearmingGroup}(*group*) command, a special group can be defined to rearm the ARTY group. If this unit has been assigned and the group has entered the state -- **OutOfAmmo** the event **Rearm** is triggered followed by a transition to the state **Rearming**. -- If the rearming group is less than 100 meters away from the ARTY group, the rearming process starts. If the rearming group is more than 100 meters away from the ARTY unit, the @@ -109,6 +112,16 @@ -- -- Once the rearming is complete, the **Rearmed** event is triggered and the group enters the state **CombatReady**. At this point targeted can be engaged again. -- +-- ### Green Branch +-- The ARTY group can be ordered to change its position via the @{#ARTY.AssignMoveCoord}() function as described below. When the group receives the command to move +-- the event **Move** is triggered and the state changes to **Moving**. When the unit arrives to its destination the event **Arrived** is triggered and the group +-- becomes **CombatReady** again. +-- +-- Note, that the ARTY group will not open fire while it is in state **Moving**. This property differentiates artillery from tanks. +-- +-- ### Yellow Branch +-- When a new target is assigned via the @{#ARTY.AssignTargetCoord}() function (see below), the **NewTarget** event is triggered. +-- -- ## Assigning Targets -- Assigning targets is a central point of the ARTY class. Multiple targets can be assigned simultanioulsly and are put into a queue. -- Of course, targets can be added at any time during the mission. For example, once they are detected by a reconnaissance unit. @@ -135,9 +148,9 @@ -- or the same target should be attacked two or more times with different parameters a suffix "#01", "#02", "#03" is automatically appended to the specified name. -- -- ## Target Queue --- In case, multiple targets have been defined, it is important to understand how the target queue works. +-- In case multiple targets have been defined, it is important to understand how the target queue works. -- --- Here, the important parameters are the priority *prio*, the number of engagements *maxengage* and the scheduled *time* as described above. +-- Here, the essential parameters are the priority *prio*, the number of engagements *maxengage* and the scheduled *time* as described above. -- -- For example, we have assigned two targets one with *prio*=10 and the other with *prio*=50 and both targets should be engaged three times (*maxengage*=3). -- Let's first consider the case that none of the targets is scheduled to be executed at a certain time (*time*=nil). @@ -146,7 +159,7 @@ -- have been engaged equally often, the target with the higher priority is engaged again. This coninues until a target has engaged three times. -- Once the maximum number of engagements is reached, the target is deleted from the queue. -- --- In other works, the queue is first sorted with respect to the number of engagements and targets with the same number of engagements are sorted with +-- In other words, the queue is first sorted with respect to the number of engagements and targets with the same number of engagements are sorted with -- respect to their priority. -- -- ### Timed Engagements @@ -198,6 +211,43 @@ -- * @{#ARTY.WeaponType}.GuidedMissile: Any guided missiles are used during the attack. Corresponding ammo type are missiles and can be defined by @{#ARTY.SetMissileTypes}. -- * @{#ARTY.WeaponType}.CruiseMissile: Only cruise missiles are used during the attack. Corresponding ammo type are missiles and can be defined by @{#ARTY.SetMissileTypes}. -- +-- ## Assigning Moves +-- The ARTY group can be commanded to move. This is done by the @{#ARTY.AssignMoveCoord}(*coord*,*time*,*speed*,*onroad*,*cancel*,*name*) function. +-- With this multiple timed moves of the group can be scheduled easily. By default, these moves will only be executed if the group is state **CombatReady**. +-- +-- ### Parameters +-- +-- * *coord*: Coordinates where the group should move to given as @{Point#COORDINATE} object. +-- * *time*: The time when the move should be executed. This has to be given as a string in the format "hh:mm:ss" (hh=hours, mm=minutes, ss=seconds). +-- * *speed*: Speed of the group in km/h. +-- * *onroad*: If this parameter is set to true, the group uses mainly roads to get to the commanded coordinates. +-- * *cancel*: If set to true, any current engagement of targets is cancelled at the time the move should be executed. +-- * *name*: Can be used to set a user defined name of the move. By default the name is created from the LL DMS coordinates. +-- +-- ## Automatic Rearming +-- +-- If an ARTY group runs out of ammunition, it can be rearmed automatically. +-- +-- ### Rearming Group +-- The first way to activate the automatic rearming is to define a rearming group with the function @{#ARTY.SetRearmingGroup}(*group*). For the blue side, this +-- could be a M181 transport truck and for the red side an Ural-375 truck. +-- +-- Once the ARTY group is out of ammo and the **Rearm** event is triggered, the defined rearming truck will drive to the ARTY group. +-- So the rearming truck does not have to be placed nearby the artillery group. When the rearming is complete, the rearming truck will drive back to its original position. +-- +-- ### Rearming Place +-- The second alternative is to define a rearming place, e.g. a FRAP, airport or any other warehouse. This is done with the function @{#ARTY.SetRearmingPlace}(*coord*). +-- The parameter *coord* specifies the coordinate of the rearming place which should not be further away then 100 meters from the warehouse. +-- +-- When the **Rearm** event is triggered, the ARTY group will move to the rearming place. Of course, the group must be mobil. So for a mortar this rearming procedure would not work. +-- +-- After the rearming is complete, the ARTY group will move back to its original position and resume normal operations. +-- +-- ### Rearming Group **and** Rearming Place +-- If both a rearming group *and* a rearming place are specified like described above, both the ARTY group and the rearming truck will move to the rearming place and meet there. +-- +-- After the rearming is complete, both groups will move back to their original positions. +-- -- ## Fine Tuning -- -- The mission designer has a few options to tailor the ARTY object according to his needs. @@ -215,13 +265,66 @@ -- ## Examples -- -- ### Assigning Multiple Targets --- This basic example illustrates how to assign multiple targets. +-- This basic example illustrates how to assign multiple targets and defining a rearming group. +-- -- Creat a new ARTY object from a Paladin group. +-- paladin=ARTY:New(GROUP:FindByName("Blue Paladin")) +-- +-- -- Define a rearming group. This is a Transport M818 truck. +-- paladin:SetRearmingGroup(GROUP:FindByName("Blue Ammo Truck")) +-- +-- -- Set the max firing range. A Paladin unit has a range of 20 km. +-- paladin:SetMaxFiringRange(20) +-- +-- -- Low priorty (90) target, will be engage last. Target is engaged two times. At each engagement five shots are fired. +-- paladin:AssignTargetCoord(GROUP:FindByName("Red Targets 3"):GetCoordinate(), 90, nil, 5, 2) +-- -- Medium priorty (nil=50) target, will be engage second. Target is engaged two times. At each engagement ten shots are fired. +-- paladin:AssignTargetCoord(GROUP:FindByName("Red Targets 1"):GetCoordinate(), nil, nil, 10, 2) +-- -- High priorty (10) target, will be engage first. Target is engaged three times. At each engagement twenty shots are fired. +-- paladin:AssignTargetCoord(GROUP:FindByName("Red Targets 2"):GetCoordinate(), 10, nil, 20, 3) +-- +-- -- Start ARTY process. +-- paladin:Start() +-- **Note** +-- +-- * If a parameter should be set to its default value, it has to be set to *nil* if other non-default parameters follow. Parameters at the end can simply be skiped. +-- * In this example, the target coordinates are taken from groups placed in the mission edit using the COORDINATE:GetCoordinate() function. -- -- ### Scheduled Engagements --- This example shows how to execute an engagement at a certain time. +-- -- Mission starts at 8 o'clock. +-- -- Assign two scheduled targets. +-- +-- -- Create ARTY object from Paladin group. +-- paladin=ARTY:New(GROUP:FindByName("Blue Paladin")) +-- +-- -- Assign target coordinates. Priority=50 (medium), radius=100 m, use 5 shells per engagement, engage 1 time at two past 8 o'clock. +-- paladin:AssignTargetCoord(GROUP:FindByName("Red Targets 1"):GetCoordinate(), 50, 100, 5, 1, "08:02:00", ARTY.WeaponType.Auto, "Target 1") +-- +-- -- Assign target coordinates. Priority=10 (high), radius=300 m, use 10 shells per engagement, engage 1 time at seven past 8 o'clock. +-- paladin:AssignTargetCoord(GROUP:FindByName("Red Targets 2"):GetCoordinate(), 10, 300, 10, 1, "08:07:00", ARTY.WeaponType.Auto, "Target 2") +-- +-- -- Start ARTY process. +-- paladin:Start() -- -- ### Specific Weapons -- This example demonstrates how to use specific weapons during an engagement. +-- -- Define the Normandy as ARTY object. +-- normandy=ARTY:New(GROUP:FindByName("Normandy")) +-- +-- -- Add target: prio=50, radius=300 m, number of missiles=20, number of engagements=1, start time=08:05 hours, only use cruise missiles for this attack. +-- normandy:AssignTargetCoord(GROUP:FindByName("Red Targets 1"), 20, 300, 50, 1, "08:01:00", ARTY.WeaponType.CruiseMissile) +-- +-- -- Add target: prio=50, radius=300 m, number of shells=100, number of engagements=1, start time=08:15 hours, only use cannons during this attack. +-- normandy:AssignTargetCoord(GROUP:FindByName("Red Targets 1"), 50, 300, 100, 1, "08:15:00", ARTY.WeaponType.Cannon) +-- +-- -- Define shells that are counted to check whether the ship is out of ammo. +-- -- Note that this is necessary because the Normandy has a lot of other shell type weapons which cannot be used to engage ground targets in an artillery style manner. +-- normandy:SetShellTypes({"MK45_127"}) +-- +-- -- Define missile types that are counted. +-- normandy:SetMissileTypes({"BGM"}) +-- +-- -- Start ARTY process. +-- normandy:Start() -- -- -- @field #ARTY @@ -278,7 +381,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #number version -ARTY.version="0.8.8" +ARTY.version="0.8.9" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -301,6 +404,8 @@ ARTY.version="0.8.8" -- DONE: Add command move to make arty group move. -- DONE: remove schedulers for status event. -- TODO: Improve handling of special weapons. When winchester? +-- TODO: Handle rearming for ships. +-- TODO: Make coordinate after rearming general, i.e. also work after the group has moved to anonther location. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- From 9429ec66cabe53f2af998eb6797a430b15cabc07 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 7 May 2018 17:44:53 +0200 Subject: [PATCH 088/420] RAT fixed typo --- Moose Development/Moose/Functional/RAT.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index c6c422798..8196a67f5 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -4265,10 +4265,10 @@ end function RAT:_CommandImmortal(group, switch) -- Command structure for setting groups to invisible. - local SetInvisible = {id = 'SetImmortal', params = {value = switch}} + local SetImmortal = {id = 'SetImmortal', params = {value = switch}} -- Execute command. - group:SetCommand(SetInvisible) + group:SetCommand(SetImmortal) end --- Adds a parking spot at an airport when it has been used by a spawned RAT aircraft to the RAT parking data base. From 95d7b8250d1a4f2c2437c9d8f3f897ba76df6cd9 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 7 May 2018 23:32:57 +0200 Subject: [PATCH 089/420] Minor Changes. --- Moose Development/Moose/Functional/Artillery.lua | 6 +++--- Moose Development/Moose/Wrapper/Group.lua | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 2edf7dd89..973b6ccbf 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -398,12 +398,12 @@ ARTY.version="0.8.9" -- DONE: Abort firing task if no shooting event occured with 5(?) minutes. Something went wrong then. Min/max range for example. -- DONE: Improve assigned time for engagement. Next day? -- DONE: Improve documentation. --- DONE: Add pseudo user transitions. OnAfter... +-- TODO: Add pseudo user transitions. OnAfter... -- DONE: Make reaming unit a group. --- TODO: Adjust documenation again. +-- DONE: Write documenation. -- DONE: Add command move to make arty group move. -- DONE: remove schedulers for status event. --- TODO: Improve handling of special weapons. When winchester? +-- TODO: Improve handling of special weapons. When winchester if using selected weapons? -- TODO: Handle rearming for ships. -- TODO: Make coordinate after rearming general, i.e. also work after the group has moved to anonther location. diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index db2092e9e..c595a5dbb 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -264,7 +264,6 @@ function GROUP:Destroy( GenerateEvent ) if self:IsAir() then self:CreateEventCrash( timer.getTime(), UnitData ) else - env.info("FF create event dead") self:CreateEventDead( timer.getTime(), UnitData ) end end From 0600c962825b42733d6a0b55f08f701dc19e25a9 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Tue, 8 May 2018 06:43:15 +0200 Subject: [PATCH 090/420] Finish Cargo/AI_Cargo_APC --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 424f81d9c..69bc431ee 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -511,10 +511,12 @@ function AI_CARGO_APC:onbeforeUnloaded( APC, From, Event, To, Cargo ) for _, APCUnit in pairs( APC:GetUnits() ) do local APCUnit = APCUnit -- Wrapper.Unit#UNIT local CargoCheck = self.APC_Cargo[APCUnit] - self:F( { CargoCheck:GetName(), IsUnLoaded = CargoCheck:IsUnLoaded() } ) - if CargoCheck:IsUnLoaded() == false then - AllUnloaded = false - break + if CargoCheck then + self:F( { CargoCheck:GetName(), IsUnLoaded = CargoCheck:IsUnLoaded() } ) + if CargoCheck:IsUnLoaded() == false then + AllUnloaded = false + break + end end end From a1eb0ff4d96da15ff27f5e0a2d975b26e0f7955e Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Tue, 8 May 2018 20:26:46 +0200 Subject: [PATCH 091/420] Fixes --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 69bc431ee..f867256b9 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -523,7 +523,6 @@ function AI_CARGO_APC:onbeforeUnloaded( APC, From, Event, To, Cargo ) if AllUnloaded == true then self:Guard() self.CargoCarrier = APC - self.APC_Cargo = {} end end @@ -574,6 +573,7 @@ function AI_CARGO_APC._Deploy( APC, self ) if APC:IsAlive() then self:Unload() self.Transporting = false + self.APC_Cargo = {} end end From 191fcb25beb680abb574c622ea09481cf4388c1b Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 9 May 2018 00:11:53 +0200 Subject: [PATCH 092/420] PseudoATC v0.7.0 Pseudo ATC improvements. Minor changes in RAT and Suppression. --- .../Moose/Functional/PseudoATC.lua | 407 ++++++++++++------ Moose Development/Moose/Functional/RAT.lua | 6 +- .../Moose/Functional/Suppression.lua | 46 +- 3 files changed, 320 insertions(+), 139 deletions(-) diff --git a/Moose Development/Moose/Functional/PseudoATC.lua b/Moose Development/Moose/Functional/PseudoATC.lua index d70e9d069..10b28166e 100644 --- a/Moose Development/Moose/Functional/PseudoATC.lua +++ b/Moose Development/Moose/Functional/PseudoATC.lua @@ -7,7 +7,7 @@ -- -- The pseudo ATC enhances the standard DCS ATC functions. -- --- In particular, a menu entry "Pseudo ATC" is created in the special F10 menu. +-- In particular, a menu entry "Pseudo ATC" is created in the "F10 Other..." radiomenu. -- -- ## Features -- @@ -17,18 +17,17 @@ -- * Report absolute bearing and range to nearest airports. -- * Report current altitude AGL of own aircraft. -- * Upon request, ATC reports altitude until touchdown. --- * Pressure temperature, wind data and BR for mission waypoints. +-- * Report weather (pressure temperature, wind) and BR at players mission waypoints. -- * Works with static and dynamic weather. --- * All maps supported (Caucasus, NTTR, Normandy, and all future maps). --- * Multiplayer ready (?) (I suppose yes, but I don't have a server to test or debug. Jumping from client to client works.) +-- * Player can select the unit system (metric or imperial) in which data is reported. +-- * All maps supported (Caucasus, NTTR, Normandy, Persion Gulf and all future maps). -- --- Pressure units: hPa (european aircraft), mmHg (russian aircraft), inHg (american aircraft). --- -- ==== -- -- # Demo Missions -- --- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- ### [ALL Demo Missions of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master) +-- ### [ALL Demo Missions of the latest deveopment branch](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop) -- -- ==== -- @@ -40,7 +39,7 @@ -- -- ### Author: **[funkyfranky](https://forums.eagle.ru/member.php?u=115026)** -- --- ### Contributions: **Sven van de Velde ([FlightControl](https://forums.eagle.ru/member.php?u=89536))** +-- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536)** -- -- ==== -- @module PseudoATC @@ -49,9 +48,10 @@ --- PSEUDOATC class -- @type PSEUDOATC -- @field #string ClassName Name of the Class. +-- @field #table player Table comprising each player info. -- @field #boolean Debug If true, print debug info to dcs.log file. --- @field #table player Table comprising the player info. -- @field #number mdur Duration in seconds how low messages to the player are displayed. +-- @field #number mrefresh Interval in seconds after which the F10 menu is refreshed. E.g. by the closest airports. -- @field #boolean eventsmoose If true, events are handled by MOOSE. If false, events are handled directly by DCS eventhandler. -- @extends Core.Base#BASE @@ -60,19 +60,19 @@ -- -- ## Scripting: -- --- Scripting is almost trivial. Just add the following line to your script: +-- Scripting is almost trivial. Just add the following two lines to your script: -- --- PSEUDOATC:Start() +-- pseudoATC=PSEUDOATC:New() +-- pseudoATC:Start() -- -- -- @field #PSEUDOATC PSEUDOATC={ ClassName = "PSEUDOATC", - Debug=true, player={}, - maxairport=10, + Debug=false, mdur=30, - mrefresh=120, + mrefresh=60, eventsmoose=true, } @@ -92,38 +92,93 @@ PSEUDOATC.unit={ PSEUDOATC.id="PseudoATC | " --- PSEUDOATC version. --- @field #list -PSEUDOATC.version={ - version = "0.6.0", - print = true, -} +-- @field #number version +PSEUDOATC.version="0.7.0" +----------------------------------------------------------------------------------------------------------------------------------------- + +-- TODO list +-- TODO: Add takeoff event. +-- TODO: Add user functions. + +----------------------------------------------------------------------------------------------------------------------------------------- --- PSEUDOATC contructor. Starts the PseudoATC. -- @param #PSEUDOATC self -- @return #PSEUDOATC Returns a PSEUDOATC object. -function PSEUDOATC:Start() +function PSEUDOATC:New() -- Inherit BASE. local self=BASE:Inherit(self, BASE:New()) -- #PSEUDOATC -- Debug info - self:E(PSEUDOATC.id..string.format("Creating PseudoATC object. PseudoATC version %s", PSEUDOATC.version.version)) + self:E(PSEUDOATC.id..string.format("PseudoATC version %s", PSEUDOATC.version)) + + -- Return object. + return self +end + +--- PSEUDOATC contructor. Starts the PseudoATC. +-- @param #PSEUDOATC self +-- @return #PSEUDOATC Returns a PSEUDOATC object. +function PSEUDOATC:Start() + self:F() + + -- Debug info + self:E(PSEUDOATC.id.."Starting PseudoATC") -- Handle events. if self.eventsmoose then - self:HandleEvent(EVENTS.Birth, self._OnBirth) + self:T(PSEUDOATC.id.."Events are handled by MOOSE.") + self:HandleEvent(EVENTS.Birth, self._OnBirth) + self:HandleEvent(EVENTS.Land, self._PlayerLanded) + self:HandleEvent(EVENTS.Takeoff, self._PlayerTakeOff) self:HandleEvent(EVENTS.PlayerLeaveUnit, self._PlayerLeft) - --self:HandleEvent(EVENTS.PilotDead, self._PlayerLeft) - self:HandleEvent(EVENTS.Land, self._PlayerLanded) - --self:HandleEvent(EVENTS.Takeoff, self._PlayerTakeoff) + self:HandleEvent(EVENTS.Crash, self._PlayerLeft) + self:HandleEvent(EVENTS.Ejection, self._PlayerLeft) + --self:HandleEvent(EVENTS.PilotDead, self._PlayerLeft) else + self:T(PSEUDOATC.id.."Events are handled by DCS.") -- Events are handled directly by DCS. world.addEventHandler(self) end - -- Return object. - return self +end + +----------------------------------------------------------------------------------------------------------------------------------------- +-- User Functions + +--- Debug mode on. Send messages to everone. +-- @param #PSEUDOATC self +function PSEUDOATC:DebugOn() + self.Debug=true +end + +--- Debug mode off. This is the default setting. +-- @param #PSEUDOATC self +function PSEUDOATC:DebugOff() + self.Debug=false +end + +--- Set message duration how long messages are displayed. +-- @param #PSEUDOATC self +-- @param #number duration Time in seconds. Default is 30 sec. +function PSEUDOATC:SetMessageDuration(duration) + self.mdur=duration or 30 +end + +--- Set time interval after which the F10 radio menu is refreshed. +-- @param #PSEUDOATC self +-- @param #number interval Interval in seconds. Default is every 60 sec. +function PSEUDOATC:SetMessageDuration(interval) + self.mrefresh=interval or 60 +end + +--- Enable/disable event handling by MOOSE or DCS. +-- @param #PSEUDOATC self +-- @param #boolean switch If true, events are handled by MOOSE (default). If fase, events are handled directly by DCS. +function PSEUDOATC:SetMessageDuration(switch) + self.eventsmoose=switch end ----------------------------------------------------------------------------------------------------------------------------------------- @@ -163,20 +218,23 @@ function PSEUDOATC:onEvent(Event) end -- Event info. - if self.Debug then - env.info(PSEUDOATC.id..string.format("EVENT: Event in onEvent with ID = %s", tostring(Event.id))) - env.info(PSEUDOATC.id..string.format("EVENT: Ini unit = %s" , tostring(EventData.IniUnitName))) - env.info(PSEUDOATC.id..string.format("EVENT: Ini group = %s" , tostring(EventData.IniGroupName))) - env.info(PSEUDOATC.id..string.format("EVENT: Ini player = %s" , tostring(_playername))) - env.info(PSEUDOATC.id..string.format("EVENT: Place = %s" , tostring(EventData.PlaceName))) - env.info(PSEUDOATC.id..string.format("EVENT: SubPlace = %s" , tostring(EventData.SubPlaceName))) - end + self:T3(PSEUDOATC.id..string.format("EVENT: Event in onEvent with ID = %s", tostring(Event.id))) + self:T3(PSEUDOATC.id..string.format("EVENT: Ini unit = %s" , tostring(EventData.IniUnitName))) + self:T3(PSEUDOATC.id..string.format("EVENT: Ini group = %s" , tostring(EventData.IniGroupName))) + self:T3(PSEUDOATC.id..string.format("EVENT: Ini player = %s" , tostring(_playername))) + self:T3(PSEUDOATC.id..string.format("EVENT: Place = %s" , tostring(EventData.PlaceName))) + self:T3(PSEUDOATC.id..string.format("EVENT: SubPlace = %s" , tostring(EventData.SubPlaceName))) -- Event birth. if Event.id == world.event.S_EVENT_BIRTH and _playername then self:_OnBirth(EventData) end + -- Event takeoff. + if Event.id == world.event.S_EVENT_TAKEOFF and _playername then + self:_PlayerTakeOff(EventData) + end + -- Event land. if Event.id == world.event.S_EVENT_LAND and _playername and EventData.Place then self:_PlayerLanded(EventData) @@ -186,6 +244,26 @@ function PSEUDOATC:onEvent(Event) if Event.id == world.event.S_EVENT_PLAYER_LEAVE_UNIT and _playername then self:_PlayerLeft(EventData) end + + -- Event crash ==> player left unit + if Event.id == world.event.S_EVENT_CRASH and _playername then + self:_PlayerLeft(EventData) + end + + -- Event eject ==> player left unit + if Event.id == world.event.S_EVENT_EJECTION and _playername then + self:_PlayerLeft(EventData) + end + + -- Event pilot dead ==> player left unit + if Event.id == world.event.S_EVENT_PILOT_DEAD and _playername then + self:_PlayerLeft(EventData) + end + + -- Event pilot dead ==> player left unit + if Event.id == world.event.S_EVENT_PILOT_DEAD and _playername then + self:_PlayerLeft(EventData) + end end @@ -248,6 +326,28 @@ function PSEUDOATC:_PlayerLanded(EventData) end end +--- Function called by MOOSE/DCS event handler when a player took off. +-- @param #PSEUDOATC self +-- @param Core.Event#EVENTDATA EventData +function PSEUDOATC:_PlayerTakeOff(EventData) + self:F({EventData=EventData}) + + -- Get unit, player and place. + local _unitName=EventData.IniUnitName + local _unit,_playername=self:_GetPlayerUnitAndName(_unitName) + local _base=nil + local _baseName=nil + if EventData.place then + _base=EventData.place + _baseName=EventData.place:getName() + end + + -- Call take-off function. + if _unit and _playername and _base then + self:PlayerTakeOff(_unit, _baseName) + end +end + ----------------------------------------------------------------------------------------------------------------------------------------- -- Event Functions @@ -283,15 +383,15 @@ function PSEUDOATC:PlayerEntered(unit) -- Create main F10 menu, i.e. "F10/Pseudo ATC" self.player[GID].menu_main=missionCommands.addSubMenuForGroup(GID, "Pseudo ATC") - -- Create list of nearby airports. + -- Create/update list of nearby airports. self:LocalAirports(GID) - -- Create submenu My Positon. - self:MenuAircraft(GID) - - -- Create submenu airports. + -- Create submenu of local airports. self:MenuAirports(GID) + -- Create submenu Waypoints. + self:MenuWaypoints(GID) + -- Start scheduler to refresh the F10 menues. self.player[GID].scheduler, self.player[GID].schedulerid=SCHEDULER:New(nil, self.MenuRefresh, {self, GID}, self.mrefresh, self.mrefresh) @@ -319,7 +419,7 @@ function PSEUDOATC:PlayerLanded(unit, place) MESSAGE:New(text, 30):ToAllIf(self.Debug) -- Stop altitude reporting timer if its activated. - self:AltidudeStopTimer(id) + self:AltitudeTimerStop(id) -- Welcome message. if place then @@ -329,11 +429,40 @@ function PSEUDOATC:PlayerLanded(unit, place) end +--- Function called when a player took off. +-- @param #PSEUDOATC self +-- @param Wrapper.Unit#UNIT unit Unit of player which has landed. +-- @param #string place Name of the place the player landed at. +function PSEUDOATC:PlayerTakeOff(unit, place) + self:F2({unit=unit, place=place}) + + -- Gather some information. + local group=unit:GetGroup() + local id=group:GetID() + local PlayerName=self.player[id].playername + local Callsign=self.player[id].callsign + local UnitName=self.player[id].unitname + local GroupName=self.player[id].groupname + local CallSign=self.player[id].callsign + + -- Debug message. + local text=string.format("Player %s (%s) from group %s (ID %d) took off at %s", PlayerName, UnitName, GroupName, id, place) + self:T(PSEUDOATC.id..text) + MESSAGE:New(text, 30):ToAllIf(self.Debug) + + -- Bye-Bye message. + if place then + local text=string.format("%s, %s, your are airborn. Have a save trip!", place, CallSign) + MESSAGE:New(text, self.mdur):ToGroup(group) + end + +end + --- Function called when a player leaves a unit or dies. -- @param #PSEUDOATC self -- @param Wrapper.Unit#UNIT unit Player unit which was left. function PSEUDOATC:PlayerLeft(unit) - self:F2({unit=unit}) + self:F({unit=unit}) -- Get id. local group=unit:GetGroup() @@ -348,10 +477,15 @@ function PSEUDOATC:PlayerLeft(unit) if self.player[id].schedulerid then self.player[id].scheduler:Stop(self.player[id].schedulerid) end - - -- Remove main menu. - missionCommands.removeItem(self.player[id].menu_main) + -- Stop scheduler for reporting alt if it runs. + self:AltitudeTimerStop(id) + + -- Remove main menu. + if self.player[id].menu_main then + missionCommands.removeItem(self.player[id].menu_main) + end + -- Remove player array. self.player[id]=nil end @@ -375,12 +509,13 @@ function PSEUDOATC:MenuRefresh(id) -- Create list of nearby airports. self:LocalAirports(id) - - -- Create submenu My Positon. - --self:MenuAircraft(id) - - -- Create submenu airports. + + -- Create submenu Local Airports. self:MenuAirports(id) + + -- Create submenu Waypoints etc. + self:MenuWaypoints(id) + end @@ -395,47 +530,49 @@ function PSEUDOATC:MenuClear(id) self:T(PSEUDOATC.id..text) MESSAGE:New(text,30):ToAllIf(self.Debug) - + -- Delete Airports menu. if self.player[id].menu_airports then missionCommands.removeItemForGroup(id, self.player[id].menu_airports) - --[[ - for name,item in pairs(self.player[id].menu_airports) do - - -- Debug message. - self:E(PSEUDOATC.id..string.format("Deleting menu item %s for ID %d", name, id)) - - -- Remove menu item. - missionCommands.removeItemForGroup(id, self.player[id].menu_airports[name]) - end - ]] + self.player[id].menu_airports=nil else self:T2(PSEUDOATC.id.."No airports to clear menus.") end - -- Remove - if self.player[id].menu_aircraft then - missionCommands.removeItemForGroup(id, self.player[id].menu_aircraft.main) + -- Delete waypoints menu. + if self.player[id].menu_waypoints then + missionCommands.removeItemForGroup(id, self.player[id].menu_waypoints) + self.player[id].menu_waypoints=nil end - self.player[id].menu_airports=nil - --self.player[id].menu_aircraft=nil + -- Delete report alt until touchdown menu command. + if self.player[id].menu_reportalt then + missionCommands.removeItemForGroup(id, self.player[id].menu_reportalt) + self.player[id].menu_reportalt=nil + end + + -- Delete request current alt menu command. + if self.player[id].menu_requesttalt then + missionCommands.removeItemForGroup(id, self.player[id].menu_requestalt) + self.player[id].menu_requestalt=nil + end + end ---- Create "F10/Pseudo ATC" menu items "Airport Data". +--- Create "F10/Pseudo ATC/Local Airports" menu item. -- @param #PSEUDOATC self -- @param #number id Group id of player unit for which menues are created. function PSEUDOATC:MenuAirports(id) self:F(id) -- Table for menu entries. - self.player[id].menu_airports=missionCommands.addSubMenuForGroup(id, "Airports", self.player[id].menu_main) + self.player[id].menu_airports=missionCommands.addSubMenuForGroup(id, "Local Airports", self.player[id].menu_main) local i=0 for _,airport in pairs(self.player[id].airports) do i=i+1 - if i>self.maxairport then - break -- Max X<10 airports due to 10 menu items restriction. + if i > 10 then + break -- Max 10 airports due to 10 menu items restriction. end local name=airport.name @@ -444,14 +581,9 @@ function PSEUDOATC:MenuAirports(id) --F10menu_ATC_airports[ID][name] = missionCommands.addSubMenuForGroup(ID, name, F10menu_ATC) local submenu=missionCommands.addSubMenuForGroup(id, name, self.player[id].menu_airports) - --self.player[id].menu_airports[name]=submenu -- Create menu reporting commands missionCommands.addCommandForGroup(id, "Weather Report", submenu, self.ReportWeather, self, id, pos, name) - --missionCommands.addCommandForGroup(id, "Request QFE", submenu, self.ReportPressure, self, id, "QFE", pos, name) - --missionCommands.addCommandForGroup(id, "Request QNH", submenu, self.ReportPressure, self, id, "QNH", pos, name) - --missionCommands.addCommandForGroup(id, "Request Wind", submenu, self.ReportWind, self, id, pos, name) - --missionCommands.addCommandForGroup(id, "Request Temperature", submenu, self.ReportTemperature, self, id, pos, name) missionCommands.addCommandForGroup(id, "Request BR", submenu, self.ReportBR, self, id, pos, name) -- Debug message. @@ -459,32 +591,28 @@ function PSEUDOATC:MenuAirports(id) end end ---- Create F10/Pseudo ATC menu item "My Plane". +--- Create F10/Pseudo ATC/Waypoints menu items and misc items. -- @param #PSEUDOATC self -- @param #number id Group id of player unit for which menues are created. -function PSEUDOATC:MenuAircraft(id) +function PSEUDOATC:MenuWaypoints(id) self:F(id) - -- Table for menu entries. - --self.player[id].menu_aircraft={} - + -- Player unit and callsign. local unit=self.player[id].unit --Wrapper.Unit#UNIT local callsign=self.player[id].callsign local name=string.format("My Aircraft (%s)", callsign) -- Debug info. - self:T(PSEUDOATC.id..string.format("Creating menu item %s for ID %d", name,id)) - - -- F10/PseudoATC/My Aircraft (callsign) - --self.player[id].menu_aircraft.main = missionCommands.addSubMenuForGroup(id, name, self.player[id].menu_main) - + self:T(PSEUDOATC.id..string.format("Creating waypoint menu for %s (ID %d).", name, id)) + if #self.player[id].waypoints>0 then - -- F10/PseudoATC/Waypoints + -- F10/PseudoATC/Waypoints self.player[id].menu_waypoints=missionCommands.addSubMenuForGroup(id, "Waypoints", self.player[id].menu_main) local j=0 for i, wp in pairs(self.player[id].waypoints) do + -- Increase counter j=j+1 @@ -501,23 +629,18 @@ function PSEUDOATC:MenuAircraft(id) -- Menu commands for each waypoint "F10/PseudoATC/My Aircraft (callsign)/Waypoints/Waypoint X/" missionCommands.addCommandForGroup(id, "Weather Report", submenu, self.ReportWeather, self, id, pos, name) - --missionCommands.addCommandForGroup(id, "Request QFE", submenu, self.ReportPressure, self, id, "QFE", pos, pname) - --missionCommands.addCommandForGroup(id, "Request QNH", submenu, self.ReportPressure, self, id, "QNH", pos, pname) - --missionCommands.addCommandForGroup(id, "Request Wind", submenu, self.ReportWind, self, id, pos, pname) - --missionCommands.addCommandForGroup(id, "Request Temperature", submenu, self.ReportTemperature, self, id, pos, pname) missionCommands.addCommandForGroup(id, "Request BR", submenu, self.ReportBR, self, id, pos, name) end end - missionCommands.addCommandForGroup(id, "Request current altitude AGL", self.player[id].menu_main, self.ReportHeight, self, id) - missionCommands.addCommandForGroup(id, "Report altitude until touchdown", self.player[id].menu_main, self.AltidudeStartTimer, self, id) - missionCommands.addCommandForGroup(id, "Quit reporting altitude", self.player[id].menu_main, self.AltidudeStopTimer, self, id) + self.player[id].menu_reportalt = missionCommands.addCommandForGroup(id, "Report alt until touchdown", self.player[id].menu_main, self.AltidudeTimerToggle, self, id) + self.player[id].menu_requestalt = missionCommands.addCommandForGroup(id, "Request altitude AGL", self.player[id].menu_main, self.ReportHeight, self, id) end ----------------------------------------------------------------------------------------------------------------------------------------- -- Reporting Functions ---- Weather Report. Report pressure QFE/QNH, temperature, wind at certain location +--- Weather Report. Report pressure QFE/QNH, temperature, wind at certain location. -- @param #PSEUDOATC self -- @param #number id Group id to which the report is delivered. -- @param Core.Point#COORDINATE position Coordinates at which the pressure is measured. @@ -533,7 +656,7 @@ function PSEUDOATC:ReportWeather(id, position, location) -- Get pressure in hPa. local Pqnh=position:GetPressure(0) -- Get pressure at sea level. local Pqfe=position:GetPressure() -- Get pressure at (land) height of position. - + -- Unit conversion. local _Pqnh=string.format("%.2f inHg", Pqnh * PSEUDOATC.unit.hPa2inHg) local _Pqfe=string.format("%.2f inHg", Pqfe * PSEUDOATC.unit.hPa2inHg) @@ -675,7 +798,7 @@ function PSEUDOATC:ReportWind(id, position, location) -- Formatted wind direction. local Ds = string.format('%03d°', Dir) - -- Settings. + -- Player settings. local settings=_DATABASE:GetPlayerSettings(self.player[id].playername) or _SETTINGS --Core.Settings#SETTINGS -- Velocity in player units. @@ -732,7 +855,7 @@ end -- @param #number id Group id to the report is delivered. -- @param #number dt (Optional) Duration the message is displayed. -- @param #boolean _clear (Optional) Clear previouse messages. --- @return #number Altuitude above ground. +-- @return #number Altitude above ground. function PSEUDOATC:ReportHeight(id, dt, _clear) self:F({id=id, dt=dt}) @@ -743,43 +866,67 @@ function PSEUDOATC:ReportHeight(id, dt, _clear) -- Return height [m] above ground level. local function get_AGL(p) - local vec2={x=p.x,y=p.z} - local ground=land.getHeight(vec2) - local agl=p.y-ground + local agl=0 + if p then + local vec2={x=p.x,y=p.z} + local ground=land.getHeight(vec2) + local agl=p.y-ground + end return agl end -- Get height AGL. local unit=self.player[id].unit --Wrapper.Unit#UNIT - local position=unit:GetCoordinate() - local height=get_AGL(position) - local callsign=unit:GetCallsign() - -- Settings. - local settings=_DATABASE:GetPlayerSettings(self.player[id].playername) or _SETTINGS --Core.Settings#SETTINGS + if unit and unit:IsAlive() then - local Hs=string.format("%d m", height) - if settings:IsMetric() then - Hs=string.format("%d ft", height*PSEUDOATC.unit.meter2feet) + local position=unit:GetCoordinate() + local height=get_AGL(position) + local callsign=unit:GetCallsign() + + -- Settings. + local settings=_DATABASE:GetPlayerSettings(self.player[id].playername) or _SETTINGS --Core.Settings#SETTINGS + + local Hs=string.format("%d m", height) + if settings:IsMetric() then + Hs=string.format("%d ft", height*PSEUDOATC.unit.meter2feet) + end + + -- Message text. + local _text=string.format("%s: Your altitude is %s AGL.", callsign, Hs) + + -- Send message to player group. + --MESSAGE:New(text, dt):ToGroup(self.player[id].group) + self:_DisplayMessageToGroup(self.player[id].unit,_text, dt,_clear) + + -- Return height + return height end - -- Message text. - local _text=string.format("%s: Your altitude is %s AGL.", callsign, Hs) - - -- Send message to player group. - --MESSAGE:New(text, dt):ToGroup(self.player[id].group) - self:_DisplayMessageToGroup(self.player[id].unit,_text, dt,_clear) - - -- Return height - return height + return 0 end ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Toggle report altitude reporting on/of. +-- @param #PSEUDOATC self. +-- @param #number id Group id of player unit. +function PSEUDOATC:AltidudeTimerToggle(id) + self:F(id) + + if self.player[id].altimerid then + -- If the timer is on, we turn it off. + self:AltitudeTimerStop(id) + else + -- If the timer is off, we turn it on. + self:AltitudeTimeStart(id) + end +end + --- Start altitude reporting scheduler. -- @param #PSEUDOATC self. -- @param #number id Group id of player unit. -function PSEUDOATC:AltidudeStartTimer(id) +function PSEUDOATC:AltitudeTimeStart(id) self:F(id) -- Debug info. @@ -787,13 +934,13 @@ function PSEUDOATC:AltidudeStartTimer(id) -- Start timer. --self.player[id].altimer=timer.scheduleFunction(self.ReportAltTouchdown, self, id, Tnow+2) - self.player[id].altimer, self.player[id].altimerid=SCHEDULER:New(nil, self.ReportHeight, {self, id, 0.1, true}, 1, 5) + self.player[id].altimer, self.player[id].altimerid=SCHEDULER:New(nil, self.ReportHeight, {self, id, 0.1, true}, 1, 3) end --- Stop/destroy DCS scheduler function for reporting altitude. -- @param #PSEUDOATC self. -- @param #number id Group id of player unit. -function PSEUDOATC:AltidudeStopTimer(id) +function PSEUDOATC:AltitudeTimerStop(id) -- Debug info. self:T(PSEUDOATC.id..string.format("Stopping altitude report timer for player ID %d.", id)) @@ -863,14 +1010,24 @@ function PSEUDOATC:_GetPlayerUnitAndName(_unitName) self:F(_unitName) if _unitName ~= nil then + + -- Get DCS unit from its name. local DCSunit=Unit.getByName(_unitName) - local playername=DCSunit:getPlayerName() + if DCSunit then - - if DCSunit and playername then + -- Get the player name to make sure a player entered. + local playername=DCSunit:getPlayerName() local unit=UNIT:Find(DCSunit) - return unit, playername - end + + -- Debug output. + self:T2({DCSunit=DCSunit, unit=unit, playername=playername}) + + if unit and playername then + -- Return MOOSE unit and player name + return unit, playername + end + + end end return nil,nil diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 8196a67f5..d5e78cdce 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -1280,7 +1280,7 @@ end --- Check if aircraft have accidentally been spawned on the runway. If so they will be removed immediatly. -- @param #RAT self --- @param #booblen switch If true, check is performed. If false, this check is omitted. +-- @param #boolean switch If true, check is performed. If false, this check is omitted. function RAT:CheckOnRunway(switch) self:F2(switch) if switch==nil then @@ -1291,7 +1291,7 @@ end --- Check if aircraft have accidentally been spawned on top of each other. If yes, they will be removed immediately. -- @param #RAT self --- @param #booblen switch If true, check is performed. If false, this check is omitted. +-- @param #boolean switch If true, check is performed. If false, this check is omitted. function RAT:CheckOnTop(switch) self:F2(switch) if switch==nil then @@ -1302,7 +1302,7 @@ end --- Put parking spot coordinates in a data base for future use of aircraft. -- @param #RAT self --- @param #booblen switch If true, parking spots are memorized. This is also the default setting. +-- @param #boolean switch If true, parking spots are memorized. This is also the default setting. function RAT:ParkingSpotDB(switch) self:F2(switch) if switch==nil then diff --git a/Moose Development/Moose/Functional/Suppression.lua b/Moose Development/Moose/Functional/Suppression.lua index f3a4c4954..e9f42c626 100644 --- a/Moose Development/Moose/Functional/Suppression.lua +++ b/Moose Development/Moose/Functional/Suppression.lua @@ -28,7 +28,7 @@ -- -- ### Author: **[funkyfranky](https://forums.eagle.ru/member.php?u=115026)** -- --- ### Contributions: **Sven van de Velde ([FlightControl](https://forums.eagle.ru/member.php?u=89536))** +-- ### Contributions: **[FlightControl](https://forums.eagle.ru/member.php?u=89536)** -- -- ==== -- @module Suppression @@ -45,7 +45,7 @@ -- @field #string Type Type of the group. -- @field #number SpeedMax Maximum speed of group in km/h. -- @field #boolean IsInfantry True if group has attribute Infantry. --- @field Core.Controllable#CONTROLLABLE Controllable Controllable of the FSM. Must be a ground group. +-- @field Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the FSM. Must be a ground group. -- @field #number Tsuppress_ave Average time in seconds a group gets suppressed. Actual value is sampled randomly from a Gaussian distribution. -- @field #number Tsuppress_min Minimum time in seconds the group gets suppressed. -- @field #number Tsuppress_max Maximum time in seconds the group gets suppressed. @@ -283,11 +283,16 @@ SUPPRESSION.MenuF10=nil -- @field #string id SUPPRESSION.id="SFX | " +--- PSEUDOATC version. +-- @field #number version +SUPPRESSION.version="0.7.0" + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---TODO: Figure out who was shooting and move away from him. ---TODO: Move behind a scenery building if there is one nearby. ---TODO: Retreat to a given zone or point. +--TODO list +--DONE: Figure out who was shooting and move away from him. +--DONE: Move behind a scenery building if there is one nearby. +--DONE: Retreat to a given zone or point. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -304,7 +309,7 @@ function SUPPRESSION:New(group) -- Check that group is present. if group then - self:T(SUPPRESSION.id.."Suppressive fire for group "..group:GetName()) + self:T(SUPPRESSION.id..string.format("SUPPRESSION version %s. Activating suppressive fire for group %s", SUPPRESSION.version, group:GetName())) else self:E(SUPPRESSION.id.."Suppressive fire: Requested group does not exist! (Has to be a MOOSE group.)") return nil @@ -312,7 +317,7 @@ function SUPPRESSION:New(group) -- Check that we actually have a GROUND group. if group:IsGround()==false then - self:E(SUPPRESSION.id.."SUPPRESSION fire group "..group:GetName().." has to be a GROUND group!") + self:E(SUPPRESSION.id..string.format("SUPPRESSION fire group %s has to be a GROUND group!", group:GetName())) return nil end @@ -326,7 +331,6 @@ function SUPPRESSION:New(group) -- Get max speed the group can do and convert to km/h. self.SpeedMax=self.DCSdesc.speedMaxOffRoad*3.6 - --self.SpeedMaxOffRoad=DCSdesc.speedMaxOffRoad -- Set speed to maximum. self.Speed=self.SpeedMax @@ -503,6 +507,7 @@ end -- @param #number Tmin (Optional) Minimum time [seconds] a group will be suppressed. Default is 5 seconds. -- @param #number Tmax (Optional) Maximum time a group will be suppressed. Default is 25 seconds. function SUPPRESSION:SetSuppressionTime(Tave, Tmin, Tmax) + self:F({Tave=Tave, Tmin=Tmin, Tmax=Tmax}) -- Minimum suppression time is input or default but at least 1 second. self.Tsuppress_min=Tmin or self.Tsuppress_min @@ -526,24 +531,28 @@ end -- @param #SUPPRESSION self -- @param Core.Zone#ZONE zone MOOSE zone object. function SUPPRESSION:SetRetreatZone(zone) + self:F({zone=zone}) self.RetreatZone=zone end --- Turn Debug mode on. Enables messages and more output to DCS log file. -- @param #SUPPRESSION self function SUPPRESSION:DebugOn() + self:F() self.Debug=true end --- Flare units when they are hit, die or recover from suppression. -- @param #SUPPRESSION self function SUPPRESSION:FlareOn() + self:F() self.flare=true end --- Smoke positions where units fall back to, hide or retreat. -- @param #SUPPRESSION self function SUPPRESSION:SmokeOn() + self:F() self.smoke=true end @@ -551,6 +560,7 @@ end -- @param #SUPPRESSION self -- @param #string formation Formation of the group. Default "Vee". function SUPPRESSION:SetFormation(formation) + self:F(formation) self.Formation=formation or "Vee" end @@ -558,6 +568,7 @@ end -- @param #SUPPRESSION self -- @param #number speed Speed in km/h of group. Default max speed the group can do. function SUPPRESSION:SetSpeed(speed) + self:F(speed) self.Speed=speed or self.SpeedMax self.Speed=math.min(self.Speed, self.SpeedMax) end @@ -566,6 +577,7 @@ end -- @param #SUPPRESSION self -- @param #boolean switch Enable=true or disable=false fall back of group. function SUPPRESSION:Fallback(switch) + self:F(switch) if switch==nil then switch=true end @@ -576,6 +588,7 @@ end -- @param #SUPPRESSION self -- @param #number distance Distance in meters. function SUPPRESSION:SetFallbackDistance(distance) + self:F(distance) self.FallbackDist=distance end @@ -583,6 +596,7 @@ end -- @param #SUPPRESSION self -- @param #number time Time in seconds. function SUPPRESSION:SetFallbackWait(time) + self:F(time) self.FallbackWait=time end @@ -590,6 +604,7 @@ end -- @param #SUPPRESSION self -- @param #boolean switch Enable=true or disable=false fall back of group. function SUPPRESSION:Takecover(switch) + self:F(switch) if switch==nil then switch=true end @@ -600,6 +615,7 @@ end -- @param #SUPPRESSION self -- @param #number time Time in seconds. function SUPPRESSION:SetTakecoverWait(time) + self:F(time) self.TakecoverWait=time end @@ -607,6 +623,7 @@ end -- @param #SUPPRESSION self -- @param #number range Search range in meters. function SUPPRESSION:SetTakecoverRange(range) + self:F(range) self.TakecoverRange=range end @@ -621,6 +638,7 @@ end -- @param #SUPPRESSION self -- @param #number probability Probability in percent. function SUPPRESSION:SetMinimumFleeProbability(probability) + self:F(probability) self.PminFlee=probability or 10 end @@ -628,6 +646,7 @@ end -- @param #SUPPRESSION self -- @param #number probability Probability in percent. function SUPPRESSION:SetMaximumFleeProbability(probability) + self:F(probability) self.PmaxFlee=probability or 90 end @@ -637,6 +656,7 @@ end -- @param #SUPPRESSION self -- @param #number damage Damage in percent. If group gets damaged above this value, the group will retreat. Default 50 %. function SUPPRESSION:SetRetreatDamage(damage) + self:F(damage) self.RetreatDamage=damage or 50 end @@ -644,6 +664,7 @@ end -- @param #SUPPRESSION self -- @param #number time Time in seconds. Default 7200 seconds = 2 hours. function SUPPRESSION:SetRetreatWait(time) + self:F(time) self.RetreatWait=time or 7200 end @@ -651,6 +672,7 @@ end -- @param #SUPPRESSION self -- @param #string alarmstate Alarm state. Possible "Auto", "Green", "Red". Default is "Auto". function SUPPRESSION:SetDefaultAlarmState(alarmstate) + self:F(alarmstate) if alarmstate:lower()=="auto" then self.DefaultAlarmState=SUPPRESSION.AlarmState.Auto elseif alarmstate:lower()=="green" then @@ -666,6 +688,7 @@ end -- @param #SUPPRESSION self -- @param #string roe ROE after suppression. Possible "Free", "Hold" or "Return". Default "Free". function SUPPRESSION:SetDefaultROE(roe) + self:F(roe) if roe:lower()=="free" then self.DefaultROE=SUPPRESSION.ROE.Free elseif roe:lower()=="hold" then @@ -681,6 +704,7 @@ end -- @param #SUPPRESSION self -- @param #boolean switch Enable=true or disable=false menu group. Default is true. function SUPPRESSION:MenuOn(switch) + self:F(switch) if switch==nil then switch=true end @@ -1298,7 +1322,7 @@ function SUPPRESSION:onEvent(Event) self:_OnEventHit(EventData) end - -- Event HIT + -- Event DEAD if Event.id == world.event.S_EVENT_DEAD then self:_OnEventDead(EventData) end @@ -1441,12 +1465,12 @@ end --- Make group run/drive to a certain point. We put in several intermediate waypoints because sometimes the group stops before it arrived at the desired point. --@param #SUPPRESSION self --@param Core.Point#COORDINATE fin Coordinate where we want to go. ---@param #number speed Speed of group. Default is 999. +--@param #number speed Speed of group. Default is 20. --@param #string formation Formation of group. Default is "Vee". --@param #number wait Time the group will wait/hold at final waypoint. Default is 30 seconds. function SUPPRESSION:_Run(fin, speed, formation, wait) - speed=speed or 999 + speed=speed or 20 formation=formation or "Vee" wait=wait or 30 From f9d7eea72115cf27cfa82109a9d1ce3e4105206e Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 9 May 2018 23:56:55 +0200 Subject: [PATCH 093/420] ARTY, PseudoATC, RANGE, RAT, SUPPRESSION ARTY v0.9.0: Added anti-ship missiles. Various fixes. PSEUDOATC v0.9.0: Added docu. Cleaned up code. Bug fixes. RANGE v1.1.1: Changed menu. RAT v2.2.2: Changed default setting to menu = off. Added user function to enable/disable menus. SUPPRESSION v0.9.0: Improvements. --- .../Moose/Functional/Artillery.lua | 98 +++-- .../Moose/Functional/PseudoATC.lua | 355 ++++++++---------- Moose Development/Moose/Functional/RAT.lua | 30 +- Moose Development/Moose/Functional/Range.lua | 5 +- .../Moose/Functional/Suppression.lua | 19 +- 5 files changed, 250 insertions(+), 257 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 973b6ccbf..418bb359a 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -23,19 +23,19 @@ -- -- # Demo Missions -- --- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- ### [MOOSE - ALL Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS) -- -- ==== -- -- # YouTube Channel -- --- ### [MOOSE YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl1jirWIo4t4YxqN-HxjqRkL) +-- ### [MOOSE YouTube Channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg) -- -- === -- -- ### Author: **[funkyfranky](https://forums.eagle.ru/member.php?u=115026)** -- --- ### Contributions: **[FlightControl](https://forums.eagle.ru/member.php?u=89536)** +-- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536) -- -- ==== -- @module Arty @@ -311,10 +311,10 @@ -- normandy=ARTY:New(GROUP:FindByName("Normandy")) -- -- -- Add target: prio=50, radius=300 m, number of missiles=20, number of engagements=1, start time=08:05 hours, only use cruise missiles for this attack. --- normandy:AssignTargetCoord(GROUP:FindByName("Red Targets 1"), 20, 300, 50, 1, "08:01:00", ARTY.WeaponType.CruiseMissile) +-- normandy:AssignTargetCoord(GROUP:FindByName("Red Targets 1"):GetCoordinate(), 20, 300, 50, 1, "08:01:00", ARTY.WeaponType.CruiseMissile) -- -- -- Add target: prio=50, radius=300 m, number of shells=100, number of engagements=1, start time=08:15 hours, only use cannons during this attack. --- normandy:AssignTargetCoord(GROUP:FindByName("Red Targets 1"), 50, 300, 100, 1, "08:15:00", ARTY.WeaponType.Cannon) +-- normandy:AssignTargetCoord(GROUP:FindByName("Red Targets 1"):GetCoordinate(), 50, 300, 100, 1, "08:15:00", ARTY.WeaponType.Cannon) -- -- -- Define shells that are counted to check whether the ship is out of ammo. -- -- Note that this is necessary because the Normandy has a lot of other shell type weapons which cannot be used to engage ground targets in an artillery style manner. @@ -356,9 +356,12 @@ ARTY={ RearmingArtyOnRoad=false, InitialCoord=nil, report=true, - ammoshells={"weapons.shells"}, - ammorockets={"weapons.nurs"}, - ammomissiles={"weapons.missiles"}, + --ammoshells={"weapons.shells"}, + ammoshells={}, + --ammorockets={"weapons.nurs"}, + ammorockets={}, + --ammomissiles={"weapons.missiles"}, + ammomissiles={}, Nshots=0, minrange=500, maxrange=1000000, @@ -373,6 +376,7 @@ ARTY.WeaponType={ UnguidedAny=805339120, GuidedMissile=268402688, CruiseMissile=2097152, + AntiShipMissile=65536, } --- Some ID to identify who we are in output of the DCS.log file. @@ -381,7 +385,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #number version -ARTY.version="0.8.9" +ARTY.version="0.9.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -485,7 +489,6 @@ function ARTY:New(group) -- Red branch. self:AddTransition({"CombatReady", "OutOfAmmo"}, "Rearm", "Rearming") - self:AddTransition("Rearming", "Move", "Rearming") self:AddTransition("Rearming", "Rearmed", "Rearmed") -- Green branch. @@ -503,7 +506,8 @@ function ARTY:New(group) -- Unknown transitons. To be checked if adding these causes problems. self:AddTransition("Rearming", "Arrived", "Rearming") - + self:AddTransition("Rearming", "Move", "Rearming") + return self end @@ -968,15 +972,16 @@ function ARTY:_OnEventShot(EventData) elseif self.currentTarget.weapontype==ARTY.WeaponType.UnguidedAny and _nshells+_nrockets==0 then - self:T(ARTY.id.."Unguided weapon requested but shells and rockets empty.") + self:T(ARTY.id.."Unguided weapon requested but shells AND rockets empty.") self:CeaseFire(self.currentTarget) return - elseif (self.currentTarget.weapontype==ARTY.WeaponType.CruiseMissile or self.currentTarget.weapontype==ARTY.WeaponType.CruiseMissile) and _nmissiles==0 then + elseif (self.currentTarget.weapontype==ARTY.WeaponType.GuidedMissile or self.currentTarget.weapontype==ARTY.WeaponType.CruiseMissile or self.currentTarget.weapontype==ARTY.WeaponType.AntiShipMissile) and _nmissiles==0 then - self:T(ARTY.id.."Guided or Cruise missiles requested but all missiles empty.") + self:T(ARTY.id.."Guided, anti-ship or cruise missiles requested but all missiles empty.") self:CeaseFire(self.currentTarget) return + end -- Check if number of shots reached max. @@ -1214,11 +1219,14 @@ function ARTY:onafterOpenFire(Controllable, From, Event, To, target) _type="shells or rockets" elseif self.WeaponType==ARTY.WeaponType.GuidedMissile then nfire=Nmissiles - _type="missiles" + _type="guided missiles" elseif self.WeaponType==ARTY.WeaponType.CruiseMissile then nfire=Nmissiles _type="cruise missiles" - end + elseif self.WeaponType==ARTY.WeaponType.AntiShipMissile then + nfire=Nmissiles + _type="anti-ship missiles" + end -- Adjust if less than requested ammo is left. local _n=math.min(target.nshells, nfire) @@ -1877,6 +1885,11 @@ function ARTY:GetAmmo(display) self:T2(ARTY.id..string.format("Number of weapons %d.", weapons)) self:T2({ammotable=ammotable}) + self:T(ARTY.id.."Ammotable:") + for id,bla in pairs(ammotable) do + self:T({id=id, ammo=bla}) + end + -- Loop over all weapons. for w=1,weapons do @@ -1886,28 +1899,55 @@ function ARTY:GetAmmo(display) -- Typename of current weapon local Tammo=ammotable[w]["desc"]["typeName"] + -- Get the weapon category: shell=0, missile=1, rocket=2, bomb=3 + local Category=ammotable[w].desc.category + + local MissileCategory=nil + if Category==Weapon.Category.MISSILE then + MissileCategory=ammotable[w].desc.missileCategory + end + -- Check for correct shell type. local _gotshell=false - for _,_type in pairs(self.ammoshells) do - if string.match(Tammo, _type) then + if #self.ammoshells>0 then + -- User explicitly specified the valid type(s) of shells. + for _,_type in pairs(self.ammoshells) do + if string.match(Tammo, _type) then + _gotshell=true + end + end + else + if Category==Weapon.Category.SHELL then _gotshell=true end end -- Check for correct rocket type. local _gotrocket=false - for _,_type in pairs(self.ammorockets) do - if string.match(Tammo, _type) then - _gotrocket=true + if #self.ammorockets>0 then + for _,_type in pairs(self.ammorockets) do + if string.match(Tammo, _type) then + _gotrocket=true + end end + else + if Category==Weapon.Category.ROCKET then + _gotrocket=true + end end -- Check for correct missile type. local _gotmissile=false - for _,_type in pairs(self.ammomissiles) do - if string.match(Tammo,_type) then - _gotmissile=true + if #self.ammomissiles>0 then + for _,_type in pairs(self.ammomissiles) do + if string.match(Tammo,_type) then + _gotmissile=true + end end + else + if Category==Weapon.Category.ROCKET then + _gotmissile=true + end end -- We are specifically looking for shells or rockets here. @@ -1917,7 +1957,7 @@ function ARTY:GetAmmo(display) nshells=nshells+Nammo -- Debug info. - text=text..string.format("- %d shells of type %s\n", Nammo, Tammo) + text=text..string.format("- %d shells of type %s (category=%d mc=%s)\n", Nammo, Tammo, Category, tostring(MissileCategory)) elseif _gotrocket then @@ -1925,7 +1965,7 @@ function ARTY:GetAmmo(display) nrockets=nrockets+Nammo -- Debug info. - text=text..string.format("- %d rockets of type %s\n", Nammo, Tammo) + text=text..string.format("- %d rockets of type %s (category=%d mc=%s)\n", Nammo, Tammo, Category, tostring(MissileCategory)) elseif _gotmissile then @@ -1933,12 +1973,12 @@ function ARTY:GetAmmo(display) nmissiles=nmissiles+Nammo -- Debug info. - text=text..string.format("- %d missiles of type %s\n", name, Nammo, Tammo) + text=text..string.format("- %d missiles of type %s (category=%d mc=%s)\n", Nammo, Tammo, Category, tostring(MissileCategory)) else -- Debug info. - text=text..string.format("- %d unknown ammo of type %s\n", Nammo, Tammo) + text=text..string.format("- %d unknown ammo of type %s (category=%d mc=%s)\n", Nammo, Tammo, Category, tostring(MissileCategory)) end @@ -1946,7 +1986,7 @@ function ARTY:GetAmmo(display) end -- Debug text and send message. - self:T2(ARTY.id..text) + self:T(ARTY.id..text) MESSAGE:New(text, 10):ToAllIf(display) end diff --git a/Moose Development/Moose/Functional/PseudoATC.lua b/Moose Development/Moose/Functional/PseudoATC.lua index 10b28166e..9726f1666 100644 --- a/Moose Development/Moose/Functional/PseudoATC.lua +++ b/Moose Development/Moose/Functional/PseudoATC.lua @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- **Functional** - Pseudo ATC. +--- **Functional** - (R2.4) Rudimentary ATC. -- -- ![Banner Image](..\Presentations\PSEUDOATC\PSEUDOATC_Main.jpg) -- @@ -11,35 +11,31 @@ -- -- ## Features -- --- * Report QFE or QNH pressures at nearby airbases. --- * Report wind direction and strength at airbases. --- * Report temperature at airbases. --- * Report absolute bearing and range to nearest airports. +-- * Weather report at nearby airbases and mission waypoints. +-- * Report absolute bearing and range to nearest airports and mission waypoints. -- * Report current altitude AGL of own aircraft. -- * Upon request, ATC reports altitude until touchdown. --- * Report weather (pressure temperature, wind) and BR at players mission waypoints. -- * Works with static and dynamic weather. --- * Player can select the unit system (metric or imperial) in which data is reported. --- * All maps supported (Caucasus, NTTR, Normandy, Persion Gulf and all future maps). +-- * Player can select the unit system (metric or imperial) in which information is reported. +-- * All maps supported (Caucasus, NTTR, Normandy, Persian Gulf and all future maps). -- -- ==== -- -- # Demo Missions -- --- ### [ALL Demo Missions of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master) --- ### [ALL Demo Missions of the latest deveopment branch](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop) +-- ### [MOOSE - ALL Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS) -- -- ==== -- -- # YouTube Channel -- --- ### [MOOSE YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl1jirWIo4t4YxqN-HxjqRkL) +-- ### [MOOSE YouTube Channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg) -- -- === -- -- ### Author: **[funkyfranky](https://forums.eagle.ru/member.php?u=115026)** -- --- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536)** +-- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536) -- -- ==== -- @module PseudoATC @@ -51,14 +47,37 @@ -- @field #table player Table comprising each player info. -- @field #boolean Debug If true, print debug info to dcs.log file. -- @field #number mdur Duration in seconds how low messages to the player are displayed. --- @field #number mrefresh Interval in seconds after which the F10 menu is refreshed. E.g. by the closest airports. +-- @field #number mrefresh Interval in seconds after which the F10 menu is refreshed. E.g. by the closest airports. Default is 120 sec. +-- @field #number talt Interval in seconds between reporting altitude until touchdown. Default 3 sec. +-- @field #boolean chatty Display some messages on events like take-off and touchdown. -- @field #boolean eventsmoose If true, events are handled by MOOSE. If false, events are handled directly by DCS eventhandler. -- @extends Core.Base#BASE ---# PSEUDOATC class, extends @{Base#BASE} -- The PSEUDOATC class adds some rudimentary ATC functionality via the radio menu. -- --- ## Scripting: +-- Local weather reports can be requested for nearby airports and player's mission waypoints. +-- The weather report includes +-- +-- * QFE and QNH pressures, +-- * Temperature, +-- * Wind direction and strength. +-- +-- The list of airports is updated every 60 seconds. This interval can be adjusted by the function @{#PSEUDOATC.SetMenuRefresh}(*interval*). +-- +-- Likewise, absolute bearing and range to the close by airports and mission waypoints can be requested. +-- +-- The player can switch the unit system in which all information is displayed during the mission with the MOOSE settings radio menu. +-- The unit system can be set to either imperial or metric. Altitudes are reported in feet or meter, distances in kilometers or nautical miles, +-- temperatures in degrees Fahrenheit or Celsius and QFE/QNH pressues in inHg or mmHg. +-- Note that the pressures are also reported in hPa independent of the unit system setting. +-- +-- In bad weather conditions, the ATC can "talk you down", i.e. will continuously report your altitude on the final approach. +-- Default reporting time interval is 3 seconds. This can be adjusted via the @{#PSEUDOATC.SetReportAltInterval}(*interval*) function. +-- The reporting stops automatically when the player lands or can be stopped manually by clicking on the radio menu item again. +-- So the radio menu item acts as a toggle to switch the reporting on and off. +-- +-- ## Scripting -- -- Scripting is almost trivial. Just add the following two lines to your script: -- @@ -72,38 +91,31 @@ PSEUDOATC={ player={}, Debug=false, mdur=30, - mrefresh=60, + mrefresh=120, + talt=3, + chatty=true, eventsmoose=true, } ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- PSEUDOATC unit conversions. --- @list unit -PSEUDOATC.unit={ - hPa2inHg=0.0295299830714, - hPa2mmHg=0.7500615613030, - meter2feet=3.28084, - km2nm=0.539957, -} - --- Some ID to identify who we are in output of the DCS.log file. -- @field #string id PSEUDOATC.id="PseudoATC | " --- PSEUDOATC version. -- @field #number version -PSEUDOATC.version="0.7.0" +PSEUDOATC.version="0.9.0" ----------------------------------------------------------------------------------------------------------------------------------------- -- TODO list --- TODO: Add takeoff event. --- TODO: Add user functions. +-- DONE: Add takeoff event. +-- DONE: Add user functions. ----------------------------------------------------------------------------------------------------------------------------------------- ---- PSEUDOATC contructor. Starts the PseudoATC. +--- PSEUDOATC contructor. -- @param #PSEUDOATC self -- @return #PSEUDOATC Returns a PSEUDOATC object. function PSEUDOATC:New() @@ -118,9 +130,8 @@ function PSEUDOATC:New() return self end ---- PSEUDOATC contructor. Starts the PseudoATC. +--- Starts the PseudoATC event handlers. -- @param #PSEUDOATC self --- @return #PSEUDOATC Returns a PSEUDOATC object. function PSEUDOATC:Start() self:F() @@ -160,7 +171,19 @@ function PSEUDOATC:DebugOff() self.Debug=false end ---- Set message duration how long messages are displayed. +--- Chatty mode on. Display some messages on take-off and touchdown. +-- @param #PSEUDOATC self +function PSEUDOATC:ChattyOn() + self.chatty=true +end + +--- Chatty mode off. Don't display some messages on take-off and touchdown. +-- @param #PSEUDOATC self +function PSEUDOATC:ChattyOff() + self.chatty=false +end + +--- Set duration how long messages are displayed. -- @param #PSEUDOATC self -- @param #number duration Time in seconds. Default is 30 sec. function PSEUDOATC:SetMessageDuration(duration) @@ -169,18 +192,25 @@ end --- Set time interval after which the F10 radio menu is refreshed. -- @param #PSEUDOATC self --- @param #number interval Interval in seconds. Default is every 60 sec. -function PSEUDOATC:SetMessageDuration(interval) - self.mrefresh=interval or 60 +-- @param #number interval Interval in seconds. Default is every 120 sec. +function PSEUDOATC:SetMenuRefresh(interval) + self.mrefresh=interval or 120 end --- Enable/disable event handling by MOOSE or DCS. -- @param #PSEUDOATC self --- @param #boolean switch If true, events are handled by MOOSE (default). If fase, events are handled directly by DCS. -function PSEUDOATC:SetMessageDuration(switch) +-- @param #boolean switch If true, events are handled by MOOSE (default). If false, events are handled directly by DCS. +function PSEUDOATC:SetEventsMoose(switch) self.eventsmoose=switch end +--- Set time interval for reporting altitude until touchdown. +-- @param #PSEUDOATC self +-- @param #number interval Interval in seconds. Default is every 3 sec. +function PSEUDOATC:SetReportAltInterval(interval) + self.talt=interval or 3 +end + ----------------------------------------------------------------------------------------------------------------------------------------- -- Event Handling @@ -231,7 +261,7 @@ function PSEUDOATC:onEvent(Event) end -- Event takeoff. - if Event.id == world.event.S_EVENT_TAKEOFF and _playername then + if Event.id == world.event.S_EVENT_TAKEOFF and _playername and EventData.Place then self:_PlayerTakeOff(EventData) end @@ -259,12 +289,7 @@ function PSEUDOATC:onEvent(Event) if Event.id == world.event.S_EVENT_PILOT_DEAD and _playername then self:_PlayerLeft(EventData) end - - -- Event pilot dead ==> player left unit - if Event.id == world.event.S_EVENT_PILOT_DEAD and _playername then - self:_PlayerLeft(EventData) - end - + end --- Function called my MOOSE event handler when a player enters a unit. @@ -376,7 +401,7 @@ function PSEUDOATC:PlayerEntered(unit) self.player[GID].waypoints=group:GetTaskRoute() -- Info message. - local text=string.format("Player %s entered unit %s of group %s. ID = %d", PlayerName, UnitName, GroupName, GID) + local text=string.format("Player %s entered unit %s of group %s (id=%d).", PlayerName, UnitName, GroupName, GID) self:T(PSEUDOATC.id..text) MESSAGE:New(text, 30):ToAllIf(self.Debug) @@ -414,7 +439,7 @@ function PSEUDOATC:PlayerLanded(unit, place) local CallSign=self.player[id].callsign -- Debug message. - local text=string.format("Player %s (%s) from group %s (ID %d) landed at %s", PlayerName, UnitName, GroupName, id, place) + local text=string.format("Player %s in unit %s of group %s (id=%d) landed at %s.", PlayerName, UnitName, GroupName, id, place) self:T(PSEUDOATC.id..text) MESSAGE:New(text, 30):ToAllIf(self.Debug) @@ -422,7 +447,7 @@ function PSEUDOATC:PlayerLanded(unit, place) self:AltitudeTimerStop(id) -- Welcome message. - if place then + if place and self.chatty then local text=string.format("Touchdown! Welcome to %s. Have a nice day!", place) MESSAGE:New(text, self.mdur):ToGroup(group) end @@ -446,13 +471,13 @@ function PSEUDOATC:PlayerTakeOff(unit, place) local CallSign=self.player[id].callsign -- Debug message. - local text=string.format("Player %s (%s) from group %s (ID %d) took off at %s", PlayerName, UnitName, GroupName, id, place) + local text=string.format("Player %s in unit %s of group %s (id=%d) took off at %s.", PlayerName, UnitName, GroupName, id, place) self:T(PSEUDOATC.id..text) MESSAGE:New(text, 30):ToAllIf(self.Debug) -- Bye-Bye message. - if place then - local text=string.format("%s, %s, your are airborn. Have a save trip!", place, CallSign) + if place and self.chatty then + local text=string.format("%s, %s, you are airborn. Have a save trip!", place, CallSign) MESSAGE:New(text, self.mdur):ToGroup(group) end @@ -468,26 +493,30 @@ function PSEUDOATC:PlayerLeft(unit) local group=unit:GetGroup() local id=group:GetID() - -- Debug message. - local text=string.format("Player %s (%s) callsign %s of group %s just left.", self.player[id].playername, self.player[id].unitname, self.player[id].callsign, self.player[id].groupname) - self:T(PSEUDOATC.id..text) - MESSAGE:New(text, 30):ToAllIf(self.Debug) + if self.player[id] then - -- Stop scheduler for menu updates - if self.player[id].schedulerid then - self.player[id].scheduler:Stop(self.player[id].schedulerid) + -- Debug message. + local text=string.format("Player %s (callsign %s) of group %s just left unit %s.", self.player[id].playername, self.player[id].callsign, self.player[id].groupname, self.player[id].unitname) + self:T(PSEUDOATC.id..text) + MESSAGE:New(text, 30):ToAllIf(self.Debug) + + -- Stop scheduler for menu updates + if self.player[id].schedulerid then + self.player[id].scheduler:Stop(self.player[id].schedulerid) + end + + -- Stop scheduler for reporting alt if it runs. + self:AltitudeTimerStop(id) + + -- Remove main menu. + if self.player[id].menu_main then + missionCommands.removeItem(self.player[id].menu_main) + end + + -- Remove player array. + self.player[id]=nil + end - - -- Stop scheduler for reporting alt if it runs. - self:AltitudeTimerStop(id) - - -- Remove main menu. - if self.player[id].menu_main then - missionCommands.removeItem(self.player[id].menu_main) - end - - -- Remove player array. - self.player[id]=nil end ----------------------------------------------------------------------------------------------------------------------------------------- @@ -551,14 +580,14 @@ function PSEUDOATC:MenuClear(id) end -- Delete request current alt menu command. - if self.player[id].menu_requesttalt then + if self.player[id].menu_requestalt then missionCommands.removeItemForGroup(id, self.player[id].menu_requestalt) self.player[id].menu_requestalt=nil end end ---- Create "F10/Pseudo ATC/Local Airports" menu item. +--- Create "F10/Pseudo ATC/Local Airports/Airport Name/" menu items each containing weather report and BR request. -- @param #PSEUDOATC self -- @param #number id Group id of player unit for which menues are created. function PSEUDOATC:MenuAirports(id) @@ -591,7 +620,7 @@ function PSEUDOATC:MenuAirports(id) end end ---- Create F10/Pseudo ATC/Waypoints menu items and misc items. +--- Create "F10/Pseudo ATC/Waypoints/ menu items. -- @param #PSEUDOATC self -- @param #number id Group id of player unit for which menues are created. function PSEUDOATC:MenuWaypoints(id) @@ -600,10 +629,9 @@ function PSEUDOATC:MenuWaypoints(id) -- Player unit and callsign. local unit=self.player[id].unit --Wrapper.Unit#UNIT local callsign=self.player[id].callsign - local name=string.format("My Aircraft (%s)", callsign) -- Debug info. - self:T(PSEUDOATC.id..string.format("Creating waypoint menu for %s (ID %d).", name, id)) + self:T(PSEUDOATC.id..string.format("Creating waypoint menu for %s (ID %d).", callsign, id)) if #self.player[id].waypoints>0 then @@ -633,8 +661,8 @@ function PSEUDOATC:MenuWaypoints(id) end end - self.player[id].menu_reportalt = missionCommands.addCommandForGroup(id, "Report alt until touchdown", self.player[id].menu_main, self.AltidudeTimerToggle, self, id) - self.player[id].menu_requestalt = missionCommands.addCommandForGroup(id, "Request altitude AGL", self.player[id].menu_main, self.ReportHeight, self, id) + self.player[id].menu_reportalt = missionCommands.addCommandForGroup(id, "Talk me down", self.player[id].menu_main, self.AltidudeTimerToggle, self, id) + self.player[id].menu_requestalt = missionCommands.addCommandForGroup(id, "Request altitude", self.player[id].menu_main, self.ReportHeight, self, id) end ----------------------------------------------------------------------------------------------------------------------------------------- @@ -657,28 +685,27 @@ function PSEUDOATC:ReportWeather(id, position, location) local Pqnh=position:GetPressure(0) -- Get pressure at sea level. local Pqfe=position:GetPressure() -- Get pressure at (land) height of position. + -- Pressure conversion + local hPa2inHg=0.0295299830714 + local hPa2mmHg=0.7500615613030 + -- Unit conversion. - local _Pqnh=string.format("%.2f inHg", Pqnh * PSEUDOATC.unit.hPa2inHg) - local _Pqfe=string.format("%.2f inHg", Pqfe * PSEUDOATC.unit.hPa2inHg) + local _Pqnh=string.format("%.2f inHg", Pqnh * hPa2inHg) + local _Pqfe=string.format("%.2f inHg", Pqfe * hPa2inHg) if settings:IsMetric() then - _Pqnh=string.format("%.1f mmHg", Pqnh * PSEUDOATC.unit.hPa2mmHg) - _Pqfe=string.format("%.1f mmHg", Pqfe * PSEUDOATC.unit.hPa2mmHg) + _Pqnh=string.format("%.1f mmHg", Pqnh * hPa2mmHg) + _Pqfe=string.format("%.1f mmHg", Pqfe * hPa2mmHg) end -- Message text. text=text..string.format("QFE %.1f hPa = %s.\n", Pqfe, _Pqfe) text=text..string.format("QNH %.1f hPa = %s.\n", Pqnh, _Pqnh) - --- convert celsius to fahrenheit - local function celsius2fahrenheit(degC) - return degC*1.8+32 - end - -- Get temperature at position in degrees Celsius. local T=position:GetTemperature() -- Correct unit system. - local _T=string.format('%d°F', celsius2fahrenheit(T)) + local _T=string.format('%d°F', UTILS.CelciusToFarenheit(T)) if settings:IsMetric() then _T=string.format('%d°C', T) end @@ -696,9 +723,9 @@ function PSEUDOATC:ReportWeather(id, position, location) local Ds = string.format('%03d°', Dir) -- Velocity in player units. - local Vs=string.format('%.1f m/s', Vel) - if settings:IsImperial() then - Vs=string.format("%.1f knots", Vel*1.94384) + local Vs=string.format("%.1f knots", UTILS.MpsToKnots(Vel)) + if settings:IsMetric() then + Vs=string.format('%.1f m/s', Vel) end -- Message text. @@ -709,111 +736,6 @@ function PSEUDOATC:ReportWeather(id, position, location) end ---- Report pressure. --- @param #PSEUDOATC self --- @param #number id Group id to which the report is delivered. --- @param #string Qcode Can be "QNH" for pressure at sea level or "QFE" for pressure at field elevation. Default is QFE or more precisely pressure at position. --- @param Core.Point#COORDINATE position Coordinates at which the pressure is measured. --- @param #string location Name of the location at which the pressure is measured. -function PSEUDOATC:ReportPressure(id, Qcode, position, location) - self:F({id=id, Qcode=Qcode, position=position, location=location}) - - -- Get pressure in hPa. - local P - if Qcode=="QNH" then - P=position:GetPressure(0) -- Get pressure at sea level. - else - P=position:GetPressure() -- Get pressure at (land) height of position. - end - - -- Settings. - local settings=_DATABASE:GetPlayerSettings(self.player[id].playername) or _SETTINGS --Core.Settings#SETTINGS - - -- Unit conversion. - local P_inHg=P * PSEUDOATC.unit.hPa2inHg - local P_mmHg=P * PSEUDOATC.unit.hPa2mmHg - - local P_set=string.format("%.2f inHg", P_inHg) - if settings:IsMetric() then - P_set=string.format("%.1f mmHg", P_mmHg) - end - - -- Message text. - local text=string.format("%s at %s: P = %.1f hPa = %s.", Qcode, location, P, P_set) - - -- Send message. - MESSAGE:New(text, self.mdur):ToGroup(self.player[id].group) -end - ---- Report temperature. --- @param #PSEUDOATC self --- @param #number id Group id to the report is delivered. --- @param Core.Point#COORDINATE position Coordinates at which the pressure is measured. --- @param #string location Name of the location at which the pressure is measured. -function PSEUDOATC:ReportTemperature(id, position, location) - self:F({id=id, position=position, location=location}) - - --- convert celsius to fahrenheit - local function celsius2fahrenheit(degC) - return degC*1.8+32 - end - - -- Get temperature at position in degrees Celsius. - local T=position:GetTemperature() - - -- Formatted temperature in Celsius and Fahrenheit. - local Tc=string.format('%d°C', T) - local Tf=string.format('%d°F', celsius2fahrenheit(T)) - - -- Settings. - local settings=_DATABASE:GetPlayerSettings(self.player[id].playername) or _SETTINGS --Core.Settings#SETTINGS - - -- Correct unit system. - local _T=string.format('%d°F', celsius2fahrenheit(T)) - if settings:IsMetric() then - _T=string.format('%d°C', T) - end - - -- Message text. - local text=string.format("Temperature at %s is %s", location, _T) - - -- Send message to player group. - MESSAGE:New(text, self.mdur):ToGroup(self.player[id].group) -end - ---- Report wind direction and strength. --- @param #PSEUDOATC self --- @param #number id Group id to the report is delivered. --- @param Core.Point#COORDINATE position Coordinates at which the pressure is measured. --- @param #string location Name of the location at which the pressure is measured. -function PSEUDOATC:ReportWind(id, position, location) - self:F({id=id, position=position, location=location}) - - -- Get wind direction and speed. - local Dir,Vel=position:GetWind() - - -- Get Beaufort wind scale. - local Bn,Bd=UTILS.BeaufortScale(Vel) - - -- Formatted wind direction. - local Ds = string.format('%03d°', Dir) - - -- Player settings. - local settings=_DATABASE:GetPlayerSettings(self.player[id].playername) or _SETTINGS --Core.Settings#SETTINGS - - -- Velocity in player units. - local Vs=string.format('%.1f m/s', Vel) - if settings:IsImperial() then - Vs=string.format("%.1f knots", Vel*1.94384) - end - - -- Message text. - local text=string.format("%s: Wind from %s at %s (%s).", location, Ds, Vs, Bd) - - -- Send message to player group. - MESSAGE:New(text, self.mdur):ToGroup(self.player[id].group) -end - --- Report absolute bearing and range form player unit to airport. -- @param #PSEUDOATC self -- @param #number id Group id to the report is delivered. @@ -838,9 +760,10 @@ function PSEUDOATC:ReportBR(id, position, location) -- Settings. local settings=_DATABASE:GetPlayerSettings(self.player[id].playername) or _SETTINGS --Core.Settings#SETTINGS - local Rs=string.format("%.1f km", range/1000) - if settings:IsImperial() then - Rs=string.format("%.1f NM", range/1000 * PSEUDOATC.unit.km2nm) + + local Rs=string.format("%.1f NM", UTILS.MetersToNM(range)) + if settings:IsMetric() then + Rs=string.format("%.1f km", range/1000) end -- Message text. @@ -867,11 +790,9 @@ function PSEUDOATC:ReportHeight(id, dt, _clear) -- Return height [m] above ground level. local function get_AGL(p) local agl=0 - if p then - local vec2={x=p.x,y=p.z} - local ground=land.getHeight(vec2) - local agl=p.y-ground - end + local vec2={x=p.x,y=p.z} + local ground=land.getHeight(vec2) + local agl=p.y-ground return agl end @@ -887,16 +808,23 @@ function PSEUDOATC:ReportHeight(id, dt, _clear) -- Settings. local settings=_DATABASE:GetPlayerSettings(self.player[id].playername) or _SETTINGS --Core.Settings#SETTINGS - local Hs=string.format("%d m", height) + env.info("FF height = "..height) + + -- Height string. + local Hs=string.format("%d ft", UTILS.MetersToFeet(height)) if settings:IsMetric() then - Hs=string.format("%d ft", height*PSEUDOATC.unit.meter2feet) + Hs=string.format("%d m", height) end -- Message text. - local _text=string.format("%s: Your altitude is %s AGL.", callsign, Hs) + local _text=string.format("%s, your altitude is %s AGL.", callsign, Hs) + + -- Append flight level. + if _clear==false then + _text=_text..string.format(" FL%03d.", position.y/30.48) + end -- Send message to player group. - --MESSAGE:New(text, dt):ToGroup(self.player[id].group) self:_DisplayMessageToGroup(self.player[id].unit,_text, dt,_clear) -- Return height @@ -908,7 +836,7 @@ end ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Toggle report altitude reporting on/of. +--- Toggle report altitude reporting on/off. -- @param #PSEUDOATC self. -- @param #number id Group id of player unit. function PSEUDOATC:AltidudeTimerToggle(id) @@ -932,8 +860,7 @@ function PSEUDOATC:AltitudeTimeStart(id) -- Debug info. self:T(PSEUDOATC.id..string.format("Starting altitude report timer for player ID %d.", id)) - -- Start timer. - --self.player[id].altimer=timer.scheduleFunction(self.ReportAltTouchdown, self, id, Tnow+2) + -- Start timer. Altitude is reported every ~3 seconds. self.player[id].altimer, self.player[id].altimerid=SCHEDULER:New(nil, self.ReportHeight, {self, id, 0.1, true}, 1, 3) end @@ -946,7 +873,6 @@ function PSEUDOATC:AltitudeTimerStop(id) self:T(PSEUDOATC.id..string.format("Stopping altitude report timer for player ID %d.", id)) -- Stop timer. - --timer.removeFunction(self.player[id].alttimer) if self.player[id].altimerid then self.player[id].altimer:Stop(self.player[id].altimerid) end @@ -1061,3 +987,16 @@ function PSEUDOATC:_DisplayMessageToGroup(_unit, _text, _time, _clear) end +--- Returns a string which consits of this callsign and the player name. +-- @param #RANGE self +-- @param #string unitname Name of the player unit. +function PSEUDOATC:_myname(unitname) + self:F2(unitname) + + local unit=UNIT:FindByName(unitname) + local pname=unit:GetPlayerName() + local csign=unit:GetCallsign() + + return string.format("%s (%s)", csign, pname) +end + diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index d5e78cdce..91f42eb48 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -42,20 +42,21 @@ -- -- # Demo Missions -- --- ### [RAT Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/Release/RAT%20-%20Random%20Air%20Traffic) --- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- ### [MOOSE - ALL Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS) +-- ### [MOOSE - RAT Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/RAT%20-%20Random%20Air%20Traffic) -- -- === -- -- # YouTube Channel -- --- ### [DCS WORLD - MOOSE - RAT - Random Air Traffic](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl0u4Zxywtg-mx_ov4vi68CO) +-- ### [MOOSE YouTube Channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg) +-- ### [MOOSE - RAT - Random Air Traffic](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl0u4Zxywtg-mx_ov4vi68CO) -- -- === -- -- ### Author: **[funkyfranky](https://forums.eagle.ru/member.php?u=115026)** -- --- ### Contributions: **Sven van de Velde ([FlightControl](https://forums.eagle.ru/member.php?u=89536))** +-- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536) -- -- === -- @module Rat @@ -116,7 +117,7 @@ -- @field #boolean continuejourney Aircraft will continue their journey, i.e. get respawned at their destination with a new random destination. -- @field #number ngroups Number of groups to be spawned in total. -- @field #number alive Number of groups which are alive. --- @field #boolean f10menu Add an F10 menu for RAT. +-- @field #boolean f10menu If true, add an F10 radiomenu for RAT. Default is false. -- @field #table Menu F10 menu items for this RAT object. -- @field #string SubMenuName Submenu name for RAT object. -- @field #boolean respawn_at_landing Respawn aircraft the moment they land rather than at engine shutdown. @@ -350,7 +351,7 @@ RAT={ continuejourney=false, -- Aircraft will continue their journey, i.e. get respawned at their destination with a new random destination. alive=0, -- Number of groups which are alive. ngroups=nil, -- Number of groups to be spawned in total. - f10menu=true, -- Add an F10 menu for RAT. + f10menu=false, -- Add an F10 menu for RAT. Menu={}, -- F10 menu items for this RAT object. SubMenuName=nil, -- Submenu name for RAT object. respawn_at_landing=false, -- Respawn aircraft the moment they land rather than at engine shutdown. @@ -506,7 +507,7 @@ RAT.id="RAT | " --- RAT version. -- @list version RAT.version={ - version = "2.2.1", + version = "2.2.2", print = true, } @@ -1363,6 +1364,21 @@ function RAT:Immortal() self.immortal=true end +--- Radio menu On. Default is off. +-- @param #RAT self +function RAT:RadioMenuON() + self:F2() + self.f10menu=true +end + +--- Radio menu Off. This is the default setting. +-- @param #RAT self +function RAT:RadioMenuOFF() + self:F2() + self.f10menu=false +end + + --- Activate uncontrolled aircraft. -- @param #RAT self -- @param #number maxactivated Maximal numnber of activated aircraft. Absolute maximum will be the number of spawned groups. Default is 1. diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index bf8cbb193..bce0b8ef1 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -32,12 +32,13 @@ -- -- # Demo Missions -- --- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- ### [MOOSE - ALL Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS) -- -- === -- -- # YouTube Channel --- +-- +-- ### [MOOSE YouTube Channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg) -- ### [MOOSE - On the Range - Demonstration Video](https://www.youtube.com/watch?v=kIXcxNB9_3M) -- -- === diff --git a/Moose Development/Moose/Functional/Suppression.lua b/Moose Development/Moose/Functional/Suppression.lua index e9f42c626..8eb913930 100644 --- a/Moose Development/Moose/Functional/Suppression.lua +++ b/Moose Development/Moose/Functional/Suppression.lua @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- **Functional** - Suppress fire of ground units when they get hit. +--- **Functional** - (R2.4) Suppress fire of ground units when they get hit. -- -- ![Banner Image](..\Presentations\SUPPRESSION\Suppression_Main.png) -- @@ -16,19 +16,19 @@ -- -- # Demo Missions -- --- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases) +-- ### [MOOSE - ALL Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS) -- -- ==== -- -- # YouTube Channel -- --- ### [MOOSE YouTube Channel](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl1jirWIo4t4YxqN-HxjqRkL) +-- ### [MOOSE YouTube Channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg) -- -- === -- -- ### Author: **[funkyfranky](https://forums.eagle.ru/member.php?u=115026)** -- --- ### Contributions: **[FlightControl](https://forums.eagle.ru/member.php?u=89536)** +-- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536) -- -- ==== -- @module Suppression @@ -194,10 +194,6 @@ -- -- ![Process](..\Presentations\SUPPRESSION\Suppression_Example_01.png) -- --- ## Suppression and Rescure --- This example shows how the event **Retreat** can be captured. Here, a transport is started which picks up the wounded troups and drives them to a safe zone. --- --- ![Process](..\Presentations\SUPPRESSION\Suppression_Rescue.png) -- -- # Customization and Fine Tuning -- The following user functions can be used to change the default values @@ -281,11 +277,11 @@ SUPPRESSION.MenuF10=nil --- Some ID to identify who we are in output of the DCS.log file. -- @field #string id -SUPPRESSION.id="SFX | " +SUPPRESSION.id="SUPPRESSION | " --- PSEUDOATC version. -- @field #number version -SUPPRESSION.version="0.7.0" +SUPPRESSION.version="0.9.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1333,6 +1329,7 @@ end -- @param #SUPPRESSION self -- @param Core.Event#EVENTDATA EventData function SUPPRESSION:_OnEventHit(EventData) + self:F(EventData) local GroupNameSelf=self.Controllable:GetName() local GroupNameTgt=EventData.TgtGroupName @@ -1343,7 +1340,7 @@ function SUPPRESSION:_OnEventHit(EventData) -- Check that correct group was hit. if GroupNameTgt == GroupNameSelf then - self:T2(SUPPRESSION.id..string.format("Hit event at t = %5.1f", timer.getTime())) + self:T(SUPPRESSION.id..string.format("Hit event at t = %5.1f", timer.getTime())) -- Flare unit that was hit. if self.flare or self.Debug then From 9ee21f80ac2c5a216ff8e6e7c9026edf8e2eccac Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Thu, 10 May 2018 08:07:28 +0200 Subject: [PATCH 094/420] comments --- Moose Development/Moose/Cargo/CargoGroup.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index c4712c10e..1bdcbf263 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -31,6 +31,20 @@ do -- CARGO_GROUP -- -- The CARGO\_GROUP class defines a cargo that is represented by a @{Group} object within the simulator. -- The cargo can be Loaded, UnLoaded, Boarded, UnBoarded to and from Carriers. + -- + -- The above cargo classes are used by the AI\_CARGO\_ classes to allow AI groups to transport cargo: + -- + -- * AI Armoured Personnel Carriers to transport cargo and engage in battles, using the @{AI.AI_Cargo_APC#AI_CARGO_APC} class. + -- * AI Helicopters to transport cargo, using the @{AI.AI_Cargo_Helicopter#AI_CARGO_HELICOPTER} class. + -- * AI Planes to transport cargo, using the @{AI.AI_Cargo_Plane#AI_CARGO_PLANE} class. + -- * AI Ships is planned. + -- + -- The above cargo classes are also used by the TASK\_CARGO\_ classes to allow human players to transport cargo as part of a tasking: + -- + -- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT} to transport cargo by human players. + -- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_CSAR} to transport downed pilots by human players. + -- + -- The -- -- @field #CARGO_GROUP CARGO_GROUP -- From 1159d11a12c8cd8d02e4de0d39ef07717be43d6a Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Thu, 10 May 2018 14:39:22 +0200 Subject: [PATCH 095/420] New AI_CARGO_DISPATCHER_APC --- .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 158 ++++++++++ Moose Development/Moose/Core/Database.lua | 6 +- Moose Development/Moose/Core/Set.lua | 272 ++++++++++++++++++ Moose Setup/Moose.files | 1 + 4 files changed, 435 insertions(+), 2 deletions(-) create mode 100644 Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua new file mode 100644 index 000000000..d9c93e936 --- /dev/null +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -0,0 +1,158 @@ +--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo. +-- +-- === +-- +-- ### Author: **FlightControl** +-- +-- === +-- +-- @module AI_Cargo_Dispatcher + +--- @type AI_CARGO_DISPATCHER_APC +-- @extends Core.Fsm#FSM_CONTROLLABLE + + +--- # AI\_CARGO\_DISPATCHER\_APC class, extends @{Core.Base#BASE} +-- +-- === +-- +-- AI\_CARGO\_DISPATCHER\_APC brings a dynamic cargo handling capability for AI groups. +-- +-- Armoured Personnel APCs (APC), Trucks, Jeeps and other carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. +-- The AI\_CARGO\_DISPATCHER\_APC module uses the @{Cargo} capabilities within the MOOSE framework. +-- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER\_APC object recognize the cargo. +-- Please consult the @{Cargo} module for more information. +-- +-- +-- +-- @field #AI_CARGO_DISPATCHER_APC +AI_CARGO_DISPATCHER_APC = { + ClassName = "AI_CARGO_DISPATCHER_APC", + SetAPC = nil, + SetDeployZones = nil, + AI_CARGO_APC = {} +} + +--- @type AI_CARGO_DISPATCHER_APC.AI_CARGO_APC +-- @map + +--- @field #AI_CARGO_DISPATCHER_APC.AI_CARGO_APC +AI_CARGO_DISPATCHER_APC.AICargoAPC = {} + +--- Creates a new AI_CARGO_DISPATCHER_APC object. +-- @param #AI_CARGO_DISPATCHER_APC self +-- @param Core.Set#SET_GROUP SetAPC +-- @param Core.Set#SET_CARGO SetCargo +-- @param Core.Set#SET_ZONE SetDeployZone +-- @return #AI_CARGO_DISPATCHER_APC +-- @usage +-- +-- -- Create a new cargo dispatcher +-- SetAPC = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() +-- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() +-- AICargoDispatcher = AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo ) +-- +function AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZones ) + + local self = BASE:Inherit( self, FSM:New() ) -- #AI_CARGO_DISPATCHER_APC + + self.SetAPC = SetAPC -- Core.Set#SET_GROUP + self.SetCargo = SetCargo -- Core.Set#SET_CARGO + self.SetDeployZones = SetDeployZones -- Core.Set#SET_ZONE + + self:SetStartState( "APC" ) + + self:AddTransition( "*", "Monitor", "*" ) + + self:AddTransition( "*", "Pickup", "*" ) + self:AddTransition( "*", "Loading", "*" ) + self:AddTransition( "*", "Loaded", "*" ) + + self:AddTransition( "*", "Deploy", "*" ) + self:AddTransition( "*", "Unloading", "*" ) + self:AddTransition( "*", "Unloaded", "*" ) + + self.PickupTimeInterval = 120 + self.DeployRadiusInner = 200 + self.DeployRadiusOuter = 500 + + return self +end + + +--- The Start trigger event, which actually takes action at the specified time interval. +-- @param #AI_CARGO_DISPATCHER_APC self +-- @param Wrapper.Group#GROUP APC +-- @return #AI_CARGO_DISPATCHER_APC +function AI_CARGO_DISPATCHER_APC:onafterMonitor() + + for APCGroupName, APC in pairs( self.SetAPC:GetSet() ) do + local APC = APC -- Wrapper.Group#GROUP + local AICargoAPC = self.AICargoAPC[APC] + if not AICargoAPC then + -- ok, so this APC does not have yet an AI_CARGO_APC object... + -- let's create one and also declare the Loaded and UnLoaded handlers. + self.AICargoAPC[APC] = AI_CARGO_APC:New( APC, self.SetCargo, self.CombatRadius ) + AICargoAPC = self.AICargoAPC[APC] + + function AICargoAPC.OnAfterPickup( AICargoAPC, APC ) + self.AICargoAPC = AICargoAPC + self:Pickup( APC ) + end + + function AICargoAPC.OnAfterLoad( AICargoAPC, APC ) + self.AICargoAPC = AICargoAPC + self:Load( APC ) + end + + function AICargoAPC.OnAfterLoaded( AICargoAPC, APC ) + self.AICargoAPC = AICargoAPC + self:Loaded( APC ) + end + + function AICargoAPC.OnAfterDeploy( AICargoAPC, APC ) + self.AICargoAPC = AICargoAPC + self:Deploy( APC ) + end + + function AICargoAPC.OnAfterUnload( AICargoAPC, APC ) + self.AICargoAPC = AICargoAPC + self:Unload( APC ) + end + + function AICargoAPC.OnAfterUnloaded( AICargoAPC, APC ) + self.AICargoAPC = AICargoAPC + self:Unloaded( APC ) + end + end + + -- The Pickup sequence ... + -- Check if this APC need to go and Pickup something... + if not AICargoAPC:IsTransporting() == true then + -- ok, so there is a free APC + -- now find the first cargo that is Unloaded + local FirstCargoUnloaded = self.SetCargo:FirstCargoUnLoaded() + if FirstCargoUnloaded then + AICargoAPC:Pickup( FirstCargoUnloaded:GetCoordinate() ) + break + end + end + end + + return self +end + + + +--- Make a APC run for a cargo deploy action after the cargo has been loaded, by default. +-- @param #AI_CARGO_DISPATCHER_APC self +-- @param Wrapper.Group#GROUP APC +-- @return #AI_CARGO_DISPATCHER_APC +function AI_CARGO_DISPATCHER_APC:OnAfterLoaded( APC ) + + self:Deploy( self.SetDeployZones:GetRandomZone():GetCoordinate():GetRandomCoordinateInRadius( self.DeployRadiusInner, self.DeployRadiusOuter ) ) + + return self +end + diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 6173a355b..501dd0e5e 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -58,6 +58,7 @@ DATABASE = { ZONENAMES = {}, HITS = {}, DESTROYS = {}, + ZONES = {}, } local _DATABASECoalition = @@ -1075,7 +1076,6 @@ function DATABASE:_RegisterTemplates() local CoalitionSide = coalition.side[string.upper(CoalitionName)] - ---------------------------------------------- -- build nav points DB self.Navpoints[CoalitionName] = {} if coa_data.nav_points then --navpoints @@ -1090,8 +1090,9 @@ function DATABASE:_RegisterTemplates() self.Navpoints[CoalitionName][nav_ind]['point']['y'] = 0 self.Navpoints[CoalitionName][nav_ind]['point']['z'] = nav_data.y end + end end - end + ------------------------------------------------- if coa_data.country then --there is a country table for cntry_id, cntry_data in pairs(coa_data.country) do @@ -1147,6 +1148,7 @@ function DATABASE:_RegisterTemplates() for ZoneID, ZoneData in pairs( env.mission.triggers.zones ) do local ZoneName = ZoneData.name self.ZONENAMES[ZoneName] = ZoneName + self.ZONES[ZoneName] = ZONE:New( ZoneName ) end return self diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index a59d82909..b8043328d 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -4336,6 +4336,48 @@ function SET_CARGO:FindNearestCargoFromPointVec2( PointVec2 ) --R2.1 return NearestCargo end +function SET_CARGO:FirstCargoWithState( State ) + + local FirstCargo = nil + + for CargoName, Cargo in pairs( self.Set ) do + if Cargo:Is( State ) then + FirstCargo = Cargo + break + end + end + + return FirstCargo +end + + +--- Iterate the SET_CARGO while identifying the first @{Cargo#CARGO} that is UnLoaded. +-- @param #SET_CARGO self +-- @return Cargo.Cargo#CARGO The first @{Cargo#CARGO}. +function SET_CARGO:FirstCargoUnLoaded() + local FirstCargo = self:FirstCargoWithState( "UnLoaded" ) + return FirstCargo +end + + +--- Iterate the SET_CARGO while identifying the first @{Cargo#CARGO} that is Loaded. +-- @param #SET_CARGO self +-- @return Cargo.Cargo#CARGO The first @{Cargo#CARGO}. +function SET_CARGO:FirstCargoLoaded() + local FirstCargo = self:FirstCargoWithState( "Loaded" ) + return FirstCargo +end + + +--- Iterate the SET_CARGO while identifying the first @{Cargo#CARGO} that is Deployed. +-- @param #SET_CARGO self +-- @return Cargo.Cargo#CARGO The first @{Cargo#CARGO}. +function SET_CARGO:FirstCargoDeployed() + local FirstCargo = self:FirstCargoWithState( "Deployed" ) + return FirstCargo +end + + --- (R2.1) @@ -4431,3 +4473,233 @@ function SET_CARGO:OnEventDeleteCargo( EventData ) --R2.1 end end + + +--- @type SET_ZONE +-- @extends Core.Set#SET_BASE + +--- # SET_ZONE class, extends @{Set#SET_BASE} +-- +-- Mission designers can use the @{Set#SET_ZONE} class to build sets of zones of various types. +-- +-- ## SET_ZONE constructor +-- +-- Create a new SET_ZONE object with the @{#SET_ZONE.New} method: +-- +-- * @{#SET_ZONE.New}: Creates a new SET_ZONE object. +-- +-- ## Add or Remove ZONEs from SET_ZONE +-- +-- ZONEs can be added and removed using the @{Set#SET_ZONE.AddZonesByName} and @{Set#SET_ZONE.RemoveZonesByName} respectively. +-- These methods take a single ZONE name or an array of ZONE names to be added or removed from SET_ZONE. +-- +-- ## 5.3) SET_ZONE filter criteria +-- +-- You can set filter criteria to build the collection of zones in SET_ZONE. +-- Filter criteria are defined by: +-- +-- * @{#SET_ZONE.FilterPrefixes}: Builds the SET_ZONE with the zones having a certain text pattern of prefix. +-- +-- Once the filter criteria have been set for the SET_ZONE, you can start filtering using: +-- +-- * @{#SET_ZONE.FilterStart}: Starts the filtering of the zones within the SET_ZONE. +-- +-- ## 5.4) SET_ZONE iterators +-- +-- Once the filters have been defined and the SET_ZONE has been built, you can iterate the SET_ZONE with the available iterator methods. +-- The iterator methods will walk the SET_ZONE set, and call for each airbase within the set a function that you provide. +-- The following iterator methods are currently available within the SET_ZONE: +-- +-- * @{#SET_ZONE.ForEachZone}: Calls a function for each zone it finds within the SET_ZONE. +-- +-- === +-- @field #SET_ZONE SET_ZONE +SET_ZONE = { + ClassName = "SET_ZONE", + Zones = {}, + Filter = { + Prefixes = nil, + }, + FilterMeta = { + }, +} + + +--- Creates a new SET_ZONE object, building a set of zones. +-- @param #SET_ZONE self +-- @return #SET_ZONE self +-- @usage +-- -- Define a new SET_ZONE Object. The DatabaseSet will contain a reference to all Zones. +-- DatabaseSet = SET_ZONE:New() +function SET_ZONE:New() + -- Inherits from BASE + local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.ZONES ) ) + + return self +end + +--- Add ZONEs to SET_ZONE. +-- @param Core.Set#SET_ZONE self +-- @param #string AddZoneNames A single name or an array of ZONE_BASE names. +-- @return self +function SET_ZONE:AddZonesByName( AddZoneNames ) + + local AddZoneNamesArray = ( type( AddZoneNames ) == "table" ) and AddZoneNames or { AddZoneNames } + + for AddAirbaseID, AddZoneName in pairs( AddZoneNamesArray ) do + self:Add( AddZoneName, ZONE:FindByName( AddZoneName ) ) + end + + return self +end + +--- Remove ZONEs from SET_ZONE. +-- @param Core.Set#SET_ZONE self +-- @param Core.Zone#ZONE_BASE RemoveZoneNames A single name or an array of ZONE_BASE names. +-- @return self +function SET_ZONE:RemoveZonesByName( RemoveZoneNames ) + + local RemoveZoneNamesArray = ( type( RemoveZoneNames ) == "table" ) and RemoveZoneNames or { RemoveZoneNames } + + for RemoveZoneID, RemoveZoneName in pairs( RemoveZoneNamesArray ) do + self:Remove( RemoveZoneName ) + end + + return self +end + + +--- Finds a Zone based on the Zone Name. +-- @param #SET_ZONE self +-- @param #string ZoneName +-- @return Core.Zone#ZONE_BASE The found Zone. +function SET_ZONE:FindZone( ZoneName ) + + local ZoneFound = self.Set[ZoneName] + return ZoneFound +end + + +--- Get a random zone from the set. +-- @param #SET_ZONE self +-- @return Core.Zone#ZONE_BASE The random Zone. +function SET_ZONE:GetRandomZone() + + local Index = self.Index + local ZoneFound = nil + + while not ZoneFound do + local ZoneRandom = math.random( 1, #Index ) + ZoneFound = self.Set[Index[ZoneRandom]] + end + + return ZoneFound +end + + + +--- Builds a set of zones of defined zone prefixes. +-- All the zones starting with the given prefixes will be included within the set. +-- @param #SET_ZONE self +-- @param #string Prefixes The prefix of which the zone name starts with. +-- @return #SET_ZONE self +function SET_ZONE:FilterPrefixes( Prefixes ) + if not self.Filter.Prefixes then + self.Filter.Prefixes = {} + end + if type( Prefixes ) ~= "table" then + Prefixes = { Prefixes } + end + for PrefixID, Prefix in pairs( Prefixes ) do + self.Filter.Prefixes[Prefix] = Prefix + end + return self +end + + +--- Starts the filtering. +-- @param #SET_ZONE self +-- @return #SET_ZONE self +function SET_ZONE:FilterStart() + + if _DATABASE then + + -- We initialize the first set. + for ObjectName, Object in pairs( self.Database ) do + if self:IsIncludeObject( Object ) then + self:Add( ObjectName, Object ) + else + self:RemoveZonesByName( ObjectName ) + end + end + 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_ZONE self +-- @param Core.Event#EVENTDATA Event +-- @return #string The name of the AIRBASE +-- @return #table The AIRBASE +function SET_ZONE: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_ZONE self +-- @param Core.Event#EVENTDATA Event +-- @return #string The name of the AIRBASE +-- @return #table The AIRBASE +function SET_ZONE:FindInDatabase( Event ) + self:F3( { Event } ) + + return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] +end + +--- Iterate the SET_ZONE and call an interator function for each ZONE, providing the ZONE and optional parameters. +-- @param #SET_ZONE self +-- @param #function IteratorFunction The function that will be called when there is an alive ZONE in the SET_ZONE. The function needs to accept a AIRBASE parameter. +-- @return #SET_ZONE self +function SET_ZONE:ForEachZone( IteratorFunction, ... ) + self:F2( arg ) + + self:ForEach( IteratorFunction, arg, self:GetSet() ) + + return self +end + + +--- +-- @param #SET_ZONE self +-- @param Core.Zone#ZONE_BASE MZone +-- @return #SET_ZONE self +function SET_ZONE:IsIncludeObject( MZone ) + self:F2( MZone ) + + local MZoneInclude = true + + if MZone then + local MZoneName = MZone:GetName() + + if self.Filter.Prefixes then + local MZonePrefix = false + for ZonePrefixId, ZonePrefix in pairs( self.Filter.Prefixes ) do + self:T3( { "Prefix:", string.find( MZoneName, ZonePrefix, 1 ), ZonePrefix } ) + if string.find( MZoneName, ZonePrefix, 1 ) then + MZonePrefix = true + end + end + self:T( { "Evaluated Prefix", MZonePrefix } ) + MZoneInclude = MZoneInclude and MZonePrefix + end + end + + self:T2( MZoneInclude ) + return MZoneInclude +end + diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index f909972b7..45a4bff5d 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -67,6 +67,7 @@ AI/AI_Cas.lua AI/AI_Bai.lua AI/AI_Formation.lua AI/AI_Cargo_APC.lua +AI/AI_Cargo_Dispatcher_APC.lua AI/AI_Cargo_Helicopter.lua AI/AI_Cargo_Airplane.lua From 6f0507ea7f11b29ae4a25b6a5364166fdc239568 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 10 May 2018 15:40:10 +0200 Subject: [PATCH 096/420] Minor fixes. Positionalble: Added isExist because of problems with getting coords from scenery objects. Suppresson: Fixes. Added new transitions. PseudoATC: Removed eject. Artillery: Optimized debug output. --- .../Moose/Functional/Artillery.lua | 82 +++++++++++-------- .../Moose/Functional/PseudoATC.lua | 9 +- .../Moose/Functional/Suppression.lua | 15 +++- .../Moose/Wrapper/Positionable.lua | 6 +- Moose Setup/Moose.files | 3 + 5 files changed, 69 insertions(+), 46 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 418bb359a..8849c68e0 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -330,7 +330,7 @@ -- @field #ARTY ARTY={ ClassName="ARTY", - Debug=true, + Debug=false, targets={}, moves={}, currentTarget=nil, @@ -384,7 +384,7 @@ ARTY.WeaponType={ ARTY.id="ARTY | " --- Arty script version. --- @field #number version +-- @field #string version ARTY.version="0.9.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -799,7 +799,8 @@ function ARTY:onafterStart(Controllable, From, Event, To) self:_EventFromTo("onafterStart", Event, From, To) -- Debug output. - local text=string.format("Started ARTY for group %s.", Controllable:GetName()) + local text=string.format("Started ARTY version %s for group %s.", ARTY.version, Controllable:GetName()) + self:E(ARTY.id..text) MESSAGE:New(text, 10):ToAllIf(self.Debug) -- Get Ammo. @@ -954,31 +955,31 @@ function ARTY:_OnEventShot(EventData) -- Weapon type name for current target. local _weapontype=self:_WeaponTypeName(self.currentTarget.weapontype) - self:T(ARTY.id..string.format("nammo=%d, nshells=%d, nrockets=%d, nmissiles=%d", _nammo, _nshells, _nrockets, _nmissiles)) - self:T(ARTY.id..string.format("Weapontype = %s", _weapontype)) + self:T(ARTY.id..string.format("Group %s ammo: total=%d, shells=%d, rockets=%d, missiles=%d", self.Controllable:GetName(), _nammo, _nshells, _nrockets, _nmissiles)) + self:T2(ARTY.id..string.format("Group %s uses weapontype %s for current target.", self.Controllable:GetName(), _weapontype)) -- Special weapon type requested ==> Check if corresponding ammo is empty. if self.currentTarget.weapontype==ARTY.WeaponType.Cannon and _nshells==0 then - self:T(ARTY.id.."Cannons requested but shells empty.") + self:T(ARTY.id.."Group %s, cannons requested but shells empty.", self.Controllable:GetName()) self:CeaseFire(self.currentTarget) return elseif self.currentTarget.weapontype==ARTY.WeaponType.Rockets and _nrockets==0 then - self:T(ARTY.id.."Rockets requested but rockets empty.") + self:T(ARTY.id.."Group %s, rockets requested but rockets empty.", self.Controllable:GetName()) self:CeaseFire(self.currentTarget) return elseif self.currentTarget.weapontype==ARTY.WeaponType.UnguidedAny and _nshells+_nrockets==0 then - self:T(ARTY.id.."Unguided weapon requested but shells AND rockets empty.") + self:T(ARTY.id.."Group %s, unguided weapon requested but shells AND rockets empty.", self.Controllable:GetName()) self:CeaseFire(self.currentTarget) return elseif (self.currentTarget.weapontype==ARTY.WeaponType.GuidedMissile or self.currentTarget.weapontype==ARTY.WeaponType.CruiseMissile or self.currentTarget.weapontype==ARTY.WeaponType.AntiShipMissile) and _nmissiles==0 then - self:T(ARTY.id.."Guided, anti-ship or cruise missiles requested but all missiles empty.") + self:T(ARTY.id.."Group %s, guided, anti-ship or cruise missiles requested but all missiles empty.", self.Controllable:GetName()) self:CeaseFire(self.currentTarget) return @@ -995,7 +996,7 @@ function ARTY:_OnEventShot(EventData) end else - self:E(ARTY.id..string.format("ERROR: No current target?!")) + self:E(ARTY.id..string.format("ERROR: No current target for group %s?!", self.Controllable:GetName())) end end end @@ -1042,22 +1043,20 @@ function ARTY:onafterStatus(Controllable, From, Event, To) -- Group is out of ammo. if self:is("OutOfAmmo") then - env.info(string.format("FF: OutOfAmmo. ==> Rearm")) + self:T2(ARTY.id..string.format("%s: OutOfAmmo. ==> Rearm", Controllable:GetName())) self:Rearm() end -- Group is out of moving. if self:is("Moving") then - --local _speed=self.Controllable:GetVelocityKMH() - --env.info(string.format("FF: Moving. Velocity = %d km/h", _speed)) - env.info(string.format("FF: Moving")) + self:T2(ARTY.id..string.format("%s: Moving", Controllable:GetName())) end -- Group is rearming. if self:is("Rearming") then local _rearmed=self:_CheckRearmed() - env.info(string.format("FF: Rearming. _rearmed = %s", tostring(_rearmed))) if _rearmed then + self:T2(ARTY.id..string.format("%s: Rearming ==> Rearmed", Controllable:GetName())) self:Rearmed() end end @@ -1065,15 +1064,16 @@ function ARTY:onafterStatus(Controllable, From, Event, To) -- Group finished rearming. if self:is("Rearmed") then local distance=self.Controllable:GetCoordinate():Get2DDistance(self.InitialCoord) - env.info(string.format("FF: Rearmed. Distance ARTY to InitalCoord = %d", distance)) + self:T2(ARTY.id..string.format("%s: Rearmed. Distance ARTY to InitalCoord = %d m", Controllable:GetName(), distance)) if distance <= self.RearmingDistance then + self:T2(ARTY.id..string.format("%s: Rearmed ==> CombatReady", Controllable:GetName())) self:CombatReady() end end -- Group arrived at destination. if self:is("Arrived") then - env.info(string.format("FF: Arrived. ==> CombatReady")) + self:T2(ARTY.id..string.format("%s: Arrived ==> CombatReady", Controllable:GetName())) self:CombatReady() end @@ -1093,15 +1093,18 @@ function ARTY:onafterStatus(Controllable, From, Event, To) -- Get a commaned move to another location. local _move=self:_CheckMoves() - -- Group is combat ready or firing but we have a high prio timed target. + if (self:is("CombatReady") or self:is("Firing")) and _move then + -- Group is combat ready or firing but we have a move. + self:T2(ARTY.id..string.format("%s: CombatReady/Firing ==> Move", Controllable:GetName())) -- Command to move. self.currentMove=_move self:Move(_move.coord, _move.speed, _move.onroad) elseif self:is("CombatReady") or (self:is("Firing") and _timedTarget) then - env.info(string.format("FF: Combatready or firing and high prio timed target.")) + -- Group is combat ready or firing but we have a high prio timed target. + self:T2(ARTY.id..string.format("%s: CombatReady or Firing+Timed Target ==> OpenFire", Controllable:GetName())) -- Engage target. if _timedTarget then @@ -1137,7 +1140,7 @@ end function ARTY:onenterCombatReady(Controllable, From, Event, To) self:_EventFromTo("onenterCombatReady", Event, From, To) -- Debug info - self:T(string.format("FF: onenterComabReady, from=%s, event=%s, to=%s", From, Event, To)) + self:T3(ARTY.id..string.format("onenterComabReady, from=%s, event=%s, to=%s", From, Event, To)) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1882,14 +1885,16 @@ function ARTY:GetAmmo(display) local weapons=#ammotable - self:T2(ARTY.id..string.format("Number of weapons %d.", weapons)) - self:T2({ammotable=ammotable}) - - self:T(ARTY.id.."Ammotable:") - for id,bla in pairs(ammotable) do - self:T({id=id, ammo=bla}) + -- Display ammo table + if display then + self:E(ARTY.id..string.format("Number of weapons %d.", weapons)) + self:E({ammotable=ammotable}) + self:E(ARTY.id.."Ammotable:") + for id,bla in pairs(ammotable) do + self:E({id=id, ammo=bla}) + end end - + -- Loop over all weapons. for w=1,weapons do @@ -1957,7 +1962,7 @@ function ARTY:GetAmmo(display) nshells=nshells+Nammo -- Debug info. - text=text..string.format("- %d shells of type %s (category=%d mc=%s)\n", Nammo, Tammo, Category, tostring(MissileCategory)) + text=text..string.format("- %d shells of type %s (category=%d, missile category=%s)\n", Nammo, Tammo, Category, tostring(MissileCategory)) elseif _gotrocket then @@ -1965,7 +1970,7 @@ function ARTY:GetAmmo(display) nrockets=nrockets+Nammo -- Debug info. - text=text..string.format("- %d rockets of type %s (category=%d mc=%s)\n", Nammo, Tammo, Category, tostring(MissileCategory)) + text=text..string.format("- %d rockets of type %s (category=%d, missile category=%s)\n", Nammo, Tammo, Category, tostring(MissileCategory)) elseif _gotmissile then @@ -1973,12 +1978,12 @@ function ARTY:GetAmmo(display) nmissiles=nmissiles+Nammo -- Debug info. - text=text..string.format("- %d missiles of type %s (category=%d mc=%s)\n", Nammo, Tammo, Category, tostring(MissileCategory)) + text=text..string.format("- %d missiles of type %s (category=%d, missile category=%s)\n", Nammo, Tammo, Category, tostring(MissileCategory)) else -- Debug info. - text=text..string.format("- %d unknown ammo of type %s (category=%d mc=%s)\n", Nammo, Tammo, Category, tostring(MissileCategory)) + text=text..string.format("- %d unknown ammo of type %s (category=%d, missile category=%s)\n", Nammo, Tammo, Category, tostring(MissileCategory)) end @@ -1986,7 +1991,11 @@ function ARTY:GetAmmo(display) end -- Debug text and send message. - self:T(ARTY.id..text) + if display then + self:E(ARTY.id..text) + else + self:T3(ARTY.id..text) + end MESSAGE:New(text, 10):ToAllIf(display) end @@ -2112,7 +2121,7 @@ function ARTY:_CheckName(givennames, name) until (unique) -- Debug output and return new name. - self:T(string.format("Original name %s, new name = %s", name, newname)) + self:T2(string.format("Original name %s, new name = %s", name, newname)) return newname end @@ -2171,6 +2180,8 @@ function ARTY:_WeaponTypeName(tnumber) name="Cruise Missiles" elseif tnumber==ARTY.WeaponType.GuidedMissile then name="Guided Missiles" + elseif tnumber==ARTY.WeaponType.AntiShipMissile then + name="Anti-Ship Missiles" end return name end @@ -2398,12 +2409,15 @@ function ARTY._PassingWaypoint(group, arty, i, final) if final then text=string.format("%s, arrived at destination.", group:GetName()) end - env.info(ARTY.id..text) + arty:T(ARTY.id..text) + + --[[ if final then MESSAGE:New(text, 10):ToCoalitionIf(group:GetCoalition(), arty.Debug or arty.report) else MESSAGE:New(text, 10):ToAllIf(arty.Debug) end + ]] -- Arrived event. if final and arty.Controllable:GetName()==group:GetName() then diff --git a/Moose Development/Moose/Functional/PseudoATC.lua b/Moose Development/Moose/Functional/PseudoATC.lua index 9726f1666..ce7f4e737 100644 --- a/Moose Development/Moose/Functional/PseudoATC.lua +++ b/Moose Development/Moose/Functional/PseudoATC.lua @@ -146,7 +146,7 @@ function PSEUDOATC:Start() self:HandleEvent(EVENTS.Takeoff, self._PlayerTakeOff) self:HandleEvent(EVENTS.PlayerLeaveUnit, self._PlayerLeft) self:HandleEvent(EVENTS.Crash, self._PlayerLeft) - self:HandleEvent(EVENTS.Ejection, self._PlayerLeft) + --self:HandleEvent(EVENTS.Ejection, self._PlayerLeft) --self:HandleEvent(EVENTS.PilotDead, self._PlayerLeft) else self:T(PSEUDOATC.id.."Events are handled by DCS.") @@ -280,6 +280,7 @@ function PSEUDOATC:onEvent(Event) self:_PlayerLeft(EventData) end +--[[ -- Event eject ==> player left unit if Event.id == world.event.S_EVENT_EJECTION and _playername then self:_PlayerLeft(EventData) @@ -289,7 +290,7 @@ function PSEUDOATC:onEvent(Event) if Event.id == world.event.S_EVENT_PILOT_DEAD and _playername then self:_PlayerLeft(EventData) end - +]] end --- Function called my MOOSE event handler when a player enters a unit. @@ -477,7 +478,7 @@ function PSEUDOATC:PlayerTakeOff(unit, place) -- Bye-Bye message. if place and self.chatty then - local text=string.format("%s, %s, you are airborn. Have a save trip!", place, CallSign) + local text=string.format("%s, %s, you are airborne. Have a safe trip!", place, CallSign) MESSAGE:New(text, self.mdur):ToGroup(group) end @@ -808,8 +809,6 @@ function PSEUDOATC:ReportHeight(id, dt, _clear) -- Settings. local settings=_DATABASE:GetPlayerSettings(self.player[id].playername) or _SETTINGS --Core.Settings#SETTINGS - env.info("FF height = "..height) - -- Height string. local Hs=string.format("%d ft", UTILS.MetersToFeet(height)) if settings:IsMetric() then diff --git a/Moose Development/Moose/Functional/Suppression.lua b/Moose Development/Moose/Functional/Suppression.lua index 8eb913930..41fb3dc09 100644 --- a/Moose Development/Moose/Functional/Suppression.lua +++ b/Moose Development/Moose/Functional/Suppression.lua @@ -357,6 +357,8 @@ function SUPPRESSION:New(group) self:AddTransition("Retreating", "Retreated", "Retreated") self:AddTransition("*", "Dead", "*") + self:AddTransition("TakingCover", "Hit", "TakingCover") + self:AddTransition("FallingBack", "Hit", "FallingBack") --- User function for OnBefore "Hit" event. -- @function [parent=#SUPPRESSION] OnBeforeHit @@ -1078,7 +1080,9 @@ function SUPPRESSION:onafterFallBack(Controllable, From, Event, To, AttackUnit) local Coord=DCoord:Translate(self.FallbackDist, heading) -- Place marker - local MarkerID=Coord:MarkToAll("Fall back position for group "..Controllable:GetName()) + if self.Debug then + local MarkerID=Coord:MarkToAll("Fall back position for group "..Controllable:GetName()) + end -- Smoke the coordinate. if self.smoke or self.Debug then @@ -1468,7 +1472,7 @@ end function SUPPRESSION:_Run(fin, speed, formation, wait) speed=speed or 20 - formation=formation or "Vee" + formation=formation or "Off road" wait=wait or 30 local group=self.Controllable -- Wrapper.Controllable#CONTROLLABLE @@ -1506,8 +1510,11 @@ function SUPPRESSION:_Run(fin, speed, formation, wait) -- First waypoint is the current position of the group. wp[1]=ini:WaypointGround(speed, formation) - local MarkerID=ini:MarkToAll(string.format("Waypoing %d of group %s (initial)", #wp, self.Controllable:GetName())) tasks[1]=group:TaskFunction("SUPPRESSION._Passing_Waypoint", self, 1, false) + + if self.Debug then + local MarkerID=ini:MarkToAll(string.format("Waypoing %d of group %s (initial)", #wp, self.Controllable:GetName())) + end self:T2(SUPPRESSION.id..string.format("Number of waypoints %d", nx)) for i=1,nx-2 do @@ -1830,7 +1837,7 @@ end -- @param #string To To state. function SUPPRESSION:_EventFromTo(BA, Event, From, To) local text=string.format("\n%s: %s EVENT %s: %s --> %s", BA, self.Controllable:GetName(), Event, From, To) - self:T(SUPPRESSION.id..text) + self:T2(SUPPRESSION.id..text) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index d9308e8bb..1ac85b6a4 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -391,7 +391,7 @@ function POSITIONABLE:GetVelocityVec3() local DCSPositionable = self:GetDCSObject() - if DCSPositionable then + if DCSPositionable and DCSPositionable:isExist() then local PositionableVelocityVec3 = DCSPositionable:getVelocity() self:T3( PositionableVelocityVec3 ) return PositionableVelocityVec3 @@ -433,7 +433,7 @@ function POSITIONABLE:GetVelocityKMH() local DCSPositionable = self:GetDCSObject() - if DCSPositionable then + if DCSPositionable and DCSPositionable:isExist() then 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. @@ -452,7 +452,7 @@ function POSITIONABLE:GetVelocityMPS() local DCSPositionable = self:GetDCSObject() - if DCSPositionable then + if DCSPositionable and DCSPositionable:isExist() then local VelocityVec3 = self:GetVelocityVec3() local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec self:T3( Velocity ) diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index f909972b7..1974715d9 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -54,6 +54,9 @@ Functional/Range.lua Functional/ZoneGoal.lua Functional/ZoneGoalCoalition.lua Functional/ZoneCaptureCoalition.lua +Functional/Artillery.lua +Functional/Suppression.lua +Functional/PseudoATC.lua AI/AI_Balancer.lua AI/AI_A2A.lua From 6b31ad9645cab7dea4736adaced5f4dd791f9789 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Thu, 10 May 2018 20:00:50 +0200 Subject: [PATCH 097/420] Moose file changes --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 47 +++-- .../Moose/AI/AI_Cargo_Dispatcher.lua | 188 ++++++++++++++++++ .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 112 +---------- .../Moose/AI/AI_Cargo_Dispatcher_Airplane.lua | 54 +++++ .../AI/AI_Cargo_Dispatcher_Helicopter.lua | 54 +++++ Moose Development/Moose/Core/Database.lua | 1 + Moose Development/Moose/Core/Set.lua | 25 ++- Moose Setup/Moose.files | 5 +- 8 files changed, 360 insertions(+), 126 deletions(-) create mode 100644 Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua create mode 100644 Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua create mode 100644 Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index f867256b9..9e4351bee 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -190,6 +190,7 @@ function AI_CARGO_APC:New( CargoCarrier, CargoSet, CombatRadius ) self:SetCarrier( CargoCarrier ) self.Transporting = false + self.Relocating = false return self end @@ -227,7 +228,7 @@ function AI_CARGO_APC:SetCarrier( CargoCarrier ) self:F( { OnHitLoaded = AICargoTroops:Is( "Loaded" ) } ) if AICargoTroops:Is( "Loaded" ) or AICargoTroops:Is( "Boarding" ) then -- There are enemies within combat range. Unload the CargoCarrier. - AICargoTroops:Unload() + AICargoTroops:Unload( false ) end end end @@ -248,6 +249,11 @@ function AI_CARGO_APC:IsTransporting() return self.Transporting == true end +function AI_CARGO_APC:IsRelocating() + + return self.Relocating == true +end + --- Find a free Carrier within a range. -- @param #AI_CARGO_APC self -- @param Core.Point#COORDINATE Coordinate @@ -337,7 +343,7 @@ function AI_CARGO_APC:onafterMonitor( APC, From, Event, To ) if APC and APC:IsAlive() then if self.CarrierCoordinate then - if self:IsTransporting() then + if self:IsRelocating() == true then local Coordinate = APC:GetCoordinate() self.Zone:Scan( { Object.Category.UNIT } ) if self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then @@ -348,7 +354,7 @@ function AI_CARGO_APC:onafterMonitor( APC, From, Event, To ) else if self:Is( "Loaded" ) then -- There are enemies within combat range. Unload the CargoCarrier. - self:__Unload( 1 ) + self:__Unload( 1, false ) else if self:Is( "Unloaded" ) then self:Follow() @@ -398,8 +404,8 @@ function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To ) local APCUnit = APCUnit -- Wrapper.Unit#UNIT for _, Cargo in pairs( self.CargoSet:GetSet() ) do local Cargo = Cargo -- Cargo.Cargo#CARGO - self:F( { IsUnLoaded = Cargo:IsUnLoaded(), Cargo:GetName(), APC:GetName() } ) - if Cargo:IsUnLoaded() then + self:F( { IsUnLoaded = Cargo:IsUnLoaded(), IsDeployed = Cargo:IsDeployed(), Cargo:GetName(), APC:GetName() } ) + if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then if Cargo:IsInLoadRadius( APCUnit:GetCoordinate() ) then self:F( { "In radius", APCUnit:GetName() } ) APC:RouteStop() @@ -467,8 +473,8 @@ end --- @param #AI_CARGO_APC self -- @param Wrapper.Group#GROUP APC -function AI_CARGO_APC:onafterUnload( APC, From, Event, To ) - self:F( { APC, From, Event, To } ) +function AI_CARGO_APC:onafterUnload( APC, From, Event, To, Deployed ) + self:F( { APC, From, Event, To, Deployed } ) if APC and APC:IsAlive() then for _, APCUnit in pairs( APC:GetUnits() ) do @@ -476,7 +482,7 @@ function AI_CARGO_APC:onafterUnload( APC, From, Event, To ) APC:RouteStop() for _, Cargo in pairs( APCUnit:GetCargo() ) do Cargo:UnBoard() - self:__Unboard( 10, Cargo ) + self:__Unboard( 10, Cargo, Deployed ) end end end @@ -485,14 +491,14 @@ end --- @param #AI_CARGO_APC self -- @param Wrapper.Group#GROUP APC -function AI_CARGO_APC:onafterUnboard( APC, From, Event, To, Cargo ) +function AI_CARGO_APC:onafterUnboard( APC, From, Event, To, Cargo, Deployed ) self:F( { APC, From, Event, To, Cargo:GetName() } ) if APC and APC:IsAlive() then if not Cargo:IsUnLoaded() then - self:__Unboard( 10, Cargo ) + self:__Unboard( 10, Cargo, Deployed ) else - self:__Unloaded( 1, Cargo ) + self:__Unloaded( 1, Cargo, Deployed ) end end @@ -500,8 +506,8 @@ end --- @param #AI_CARGO_APC self -- @param Wrapper.Group#GROUP APC -function AI_CARGO_APC:onbeforeUnloaded( APC, From, Event, To, Cargo ) - self:F( { APC, From, Event, To, Cargo:GetName() } ) +function AI_CARGO_APC:onbeforeUnloaded( APC, From, Event, To, Cargo, Deployed ) + self:F( { APC, From, Event, To, Cargo:GetName(), Deployed = Deployed } ) local AllUnloaded = true @@ -521,6 +527,13 @@ function AI_CARGO_APC:onbeforeUnloaded( APC, From, Event, To, Cargo ) end if AllUnloaded == true then + if Deployed == true then + for APCUnit, Cargo in pairs( self.APC_Cargo ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + Cargo:SetDeployed( true ) + end + self.APC_Cargo = {} + end self:Guard() self.CargoCarrier = APC end @@ -559,7 +572,7 @@ function AI_CARGO_APC._Pickup( APC, self ) if APC:IsAlive() then self:Load() - self.Transporting = true + self.Relocating = true end end @@ -571,9 +584,9 @@ function AI_CARGO_APC._Deploy( APC, self ) APC:F( { "AI_CARGO_APC._Deploy:", APC } ) if APC:IsAlive() then - self:Unload() + self:Unload( true ) self.Transporting = false - self.APC_Cargo = {} + self.Relocating = false end end @@ -606,6 +619,8 @@ function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed, En else AI_CARGO_APC._Pickup( APC, self ) end + + self.Transporting = true end end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua new file mode 100644 index 000000000..eeaffec9f --- /dev/null +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -0,0 +1,188 @@ +--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo. +-- +-- === +-- +-- ### Author: **FlightControl** +-- +-- === +-- +-- @module AI_Cargo_Dispatcher + +--- @type AI_CARGO_DISPATCHER +-- @extends Core.Fsm#FSM_CONTROLLABLE + + +--- # AI\_CARGO\_DISPATCHER class, extends @{Core.Base#BASE} +-- +-- === +-- +-- AI\_CARGO\_DISPATCHER brings a dynamic cargo handling capability for AI groups. +-- +-- Armoured Personnel APCs (APC), Trucks, Jeeps and other carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. +-- The AI\_CARGO\_DISPATCHER module uses the @{Cargo} capabilities within the MOOSE framework. +-- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER object recognize the cargo. +-- Please consult the @{Cargo} module for more information. +-- +-- +-- +-- @field #AI_CARGO_DISPATCHER +AI_CARGO_DISPATCHER = { + ClassName = "AI_CARGO_DISPATCHER", + SetAPC = nil, + SetDeployZones = nil, + AI_CARGO_APC = {} +} + +--- @type AI_CARGO_DISPATCHER.AI_CARGO_APC +-- @map + +--- @field #AI_CARGO_DISPATCHER.AI_CARGO_APC +AI_CARGO_DISPATCHER.AICargoAPC = {} + +--- @field #AI_CARGO_DISPATCHER.PickupCargo +AI_CARGO_DISPATCHER.PickupCargo = {} + +--- Creates a new AI_CARGO_DISPATCHER object. +-- @param #AI_CARGO_DISPATCHER self +-- @param Core.Set#SET_GROUP SetAPC +-- @param Core.Set#SET_CARGO SetCargo +-- @param Core.Set#SET_ZONE SetDeployZone +-- @return #AI_CARGO_DISPATCHER +-- @usage +-- +-- -- Create a new cargo dispatcher +-- SetAPC = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() +-- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() +-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo ) +-- +function AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZones ) + + local self = BASE:Inherit( self, FSM:New() ) -- #AI_CARGO_DISPATCHER + + self.SetAPC = SetAPC -- Core.Set#SET_GROUP + self.SetCargo = SetCargo -- Core.Set#SET_CARGO + self.SetDeployZones = SetDeployZones -- Core.Set#SET_ZONE + + self:SetStartState( "APC" ) + + self:AddTransition( "*", "Monitor", "*" ) + + self:AddTransition( "*", "Pickup", "*" ) + self:AddTransition( "*", "Loading", "*" ) + self:AddTransition( "*", "Loaded", "*" ) + + self:AddTransition( "*", "Deploy", "*" ) + self:AddTransition( "*", "Unloading", "*" ) + self:AddTransition( "*", "Unloaded", "*" ) + + self.MonitorTimeInterval = 120 + self.CombatRadius = 500 + self.DeployRadiusInner = 200 + self.DeployRadiusOuter = 500 + + self:Monitor( 1 ) + + return self +end + + +--- The Start trigger event, which actually takes action at the specified time interval. +-- @param #AI_CARGO_DISPATCHER self +-- @param Wrapper.Group#GROUP APC +-- @return #AI_CARGO_DISPATCHER +function AI_CARGO_DISPATCHER:onafterMonitor() + + for APCGroupName, APC in pairs( self.SetAPC:GetSet() ) do + local APC = APC -- Wrapper.Group#GROUP + local AICargoAPC = self.AICargoAPC[APC] + if not AICargoAPC then + -- ok, so this APC does not have yet an AI_CARGO_APC object... + -- let's create one and also declare the Loaded and UnLoaded handlers. + self.AICargoAPC[APC] = AI_CARGO_APC:New( APC, self.SetCargo, self.CombatRadius ) + AICargoAPC = self.AICargoAPC[APC] + + function AICargoAPC.OnAfterPickup( AICargoAPC, APC, From, Event, To, Cargo ) + self:Pickup( APC, Cargo ) + end + + function AICargoAPC.OnAfterLoad( AICargoAPC, APC ) + self:Load( APC ) + end + + function AICargoAPC.OnAfterLoaded( AICargoAPC, APC, From, Event, To, Cargo ) + self:Loaded( APC, Cargo ) + end + + function AICargoAPC.OnAfterDeploy( AICargoAPC, APC ) + self:Deploy( APC ) + end + + function AICargoAPC.OnAfterUnload( AICargoAPC, APC ) + self:Unload( APC ) + end + + function AICargoAPC.OnAfterUnloaded( AICargoAPC, APC ) + self:Unloaded( APC ) + end + end + + -- The Pickup sequence ... + -- Check if this APC need to go and Pickup something... + self:I( { IsTransporting = AICargoAPC:IsTransporting() } ) + if AICargoAPC:IsTransporting() == false then + -- ok, so there is a free APC + -- now find the first cargo that is Unloaded + + local PickupCargo = nil + + for CargoName, Cargo in pairs( self.SetCargo:GetSet() ) do + if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then + if not self.PickupCargo[Cargo] then + self.PickupCargo[Cargo] = APC + PickupCargo = Cargo + break + end + end + end + if PickupCargo then + AICargoAPC:Pickup( PickupCargo:GetCoordinate(), 70 ) + break + end + end + end + + self:__Monitor( self.MonitorTimeInterval ) + + return self +end + + + +--- Make a APC run for a cargo deploy action after the cargo Pickup trigger has been initiated, by default. +-- @param #AI_CARGO_DISPATCHER self +-- @param Wrapper.Group#GROUP APC +-- @return #AI_CARGO_DISPATCHER +function AI_CARGO_DISPATCHER:onafterPickup( From, Event, To, APC, Cargo ) + return self +end + +--- Make a APC run for a cargo deploy action after the cargo has been loaded, by default. +-- @param #AI_CARGO_DISPATCHER self +-- @param Wrapper.Group#GROUP APC +-- @return #AI_CARGO_DISPATCHER +function AI_CARGO_DISPATCHER:OnAfterLoaded( From, Event, To, APC, Cargo ) + + self:I( { "Loaded Dispatcher", APC } ) + local RandomZone = self.SetDeployZones:GetRandomZone() + self:I( { RandomZone = RandomZone } ) + + self.AICargoAPC[APC]:Deploy( RandomZone:GetCoordinate(), 70 ) + self.PickupCargo[Cargo] = nil + + return self +end + + + + diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index d9c93e936..eb3c0d4f7 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -1,4 +1,4 @@ ---- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo. +--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo using APCs. -- -- === -- @@ -6,10 +6,10 @@ -- -- === -- --- @module AI_Cargo_Dispatcher +-- @module AI_Cargo_Dispatcher_APC --- @type AI_CARGO_DISPATCHER_APC --- @extends Core.Fsm#FSM_CONTROLLABLE +-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER --- # AI\_CARGO\_DISPATCHER\_APC class, extends @{Core.Base#BASE} @@ -28,17 +28,8 @@ -- @field #AI_CARGO_DISPATCHER_APC AI_CARGO_DISPATCHER_APC = { ClassName = "AI_CARGO_DISPATCHER_APC", - SetAPC = nil, - SetDeployZones = nil, - AI_CARGO_APC = {} } ---- @type AI_CARGO_DISPATCHER_APC.AI_CARGO_APC --- @map - ---- @field #AI_CARGO_DISPATCHER_APC.AI_CARGO_APC -AI_CARGO_DISPATCHER_APC.AICargoAPC = {} - --- Creates a new AI_CARGO_DISPATCHER_APC object. -- @param #AI_CARGO_DISPATCHER_APC self -- @param Core.Set#SET_GROUP SetAPC @@ -55,104 +46,9 @@ AI_CARGO_DISPATCHER_APC.AICargoAPC = {} -- function AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZones ) - local self = BASE:Inherit( self, FSM:New() ) -- #AI_CARGO_DISPATCHER_APC - - self.SetAPC = SetAPC -- Core.Set#SET_GROUP - self.SetCargo = SetCargo -- Core.Set#SET_CARGO - self.SetDeployZones = SetDeployZones -- Core.Set#SET_ZONE - - self:SetStartState( "APC" ) - - self:AddTransition( "*", "Monitor", "*" ) - - self:AddTransition( "*", "Pickup", "*" ) - self:AddTransition( "*", "Loading", "*" ) - self:AddTransition( "*", "Loaded", "*" ) - - self:AddTransition( "*", "Deploy", "*" ) - self:AddTransition( "*", "Unloading", "*" ) - self:AddTransition( "*", "Unloaded", "*" ) - - self.PickupTimeInterval = 120 - self.DeployRadiusInner = 200 - self.DeployRadiusOuter = 500 - - return self -end - - ---- The Start trigger event, which actually takes action at the specified time interval. --- @param #AI_CARGO_DISPATCHER_APC self --- @param Wrapper.Group#GROUP APC --- @return #AI_CARGO_DISPATCHER_APC -function AI_CARGO_DISPATCHER_APC:onafterMonitor() - - for APCGroupName, APC in pairs( self.SetAPC:GetSet() ) do - local APC = APC -- Wrapper.Group#GROUP - local AICargoAPC = self.AICargoAPC[APC] - if not AICargoAPC then - -- ok, so this APC does not have yet an AI_CARGO_APC object... - -- let's create one and also declare the Loaded and UnLoaded handlers. - self.AICargoAPC[APC] = AI_CARGO_APC:New( APC, self.SetCargo, self.CombatRadius ) - AICargoAPC = self.AICargoAPC[APC] - - function AICargoAPC.OnAfterPickup( AICargoAPC, APC ) - self.AICargoAPC = AICargoAPC - self:Pickup( APC ) - end - - function AICargoAPC.OnAfterLoad( AICargoAPC, APC ) - self.AICargoAPC = AICargoAPC - self:Load( APC ) - end - - function AICargoAPC.OnAfterLoaded( AICargoAPC, APC ) - self.AICargoAPC = AICargoAPC - self:Loaded( APC ) - end - - function AICargoAPC.OnAfterDeploy( AICargoAPC, APC ) - self.AICargoAPC = AICargoAPC - self:Deploy( APC ) - end - - function AICargoAPC.OnAfterUnload( AICargoAPC, APC ) - self.AICargoAPC = AICargoAPC - self:Unload( APC ) - end - - function AICargoAPC.OnAfterUnloaded( AICargoAPC, APC ) - self.AICargoAPC = AICargoAPC - self:Unloaded( APC ) - end - end - - -- The Pickup sequence ... - -- Check if this APC need to go and Pickup something... - if not AICargoAPC:IsTransporting() == true then - -- ok, so there is a free APC - -- now find the first cargo that is Unloaded - local FirstCargoUnloaded = self.SetCargo:FirstCargoUnLoaded() - if FirstCargoUnloaded then - AICargoAPC:Pickup( FirstCargoUnloaded:GetCoordinate() ) - break - end - end - end + local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZones ) ) -- #AI_CARGO_DISPATCHER_APC return self end - ---- Make a APC run for a cargo deploy action after the cargo has been loaded, by default. --- @param #AI_CARGO_DISPATCHER_APC self --- @param Wrapper.Group#GROUP APC --- @return #AI_CARGO_DISPATCHER_APC -function AI_CARGO_DISPATCHER_APC:OnAfterLoaded( APC ) - - self:Deploy( self.SetDeployZones:GetRandomZone():GetCoordinate():GetRandomCoordinateInRadius( self.DeployRadiusInner, self.DeployRadiusOuter ) ) - - return self -end - diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua new file mode 100644 index 000000000..4deaef1d9 --- /dev/null +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua @@ -0,0 +1,54 @@ +--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo using Planes. +-- +-- === +-- +-- ### Author: **FlightControl** +-- +-- === +-- +-- @module AI_Cargo_Dispatcher_Airplane + +--- @type AI_CARGO_DISPATCHER_AIRPLANE +-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER + + +--- # AI\_CARGO\_DISPATCHER\_AIRPLANE class, extends @{Core.Base#BASE} +-- +-- === +-- +-- AI\_CARGO\_DISPATCHER\_AIRPLANE brings a dynamic cargo handling capability for AI groups. +-- +-- Airplanes can be mobilized to intelligently transport infantry and other cargo within the simulation. +-- The AI\_CARGO\_DISPATCHER\_AIRPLANE module uses the @{Cargo} capabilities within the MOOSE framework. +-- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER\_AIRPLANE object recognize the cargo. +-- Please consult the @{Cargo} module for more information. +-- +-- +-- +-- @field #AI_CARGO_DISPATCHER_AIRPLANE +AI_CARGO_DISPATCHER_AIRPLANE = { + ClassName = "AI_CARGO_DISPATCHER_AIRPLANE", +} + +--- Creates a new AI_CARGO_DISPATCHER_AIRPLANE object. +-- @param #AI_CARGO_DISPATCHER_AIRPLANE self +-- @param Core.Set#SET_GROUP SetAirplane +-- @param Core.Set#SET_CARGO SetCargo +-- @param Core.Set#SET_ZONE SetDeployZone +-- @return #AI_CARGO_DISPATCHER_AIRPLANE +-- @usage +-- +-- -- Create a new cargo dispatcher +-- SetAirplane = SET_GROUP:New():FilterPrefixes( "Airplane" ):FilterStart() +-- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() +-- AICargoDispatcher = AI_CARGO_DISPATCHER_AIRPLANE:New( SetAirplane, SetCargo ) +-- +function AI_CARGO_DISPATCHER_AIRPLANE:New( SetAirplane, SetCargo, SetDeployZones ) + + local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetAirplane, SetCargo, SetDeployZones ) ) -- #AI_CARGO_DISPATCHER_AIRPLANE + + return self +end + + diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua new file mode 100644 index 000000000..48b8bdd4c --- /dev/null +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -0,0 +1,54 @@ +--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo using Helicopters. +-- +-- === +-- +-- ### Author: **FlightControl** +-- +-- === +-- +-- @module AI_Cargo_Dispatcher_Helicopter + +--- @type AI_CARGO_DISPATCHER_HELICOPTER +-- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER + + +--- # AI\_CARGO\_DISPATCHER\_HELICOPTER class, extends @{Core.Base#BASE} +-- +-- === +-- +-- AI\_CARGO\_DISPATCHER\_HELICOPTER brings a dynamic cargo handling capability for AI groups. +-- +-- Helicopters can be mobilized to intelligently transport infantry and other cargo within the simulation. +-- The AI\_CARGO\_DISPATCHER\_HELICOPTER module uses the @{Cargo} capabilities within the MOOSE framework. +-- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER\_HELICOPTER object recognize the cargo. +-- Please consult the @{Cargo} module for more information. +-- +-- +-- +-- @field #AI_CARGO_DISPATCHER_HELICOPTER +AI_CARGO_DISPATCHER_HELICOPTER = { + ClassName = "AI_CARGO_DISPATCHER_HELICOPTER", +} + +--- Creates a new AI_CARGO_DISPATCHER_HELICOPTER object. +-- @param #AI_CARGO_DISPATCHER_HELICOPTER self +-- @param Core.Set#SET_GROUP SetAPC +-- @param Core.Set#SET_CARGO SetCargo +-- @param Core.Set#SET_ZONE SetDeployZone +-- @return #AI_CARGO_DISPATCHER_HELICOPTER +-- @usage +-- +-- -- Create a new cargo dispatcher +-- SetHelicopter = SET_GROUP:New():FilterPrefixes( "Helicopter" ):FilterStart() +-- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() +-- AICargoDispatcher = AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargo ) +-- +function AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargo, SetDeployZones ) + + local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetHelicopter, SetCargo, SetDeployZones ) ) -- #AI_CARGO_DISPATCHER_HELICOPTER + + return self +end + + diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 501dd0e5e..c7580db68 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -1149,6 +1149,7 @@ function DATABASE:_RegisterTemplates() local ZoneName = ZoneData.name self.ZONENAMES[ZoneName] = ZoneName self.ZONES[ZoneName] = ZONE:New( ZoneName ) + self:I( "Added ZONE " .. ZoneName ) end return self diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index b8043328d..b29d1d6fa 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -4350,6 +4350,20 @@ function SET_CARGO:FirstCargoWithState( State ) return FirstCargo end +function SET_CARGO:FirstCargoWithStateAndNotDeployed( State ) + + local FirstCargo = nil + + for CargoName, Cargo in pairs( self.Set ) do + if Cargo:Is( State ) and not Cargo:IsDeployed() then + FirstCargo = Cargo + break + end + end + + return FirstCargo +end + --- Iterate the SET_CARGO while identifying the first @{Cargo#CARGO} that is UnLoaded. -- @param #SET_CARGO self @@ -4360,6 +4374,15 @@ function SET_CARGO:FirstCargoUnLoaded() end +--- Iterate the SET_CARGO while identifying the first @{Cargo#CARGO} that is UnLoaded and not Deployed. +-- @param #SET_CARGO self +-- @return Cargo.Cargo#CARGO The first @{Cargo#CARGO}. +function SET_CARGO:FirstCargoUnLoadedAndNotDeployed() + local FirstCargo = self:FirstCargoWithStateAndNotDeployed( "UnLoaded" ) + return FirstCargo +end + + --- Iterate the SET_CARGO while identifying the first @{Cargo#CARGO} that is Loaded. -- @param #SET_CARGO self -- @return Cargo.Cargo#CARGO The first @{Cargo#CARGO}. @@ -4588,7 +4611,7 @@ function SET_ZONE:GetRandomZone() local Index = self.Index local ZoneFound = nil - while not ZoneFound do + while not ZoneFound do local ZoneRandom = math.random( 1, #Index ) ZoneFound = self.Set[Index[ZoneRandom]] end diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index 90e550a13..ea44b6326 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -70,9 +70,12 @@ AI/AI_Cas.lua AI/AI_Bai.lua AI/AI_Formation.lua AI/AI_Cargo_APC.lua -AI/AI_Cargo_Dispatcher_APC.lua AI/AI_Cargo_Helicopter.lua AI/AI_Cargo_Airplane.lua +AI/AI_Cargo_Dispatcher.lua +AI/AI_Cargo_Dispatcher_APC.lua +AI/AI_Cargo_Dispatcher_Helicopter.lua +AI/AI_Cargo_Dispatcher_Airplane.lua Actions/Act_Assign.lua Actions/Act_Route.lua From aac5efbf613c347ff13378fc676120b9504a4713 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 10 May 2018 22:48:14 +0200 Subject: [PATCH 098/420] ARTY added shots to report output --- Moose Development/Moose/Functional/Artillery.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 8849c68e0..4a00da6e5 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -936,9 +936,9 @@ function ARTY:_OnEventShot(EventData) self.Nshots=self.Nshots+1 -- Debug output. - local text=string.format("Group %s fired shot %d of %d with weapon %s on target %s.", self.Controllable:GetName(), self.Nshots, self.currentTarget.nshells, _weaponName, self.currentTarget.name) + local text=string.format("%s, fired shot %d of %d with weapon %s on target %s.", self.Controllable:GetName(), self.Nshots, self.currentTarget.nshells, _weaponName, self.currentTarget.name) self:T(ARTY.id..text) - MESSAGE:New(text, 5):ToAllIf(self.Debug) + MESSAGE:New(text, 5):ToAllIf(self.report or self.Debug) -- Get current ammo. local _nammo,_nshells,_nrockets,_nmissiles=self:GetAmmo() From bca964aca85306a9702d66a2a9ae81e7ff1826ab Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Thu, 10 May 2018 23:17:06 +0200 Subject: [PATCH 099/420] Helicopter version of AI_CARGO_DISPATCHER_HELICOPTER --- .../Moose/AI/AI_Cargo_Dispatcher.lua | 28 +++-- .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 8 ++ .../AI/AI_Cargo_Dispatcher_Helicopter.lua | 8 +- .../Moose/AI/AI_Cargo_Helicopter.lua | 105 +++++++++--------- 4 files changed, 82 insertions(+), 67 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index eeaffec9f..91e500fbf 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -64,7 +64,7 @@ function AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZones ) self.SetCargo = SetCargo -- Core.Set#SET_CARGO self.SetDeployZones = SetDeployZones -- Core.Set#SET_ZONE - self:SetStartState( "APC" ) + self:SetStartState( "Dispatch" ) self:AddTransition( "*", "Monitor", "*" ) @@ -76,13 +76,10 @@ function AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZones ) self:AddTransition( "*", "Unloading", "*" ) self:AddTransition( "*", "Unloaded", "*" ) - self.MonitorTimeInterval = 120 - self.CombatRadius = 500 + self.MonitorTimeInterval = 30 self.DeployRadiusInner = 200 self.DeployRadiusOuter = 500 - self:Monitor( 1 ) - return self end @@ -93,21 +90,22 @@ end -- @return #AI_CARGO_DISPATCHER function AI_CARGO_DISPATCHER:onafterMonitor() - for APCGroupName, APC in pairs( self.SetAPC:GetSet() ) do - local APC = APC -- Wrapper.Group#GROUP - local AICargoAPC = self.AICargoAPC[APC] + for APCGroupName, Carrier in pairs( self.SetAPC:GetSet() ) do + local Carrier = Carrier -- Wrapper.Group#GROUP + local AICargoAPC = self.AICargoAPC[Carrier] if not AICargoAPC then + -- ok, so this APC does not have yet an AI_CARGO_APC object... -- let's create one and also declare the Loaded and UnLoaded handlers. - self.AICargoAPC[APC] = AI_CARGO_APC:New( APC, self.SetCargo, self.CombatRadius ) - AICargoAPC = self.AICargoAPC[APC] + self.AICargoAPC[Carrier] = self:AICargo( Carrier, self.SetCargo, self.CombatRadius ) + AICargoAPC = self.AICargoAPC[Carrier] function AICargoAPC.OnAfterPickup( AICargoAPC, APC, From, Event, To, Cargo ) self:Pickup( APC, Cargo ) end function AICargoAPC.OnAfterLoad( AICargoAPC, APC ) - self:Load( APC ) + self:Loading( APC ) end function AICargoAPC.OnAfterLoaded( AICargoAPC, APC, From, Event, To, Cargo ) @@ -119,7 +117,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() end function AICargoAPC.OnAfterUnload( AICargoAPC, APC ) - self:Unload( APC ) + self:Unloading( APC ) end function AICargoAPC.OnAfterUnloaded( AICargoAPC, APC ) @@ -139,14 +137,14 @@ function AI_CARGO_DISPATCHER:onafterMonitor() for CargoName, Cargo in pairs( self.SetCargo:GetSet() ) do if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then if not self.PickupCargo[Cargo] then - self.PickupCargo[Cargo] = APC + self.PickupCargo[Cargo] = Carrier PickupCargo = Cargo break end end end if PickupCargo then - AICargoAPC:Pickup( PickupCargo:GetCoordinate(), 70 ) + AICargoAPC:Pickup( PickupCargo:GetCoordinate():GetRandomCoordinateInRadius( 25, 50 ), 70 ) break end end @@ -177,7 +175,7 @@ function AI_CARGO_DISPATCHER:OnAfterLoaded( From, Event, To, APC, Cargo ) local RandomZone = self.SetDeployZones:GetRandomZone() self:I( { RandomZone = RandomZone } ) - self.AICargoAPC[APC]:Deploy( RandomZone:GetCoordinate(), 70 ) + self.AICargoAPC[APC]:Deploy( RandomZone:GetCoordinate():GetRandomCoordinateInRadius(25,100), 70 ) self.PickupCargo[Cargo] = nil return self diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index eb3c0d4f7..67d2a2c92 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -48,7 +48,15 @@ function AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZones ) local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZones ) ) -- #AI_CARGO_DISPATCHER_APC + self.CombatRadius = 500 + + self:Monitor( 1 ) + return self end +function AI_CARGO_DISPATCHER_APC:AICargo( APC, SetCargo ) + + return AI_CARGO_APC:New( APC, SetCargo, self.CombatRadius ) +end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index 48b8bdd4c..56c4dae78 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -32,7 +32,7 @@ AI_CARGO_DISPATCHER_HELICOPTER = { --- Creates a new AI_CARGO_DISPATCHER_HELICOPTER object. -- @param #AI_CARGO_DISPATCHER_HELICOPTER self --- @param Core.Set#SET_GROUP SetAPC +-- @param Core.Set#SET_GROUP SetHelicopter -- @param Core.Set#SET_CARGO SetCargo -- @param Core.Set#SET_ZONE SetDeployZone -- @return #AI_CARGO_DISPATCHER_HELICOPTER @@ -48,7 +48,13 @@ function AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargo, SetDeployZ local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetHelicopter, SetCargo, SetDeployZones ) ) -- #AI_CARGO_DISPATCHER_HELICOPTER + self:Monitor( 1 ) + return self end +function AI_CARGO_DISPATCHER_HELICOPTER:AICargo( Helicopter, SetCargo ) + + return AI_CARGO_HELICOPTER:New( Helicopter, SetCargo ) +end diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index c993755ee..e0d9d2e10 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -113,6 +113,16 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) return self end +function AI_CARGO_HELICOPTER:IsTransporting() + + return self.Transporting == true +end + +function AI_CARGO_HELICOPTER:IsRelocating() + + return self.Relocating == true +end + --- Set the Carrier. -- @param #AI_CARGO_HELICOPTER self @@ -169,31 +179,6 @@ function AI_CARGO_HELICOPTER:SetCarrier( Helicopter ) end ---- Find a free Carrier within a range. --- @param #AI_CARGO_HELICOPTER self --- @param Core.Point#COORDINATE Coordinate --- @param #number Radius --- @return Wrapper.Group#GROUP NewCarrier -function AI_CARGO_HELICOPTER:FindCarrier( Coordinate, Radius ) - - local CoordinateZone = ZONE_RADIUS:New( "Zone" , Coordinate:GetVec2(), Radius ) - CoordinateZone:Scan( { Object.Category.UNIT } ) - for _, DCSUnit in pairs( CoordinateZone:GetScannedUnits() ) do - local NearUnit = UNIT:Find( DCSUnit ) - self:F({NearUnit=NearUnit}) - if not NearUnit:GetState( NearUnit, "AI_CARGO_HELICOPTER" ) then - local Attributes = NearUnit:GetDesc() - self:F({Attributes=Attributes}) - if NearUnit:HasAttribute( "Trucks" ) then - return NearUnit:GetGroup() - end - end - end - - return nil - -end - --- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -- @param From @@ -208,11 +193,14 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To ) if self.RoutePickup == true then self:Load( Helicopter:GetPointVec2() ) self.RoutePickup = false + self.Relocating = true end if self.RouteDeploy == true then - self:Unload() + self:Unload( true ) self.RouteDeploy = false + self.Transporting = false + self.Relocating = false end end @@ -230,7 +218,9 @@ end -- @param #number Speed function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordinate, Speed ) - if Helicopter and Helicopter:IsAlive() then + if Helicopter and Helicopter:IsAlive() ~= nil then + + Helicopter:Activate() self.RoutePickup = true @@ -270,7 +260,9 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin Route[#Route].task = Helicopter:TaskCombo( Tasks ) -- Now route the helicopter - Helicopter:Route( Route, 0.5 ) + Helicopter:Route( Route, 1 ) + + self.Transporting = true end end @@ -285,7 +277,7 @@ end -- @param #number Speed function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordinate, Speed ) - if Helicopter and Helicopter:IsAlive() then + if Helicopter and Helicopter:IsAlive() ~= nil then self.RouteDeploy = true @@ -325,7 +317,7 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin Route[#Route].task = Helicopter:TaskCombo( Tasks ) -- Now route the helicopter - Helicopter:Route( Route, 0.5 ) + Helicopter:Route( Route, 1 ) end end @@ -373,14 +365,15 @@ end --- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -function AI_CARGO_HELICOPTER:onafterBoard( Helicopter, From, Event, To ) +function AI_CARGO_HELICOPTER:onafterBoard( Helicopter, From, Event, To, Cargo ) + self:F( { APC, From, Event, To, Cargo } ) if Helicopter and Helicopter:IsAlive() then - self:F({ IsLoaded = self.Cargo:IsLoaded() } ) - if not self.Cargo:IsLoaded() then - self:__Board( 10 ) + self:F({ IsLoaded = Cargo:IsLoaded() } ) + if not Cargo:IsLoaded() then + self:__Board( 10, Cargo ) else - self:__Loaded( 1 ) + self:__Loaded( 1, Cargo ) end end @@ -388,12 +381,13 @@ end --- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -function AI_CARGO_HELICOPTER:onbeforeLoaded( Helicopter, From, Event, To ) +function AI_CARGO_HELICOPTER:onbeforeLoaded( Helicopter, From, Event, To, Cargo ) + self:F( { APC, From, Event, To } ) local Loaded = true if Helicopter and Helicopter:IsAlive() then - for HelicopterUnit, Cargo in pairs( self.APC_Cargo ) do + for HelicopterUnit, Cargo in pairs( self.Helicopter_Cargo ) do local Cargo = Cargo -- Cargo.Cargo#CARGO self:F( { IsLoaded = Cargo:IsLoaded(), IsDestroyed = Cargo:IsDestroyed() } ) if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then @@ -410,14 +404,14 @@ end --- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -function AI_CARGO_HELICOPTER:onafterUnload( Helicopter, From, Event, To ) +function AI_CARGO_HELICOPTER:onafterUnload( Helicopter, From, Event, To, Deployed ) if Helicopter and Helicopter:IsAlive() then for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT for _, Cargo in pairs( HelicopterUnit:GetCargo() ) do Cargo:UnBoard() - self:__Unboard( 10, Cargo ) + self:__Unboard( 10, Cargo, Deployed ) end end end @@ -427,13 +421,13 @@ end --- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -function AI_CARGO_HELICOPTER:onafterUnboard( Helicopter, From, Event, To ) +function AI_CARGO_HELICOPTER:onafterUnboard( Helicopter, From, Event, To, Cargo, Deployed ) if Helicopter and Helicopter:IsAlive() then - if not self.Cargo:IsUnLoaded() then - self:__Unboard( 10 ) + if not Cargo:IsUnLoaded() then + self:__Unboard( 10, Cargo, Deployed ) else - self:__Unloaded( 1 ) + self:__Unloaded( 1, Cargo, Deployed ) end end @@ -441,25 +435,34 @@ end --- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -function AI_CARGO_HELICOPTER:onbeforeUnloaded( Helicopter, From, Event, To ) +function AI_CARGO_HELICOPTER:onbeforeUnloaded( Helicopter, From, Event, To, Cargo, Deployed ) + self:F( { APC, From, Event, To, Cargo:GetName(), Deployed = Deployed } ) local AllUnloaded = true --Cargo:Regroup() if Helicopter and Helicopter:IsAlive() then - for _, CargoCheck in pairs( self.CargoSet:GetSet() ) do - local CargoCheck = CargoCheck -- Cargo.Cargo#CARGO - self:F( { CargoCheck:GetName(), IsUnLoaded = CargoCheck:IsUnLoaded() } ) - if CargoCheck:IsUnLoaded() == false then - AllUnloaded = false - break + for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do + local CargoCheck = self.Helicopter_Cargo[HelicopterUnit] -- Cargo.Cargo#CARGO + if CargoCheck then + self:F( { CargoCheck:GetName(), IsUnLoaded = CargoCheck:IsUnLoaded() } ) + if CargoCheck:IsUnLoaded() == false then + AllUnloaded = false + break + end end end if AllUnloaded == true then + if Deployed == true then + for HelicopterUnit, Cargo in pairs( self.Helicopter_Cargo ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + Cargo:SetDeployed( true ) + end + self.Helicopter_Cargo = {} + end self.Helicopter = Helicopter - self.Helicopter_Cargo = {} end end From 74668bb7dbf8b8981a8a661bbb2bcbb749a6753e Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Thu, 10 May 2018 23:42:14 +0200 Subject: [PATCH 100/420] Fixes --- .../Moose/AI/AI_Cargo_Dispatcher.lua | 2 +- .../Moose/AI/AI_Cargo_Helicopter.lua | 40 ++++++++++--------- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 91e500fbf..9f4b1fdce 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -175,7 +175,7 @@ function AI_CARGO_DISPATCHER:OnAfterLoaded( From, Event, To, APC, Cargo ) local RandomZone = self.SetDeployZones:GetRandomZone() self:I( { RandomZone = RandomZone } ) - self.AICargoAPC[APC]:Deploy( RandomZone:GetCoordinate():GetRandomCoordinateInRadius(25,100), 70 ) + self.AICargoAPC[APC]:Deploy( RandomZone:GetCoordinate():GetRandomCoordinateInRadius( 25, 200 ), 70 ) self.PickupCargo[Cargo] = nil return self diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index e0d9d2e10..db578e33d 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -227,17 +227,17 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin local Route = {} --- Calculate the target route point. - local CoordinateFrom = Helicopter:GetCoordinate() + --local CoordinateFrom = Helicopter:GetCoordinate() local CoordinateTo = Coordinate --- Create a route point of type air. - local WaypointFrom = CoordinateFrom:WaypointAir( - "RADIO", - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - Speed, - true - ) +-- local WaypointFrom = CoordinateFrom:WaypointAir( +-- "RADIO", +-- POINT_VEC3.RoutePointType.TurningPoint, +-- POINT_VEC3.RoutePointAction.TurningPoint, +-- Speed, +-- true +-- ) --- Create a route point of type air. local WaypointTo = CoordinateTo:WaypointAir( @@ -248,7 +248,7 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin true ) - Route[#Route+1] = WaypointFrom +-- Route[#Route+1] = WaypointFrom Route[#Route+1] = WaypointTo --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... @@ -259,6 +259,8 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() ) Route[#Route].task = Helicopter:TaskCombo( Tasks ) + Route[#Route+1] = WaypointTo + -- Now route the helicopter Helicopter:Route( Route, 1 ) @@ -284,17 +286,17 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin local Route = {} --- Calculate the target route point. - local CoordinateFrom = Helicopter:GetCoordinate() +-- local CoordinateFrom = Helicopter:GetCoordinate() local CoordinateTo = Coordinate --- Create a route point of type air. - local WaypointFrom = CoordinateFrom:WaypointAir( - "RADIO", - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - Speed, - true - ) +-- local WaypointFrom = CoordinateFrom:WaypointAir( +-- "RADIO", +-- POINT_VEC3.RoutePointType.TurningPoint, +-- POINT_VEC3.RoutePointAction.TurningPoint, +-- Speed, +-- true +-- ) --- Create a route point of type air. local WaypointTo = CoordinateTo:WaypointAir( @@ -305,7 +307,7 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin true ) - Route[#Route+1] = WaypointFrom +-- Route[#Route+1] = WaypointFrom Route[#Route+1] = WaypointTo --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... @@ -316,6 +318,8 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() ) Route[#Route].task = Helicopter:TaskCombo( Tasks ) + Route[#Route+1] = WaypointTo + -- Now route the helicopter Helicopter:Route( Route, 1 ) end From d61840d8343acb940deb3331037496b671cacf69 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Fri, 11 May 2018 12:27:55 +0200 Subject: [PATCH 101/420] Added Big Smoke Effect Added big smoke effects to COORDINATE API. --- Moose Development/Moose/Core/Point.lua | 82 +++++++++++++++++++++ Moose Development/Moose/Utilities/Utils.lua | 13 ++++ 2 files changed, 95 insertions(+) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 8ef92dc38..3385b1311 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1013,6 +1013,88 @@ do -- COORDINATE self:Smoke( SMOKECOLOR.Blue ) end + --- Big smoke and fire at the coordinate. + -- @param #COORDINATE self + -- @param Utilities.Utils#BIGSMOKEPRESET preset Smoke preset (1=small smoke and fire, 2=medium smoke and fire, 3=large smoke and fire, 4=huge smoke and fire, 5=small smoke, 6=medium smoke, 7=large smoke, 8=huge smoke). + -- @param #number density (Optional) Smoke density. Number in [0,...,1]. Default 0.5. + function COORDINATE:BigSmokeAndFire( preset, density ) + self:F2( { preset=preset, density=density } ) + density=density or 0.5 + trigger.action.effectSmokeBig( self:GetVec3(), preset, density ) + end + + --- Small smoke and fire at the coordinate. + -- @param #COORDINATE self + -- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. + function COORDINATE:BigSmokeAndFireSmall( density ) + self:F2( { density=density } ) + density=density or 0.5 + self:BigSmokeAndFire(BIGSMOKEPRESET.SmallSmokeAndFire, density) + end + + --- Medium smoke and fire at the coordinate. + -- @param #COORDINATE self + -- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. + function COORDINATE:BigSmokeAndFireMedium( density ) + self:F2( { density=density } ) + density=density or 0.5 + self:BigSmokeAndFire(BIGSMOKEPRESET.MediumSmokeAndFire, density) + end + + --- Large smoke and fire at the coordinate. + -- @param #COORDINATE self + -- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. + function COORDINATE:BigSmokeAndFireLarge( density ) + self:F2( { density=density } ) + density=density or 0.5 + self:BigSmokeAndFire(BIGSMOKEPRESET.LargeSmokeAndFire, density) + end + + --- Huge smoke and fire at the coordinate. + -- @param #COORDINATE self + -- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. + function COORDINATE:BigSmokeAndFireHuge( density ) + self:F2( { density=density } ) + density=density or 0.5 + self:BigSmokeAndFire(BIGSMOKEPRESET.HugeSmokeAndFire, density) + end + + --- Small smoke at the coordinate. + -- @param #COORDINATE self + -- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. + function COORDINATE:BigSmokeSmall( density ) + self:F2( { density=density } ) + density=density or 0.5 + self:BigSmokeAndFire(BIGSMOKEPRESET.SmallSmoke, density) + end + + --- Medium smoke at the coordinate. + -- @param #COORDINATE self + -- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. + function COORDINATE:BigSmokeMedium( density ) + self:F2( { density=density } ) + density=density or 0.5 + self:BigSmokeAndFire(BIGSMOKEPRESET.MediumSmoke, density) + end + + --- Large smoke at the coordinate. + -- @param #COORDINATE self + -- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. + function COORDINATE:BigSmokeLarge( density ) + self:F2( { density=density } ) + density=density or 0.5 + self:BigSmokeAndFire(BIGSMOKEPRESET.LargeSmoke, density) + end + + --- Huge smoke at the coordinate. + -- @param #COORDINATE self + -- @number density (Optional) Smoke density. Number between 0 and 1. Default 0.5. + function COORDINATE:BigSmokeHuge( density ) + self:F2( { density=density } ) + density=density or 0.5 + self:BigSmokeAndFire(BIGSMOKEPRESET.HugeSmoke, density) + end + --- Flares the point in a color. -- @param #COORDINATE self -- @param Utilities.Utils#FLARECOLOR FlareColor diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index e7f730ad1..7ffb6dd44 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -29,6 +29,19 @@ SMOKECOLOR = trigger.smokeColor -- #SMOKECOLOR FLARECOLOR = trigger.flareColor -- #FLARECOLOR +--- Big smoke preset enum. +-- @type BIGSMOKEPRESET +BIGSMOKEPRESET = { + SmallSmokeAndFire=0, + MediumSmokeAndFire=1, + LargeSmokeAndFire=2, + HugeSmokeAndFire=3, + SmallSmoke=4, + MediumSmoke=5, + LargeSmoke=6, + HugeSmoke=7, +} + --- Utilities static class. -- @type UTILS UTILS = { From 230a803045f3e0ccd4ad8d6243f34019434ec193 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Fri, 11 May 2018 12:30:33 +0200 Subject: [PATCH 102/420] Corrected description. --- Moose Development/Moose/Core/Point.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 3385b1311..5060600d6 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1015,7 +1015,7 @@ do -- COORDINATE --- Big smoke and fire at the coordinate. -- @param #COORDINATE self - -- @param Utilities.Utils#BIGSMOKEPRESET preset Smoke preset (1=small smoke and fire, 2=medium smoke and fire, 3=large smoke and fire, 4=huge smoke and fire, 5=small smoke, 6=medium smoke, 7=large smoke, 8=huge smoke). + -- @param Utilities.Utils#BIGSMOKEPRESET preset Smoke preset (0=small smoke and fire, 1=medium smoke and fire, 2=large smoke and fire, 3=huge smoke and fire, 4=small smoke, 5=medium smoke, 6=large smoke, 7=huge smoke). -- @param #number density (Optional) Smoke density. Number in [0,...,1]. Default 0.5. function COORDINATE:BigSmokeAndFire( preset, density ) self:F2( { preset=preset, density=density } ) From 25a6cbcf6dfe125ba2b34b68b2821c2673e327f4 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sat, 12 May 2018 08:37:27 +0200 Subject: [PATCH 103/420] Helicopter AI_CARGO_DISPATCHER working (almost). Queueing at deploy locations working. --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 33 +- .../Moose/AI/AI_Cargo_Dispatcher.lua | 89 +++- .../Moose/AI/AI_Cargo_Helicopter.lua | 400 ++++++++++++++---- Moose Development/Moose/Cargo/CargoGroup.lua | 30 ++ Moose Development/Moose/Core/Database.lua | 48 ++- Moose Development/Moose/Core/Zone.lua | 13 + .../Moose/Wrapper/Controllable.lua | 7 +- 7 files changed, 502 insertions(+), 118 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 9e4351bee..b5d0a3f8a 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -108,6 +108,7 @@ function AI_CARGO_APC:New( CargoCarrier, CargoSet, CombatRadius ) self:AddTransition( "*", "Monitor", "*" ) self:AddTransition( "*", "Follow", "Following" ) self:AddTransition( "*", "Guard", "Unloaded" ) + self:AddTransition( "*", "Home", "*" ) self:AddTransition( "*", "Destroyed", "Destroyed" ) @@ -600,14 +601,14 @@ end -- @param Core.Point#COORDINATE Coordinate -- @param #number Speed -- @param #string EndPointFormation The formation at the end point of the action. -function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed, EndPointFormation ) +function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate ) if APC and APC:IsAlive() then if Coordinate then self.RoutePickup = true - local Waypoints = APC:TaskGroundOnRoad( Coordinate, Speed, EndPointFormation ) + local Waypoints = APC:TaskGroundOnRoad( Coordinate, 150, "Line abreast" ) local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Pickup", self ) @@ -634,13 +635,13 @@ end -- @param Core.Point#COORDINATE Coordinate -- @param #number Speed -- @param #string EndPointFormation The formation at the end point of the action. -function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed, EndPointFormation ) +function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate ) if APC and APC:IsAlive() then self.RouteDeploy = true - local Waypoints = APC:TaskGroundOnRoad( Coordinate, Speed, EndPointFormation ) + local Waypoints = APC:TaskGroundOnRoad( Coordinate, 150, "Line abreast" ) local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Deploy", self ) @@ -653,3 +654,27 @@ function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed, En end + +--- @param #AI_CARGO_HELICOPTER self +-- @param Wrapper.Group#GROUP APC +-- @param From +-- @param Event +-- @param To +-- @param Core.Point#COORDINATE Coordinate +-- @param #number Speed +function AI_CARGO_APC:onafterHome( APC, From, Event, To, Coordinate ) + + if APC and APC:IsAlive() ~= nil then + + self.RouteHome = true + + local Waypoints = APC:TaskGroundOnRoad( Coordinate, 120, "Line abreast" ) + + self:F({Waypoints = Waypoints}) + local Waypoint = Waypoints[#Waypoints] + + APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. + + end + +end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 9f4b1fdce..dd03c258e 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -37,11 +37,13 @@ AI_CARGO_DISPATCHER = { -- @map --- @field #AI_CARGO_DISPATCHER.AI_CARGO_APC -AI_CARGO_DISPATCHER.AICargoAPC = {} +AI_CARGO_DISPATCHER.AI_Cargo = {} --- @field #AI_CARGO_DISPATCHER.PickupCargo AI_CARGO_DISPATCHER.PickupCargo = {} + + --- Creates a new AI_CARGO_DISPATCHER object. -- @param #AI_CARGO_DISPATCHER self -- @param Core.Set#SET_GROUP SetAPC @@ -54,7 +56,7 @@ AI_CARGO_DISPATCHER.PickupCargo = {} -- SetAPC = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() -- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() -- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() --- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo ) +-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone ) -- function AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZones ) @@ -76,14 +78,42 @@ function AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZones ) self:AddTransition( "*", "Unloading", "*" ) self:AddTransition( "*", "Unloaded", "*" ) + self:AddTransition( "*", "Home", "*" ) + self.MonitorTimeInterval = 30 self.DeployRadiusInner = 200 self.DeployRadiusOuter = 500 + self.CarrierHome = {} + return self end +--- Set the home zone. +-- When there is nothing anymore to pickup, the carriers will go to a random coordinate in this zone. +-- They will await here new orders. +-- @param #AI_CARGO_DISPATCHER self +-- @param Core.Zone#ZONE_BASE HomeZone +-- @return #AI_CARGO_DISPATCHER +-- @usage +-- +-- -- Create a new cargo dispatcher +-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone ) +-- +-- -- Set the home coordinate +-- local HomeZone = ZONE:New( "Home" ) +-- AICargoDispatcher:SetHomeZone( HomeZone ) +-- +function AI_CARGO_DISPATCHER:SetHomeZone( HomeZone ) + + self.HomeZone = HomeZone + + return self +end + + + --- The Start trigger event, which actually takes action at the specified time interval. -- @param #AI_CARGO_DISPATCHER self -- @param Wrapper.Group#GROUP APC @@ -92,60 +122,78 @@ function AI_CARGO_DISPATCHER:onafterMonitor() for APCGroupName, Carrier in pairs( self.SetAPC:GetSet() ) do local Carrier = Carrier -- Wrapper.Group#GROUP - local AICargoAPC = self.AICargoAPC[Carrier] - if not AICargoAPC then + local AI_Cargo = self.AI_Cargo[Carrier] + if not AI_Cargo then -- ok, so this APC does not have yet an AI_CARGO_APC object... -- let's create one and also declare the Loaded and UnLoaded handlers. - self.AICargoAPC[Carrier] = self:AICargo( Carrier, self.SetCargo, self.CombatRadius ) - AICargoAPC = self.AICargoAPC[Carrier] + self.AI_Cargo[Carrier] = self:AICargo( Carrier, self.SetCargo, self.CombatRadius ) + AI_Cargo = self.AI_Cargo[Carrier] - function AICargoAPC.OnAfterPickup( AICargoAPC, APC, From, Event, To, Cargo ) + function AI_Cargo.OnAfterPickup( AI_Cargo, APC, From, Event, To, Cargo ) self:Pickup( APC, Cargo ) end - function AICargoAPC.OnAfterLoad( AICargoAPC, APC ) + function AI_Cargo.OnAfterLoad( AI_Cargo, APC ) self:Loading( APC ) end - function AICargoAPC.OnAfterLoaded( AICargoAPC, APC, From, Event, To, Cargo ) + function AI_Cargo.OnAfterLoaded( AI_Cargo, APC, From, Event, To, Cargo ) self:Loaded( APC, Cargo ) end - function AICargoAPC.OnAfterDeploy( AICargoAPC, APC ) + function AI_Cargo.OnAfterDeploy( AI_Cargo, APC ) self:Deploy( APC ) end - function AICargoAPC.OnAfterUnload( AICargoAPC, APC ) + function AI_Cargo.OnAfterUnload( AI_Cargo, APC ) self:Unloading( APC ) end - function AICargoAPC.OnAfterUnloaded( AICargoAPC, APC ) + function AI_Cargo.OnAfterUnloaded( AI_Cargo, APC ) self:Unloaded( APC ) end end -- The Pickup sequence ... -- Check if this APC need to go and Pickup something... - self:I( { IsTransporting = AICargoAPC:IsTransporting() } ) - if AICargoAPC:IsTransporting() == false then + self:I( { IsTransporting = AI_Cargo:IsTransporting() } ) + if AI_Cargo:IsTransporting() == false then -- ok, so there is a free APC -- now find the first cargo that is Unloaded local PickupCargo = nil for CargoName, Cargo in pairs( self.SetCargo:GetSet() ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + self:F( { Cargo = Cargo:GetName(), UnLoaded = Cargo:IsUnLoaded(), Deployed = Cargo:IsDeployed(), PickupCargo = self.PickupCargo[Cargo] ~= nil } ) if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then - if not self.PickupCargo[Cargo] then - self.PickupCargo[Cargo] = Carrier + local CargoVec2 = { x = Cargo:GetX(), y = Cargo:GetY() } + local LocationFound = false + for APC, Vec2 in pairs( self.PickupCargo ) do + if Vec2.x == CargoVec2.x and Vec2.y == CargoVec2.y then + LocationFound = true + break + end + end + if LocationFound == false then + self.PickupCargo[Carrier] = CargoVec2 PickupCargo = Cargo break end end end if PickupCargo then - AICargoAPC:Pickup( PickupCargo:GetCoordinate():GetRandomCoordinateInRadius( 25, 50 ), 70 ) + self.CarrierHome[Carrier] = nil + AI_Cargo:Pickup( PickupCargo:GetCoordinate() ) break + else + if self.HomeZone then + if not self.CarrierHome[Carrier] then + self.CarrierHome[Carrier] = true + AI_Cargo:Home( self.HomeZone:GetRandomPointVec2() ) + end + end end end end @@ -175,9 +223,10 @@ function AI_CARGO_DISPATCHER:OnAfterLoaded( From, Event, To, APC, Cargo ) local RandomZone = self.SetDeployZones:GetRandomZone() self:I( { RandomZone = RandomZone } ) - self.AICargoAPC[APC]:Deploy( RandomZone:GetCoordinate():GetRandomCoordinateInRadius( 25, 200 ), 70 ) - self.PickupCargo[Cargo] = nil - + self.AI_Cargo[APC]:Deploy( RandomZone:GetCoordinate(), 70 ) + + self.PickupCargo[APC] = nil + return self end diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index db578e33d..695deb248 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -22,6 +22,8 @@ AI_CARGO_HELICOPTER = { Coordinate = nil -- Core.Point#COORDINATE, } +AI_CARGO_HELICOPTER_QUEUE = {} + --- Creates a new AI_CARGO_HELICOPTER object. -- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter @@ -33,6 +35,8 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_CARGO_HELICOPTER self.CargoSet = CargoSet -- Cargo.CargoGroup#CARGO_GROUP + + self.Zone = ZONE_GROUP:New( Helicopter:GetName(), Helicopter, 300 ) self:SetStartState( "Unloaded" ) @@ -47,6 +51,9 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) self:AddTransition( "Unboarding", "Unloaded", "Unloaded" ) self:AddTransition( "*", "Landed", "*" ) + self:AddTransition( "*", "Queue", "*" ) + self:AddTransition( "*", "Orbit" , "*" ) + self:AddTransition( "*", "Home" , "*" ) self:AddTransition( "*", "Destroyed", "Destroyed" ) @@ -207,8 +214,6 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To ) end - - --- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -- @param From @@ -216,57 +221,81 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate -- @param #number Speed -function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordinate, Speed ) +function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordinate ) - if Helicopter and Helicopter:IsAlive() ~= nil then + local HelicopterInZone = false - Helicopter:Activate() - - self.RoutePickup = true - - local Route = {} - - --- Calculate the target route point. - --local CoordinateFrom = Helicopter:GetCoordinate() - local CoordinateTo = Coordinate - - --- Create a route point of type air. --- local WaypointFrom = CoordinateFrom:WaypointAir( --- "RADIO", --- POINT_VEC3.RoutePointType.TurningPoint, --- POINT_VEC3.RoutePointAction.TurningPoint, --- Speed, --- true --- ) - - --- Create a route point of type air. - local WaypointTo = CoordinateTo:WaypointAir( - "RADIO", - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - Speed, - true - ) - --- Route[#Route+1] = WaypointFrom - Route[#Route+1] = WaypointTo - - --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... - Helicopter:WayPointInitialize( Route ) + --- @param Wrapper.Unit#UNIT ZoneUnit + local function EvaluateZone( ZoneUnit ) - local Tasks = {} + if ZoneUnit:IsAlive() then + local ZoneUnitCategory = ZoneUnit:GetDesc().category + local ZoneGroup = ZoneUnit:GetGroup() + if ZoneUnitCategory == Unit.Category.HELICOPTER then + local State = ZoneGroup:GetState( ZoneGroup, "Landing" ) + self:F({ZoneUnit=ZoneUnit:GetName(), State=State, UnitCategory = Unit.Category.HELICOPTER } ) + if State == true then + HelicopterInZone = true + return false + end + end + end - Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() ) - Route[#Route].task = Helicopter:TaskCombo( Tasks ) - - Route[#Route+1] = WaypointTo - - -- Now route the helicopter - Helicopter:Route( Route, 1 ) - - self.Transporting = true + return true + end + + if Helicopter and Helicopter:IsAlive() then + + local Distance = Coordinate:DistanceFromPointVec2( Helicopter:GetCoordinate() ) + + if Distance > 300 then + self:__Queue( -10, Coordinate ) + else + + -- This will search the zone and will call the local function "EvaluateZone", which passes a UNIT object. + local Zone = ZONE_RADIUS:New( "Deploy", Coordinate:GetVec2(), 300 ) + Zone:SearchZone( EvaluateZone ) + + self:F({HelicopterInZone=HelicopterInZone}) + + if HelicopterInZone == false then + + Helicopter:SetState( Helicopter, "Landing", true ) + + local Route = {} + +-- local CoordinateFrom = Helicopter:GetCoordinate() +-- local WaypointFrom = CoordinateFrom:WaypointAir( +-- "RADIO", +-- POINT_VEC3.RoutePointType.TurningPoint, +-- POINT_VEC3.RoutePointAction.TurningPoint, +-- Speed, +-- true +-- ) +-- Route[#Route+1] = WaypointFrom + local CoordinateTo = Coordinate + local WaypointTo = CoordinateTo:WaypointAir( + "RADIO", + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + 50, + true + ) + Route[#Route+1] = WaypointTo + + local Tasks = {} + Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() ) + Route[#Route].task = Helicopter:TaskCombo( Tasks ) + + Route[#Route+1] = WaypointTo + + -- Now route the helicopter + Helicopter:Route( Route, 0 ) + else + self:__Queue( -10, Coordinate ) + end + end end - end @@ -277,56 +306,48 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate -- @param #number Speed -function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordinate, Speed ) +function AI_CARGO_HELICOPTER:onafterOrbit( Helicopter, From, Event, To, Coordinate ) - if Helicopter and Helicopter:IsAlive() ~= nil then - - self.RouteDeploy = true - - local Route = {} + if Helicopter and Helicopter:IsAlive() then - --- Calculate the target route point. --- local CoordinateFrom = Helicopter:GetCoordinate() - local CoordinateTo = Coordinate - - --- Create a route point of type air. --- local WaypointFrom = CoordinateFrom:WaypointAir( --- "RADIO", --- POINT_VEC3.RoutePointType.TurningPoint, --- POINT_VEC3.RoutePointAction.TurningPoint, --- Speed, --- true --- ) - - --- Create a route point of type air. - local WaypointTo = CoordinateTo:WaypointAir( - "RADIO", - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - Speed, - true - ) - --- Route[#Route+1] = WaypointFrom - Route[#Route+1] = WaypointTo + Helicopter:ClearState( Helicopter, "Landing" ) - --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... - Helicopter:WayPointInitialize( Route ) + if not self:IsTransporting() then + local Route = {} + + -- local CoordinateFrom = Helicopter:GetCoordinate() + -- local WaypointFrom = CoordinateFrom:WaypointAir( + -- "RADIO", + -- POINT_VEC3.RoutePointType.TurningPoint, + -- POINT_VEC3.RoutePointAction.TurningPoint, + -- Speed, + -- true + -- ) + -- Route[#Route+1] = WaypointFrom + local CoordinateTo = Coordinate + local WaypointTo = CoordinateTo:WaypointAir( + "RADIO", + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + 50, + true + ) + Route[#Route+1] = WaypointTo + + local Tasks = {} + Tasks[#Tasks+1] = Helicopter:TaskOrbitCircle( math.random( 30, 80 ), 0, CoordinateTo:GetRandomCoordinateInRadius( 800, 500 ) ) + Route[#Route].task = Helicopter:TaskCombo( Tasks ) - local Tasks = {} - - Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() ) - Route[#Route].task = Helicopter:TaskCombo( Tasks ) - - Route[#Route+1] = WaypointTo - - -- Now route the helicopter - Helicopter:Route( Route, 1 ) + Route[#Route+1] = WaypointTo + + -- Now route the helicopter + Helicopter:Route( Route, 0 ) + end end - end + --- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter function AI_CARGO_HELICOPTER:onbeforeLoad( Helicopter, From, Event, To, Coordinate ) @@ -475,4 +496,203 @@ function AI_CARGO_HELICOPTER:onbeforeUnloaded( Helicopter, From, Event, To, Carg end +--- @param #AI_CARGO_HELICOPTER self +-- @param Wrapper.Group#GROUP Helicopter +function AI_CARGO_HELICOPTER:onafterUnloaded( Helicopter, From, Event, To, Cargo, Deployed ) + + self:Orbit( Helicopter:GetCoordinate(), 50 ) + +end + +--- @param #AI_CARGO_HELICOPTER self +-- @param Wrapper.Group#GROUP Helicopter +-- @param From +-- @param Event +-- @param To +-- @param Core.Point#COORDINATE Coordinate +-- @param #number Speed +function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordinate ) + + if Helicopter and Helicopter:IsAlive() ~= nil then + + self:ScheduleOnce( 10, Helicopter.ClearState, Helicopter, Helicopter, "Landing" ) + Helicopter:Activate() + + self.RoutePickup = true + Coordinate.y = math.random( 50, 200 ) + + local Route = {} + + --- Calculate the target route point. + local CoordinateFrom = Helicopter:GetCoordinate() + local CoordinateTo = Coordinate + + --- Create a route point of type air. + local WaypointFrom = CoordinateFrom:WaypointAir( + "RADIO", + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + 150, + true + ) + + --- Create a route point of type air. + local WaypointTo = CoordinateTo:WaypointAir( + "RADIO", + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + 150, + true + ) + + Route[#Route+1] = WaypointFrom + Route[#Route+1] = WaypointTo + + --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... + Helicopter:WayPointInitialize( Route ) + + local Tasks = {} + + Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() ) + Route[#Route].task = Helicopter:TaskCombo( Tasks ) + + Route[#Route+1] = WaypointTo + + -- Now route the helicopter + Helicopter:Route( Route, 1 ) + + self.Transporting = true + end + +end + + +function AI_CARGO_HELICOPTER:_Deploy( AICargoHelicopter, Coordinate ) + AICargoHelicopter:__Queue( -10, Coordinate, 100 ) +end + +--- @param #AI_CARGO_HELICOPTER self +-- @param Wrapper.Group#GROUP Helicopter +-- @param From +-- @param Event +-- @param To +-- @param Core.Point#COORDINATE Coordinate +-- @param #number Speed +function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordinate ) + + if Helicopter and Helicopter:IsAlive() ~= nil then + + self.RouteDeploy = true + + + local Route = {} + + --- Calculate the target route point. + + Coordinate.y = math.random( 50, 200 ) + + --- Create a route point of type air. + local CoordinateFrom = Helicopter:GetCoordinate() + local WaypointFrom = CoordinateFrom:WaypointAir( + "RADIO", + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + 150, + true + ) + Route[#Route+1] = WaypointFrom + Route[#Route+1] = WaypointFrom + + --- Create a route point of type air. + local CoordinateTo = Coordinate + local WaypointTo = CoordinateTo:WaypointAir( + "RADIO", + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + 150, + true + ) + + Route[#Route+1] = WaypointTo + Route[#Route+1] = WaypointTo + + --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... + Helicopter:WayPointInitialize( Route ) + + local Tasks = {} + + Tasks[#Tasks+1] = Helicopter:TaskFunction( "AI_CARGO_HELICOPTER._Deploy", self, Coordinate ) + Tasks[#Tasks+1] = Helicopter:TaskOrbitCircle( math.random( 30, 100 ), 0, CoordinateTo:GetRandomCoordinateInRadius( 800, 500 ) ) + + --Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() ) + Route[#Route].task = Helicopter:TaskCombo( Tasks ) + + Route[#Route+1] = WaypointTo + + -- Now route the helicopter + Helicopter:Route( Route, 1 ) + + end + +end + + +--- @param #AI_CARGO_HELICOPTER self +-- @param Wrapper.Group#GROUP Helicopter +-- @param From +-- @param Event +-- @param To +-- @param Core.Point#COORDINATE Coordinate +-- @param #number Speed +function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinate ) + + if Helicopter and Helicopter:IsAlive() ~= nil then + + self.RouteHome = true + + local Route = {} + + --- Calculate the target route point. + + Coordinate.y = math.random( 50, 200 ) + + --- Create a route point of type air. + local CoordinateFrom = Helicopter:GetCoordinate() + local WaypointFrom = CoordinateFrom:WaypointAir( + "RADIO", + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + 150, + true + ) + Route[#Route+1] = WaypointFrom + + --- Create a route point of type air. + local CoordinateTo = Coordinate + local WaypointTo = CoordinateTo:WaypointAir( + "RADIO", + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + 150, + true + ) + + Route[#Route+1] = WaypointTo + + --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... + Helicopter:WayPointInitialize( Route ) + + local Tasks = {} + + Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() ) + Route[#Route].task = Helicopter:TaskCombo( Tasks ) + + Route[#Route+1] = WaypointTo + + -- Now route the helicopter + Helicopter:Route( Route, 1 ) + + end + +end diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 1bdcbf263..4780a8f55 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -488,6 +488,36 @@ do -- CARGO_GROUP return nil end + --- Get the x position of the cargo. + -- @param #CARGO_GROUP self + -- @return #number + function CARGO:GetX() + + local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO + + if Cargo then + return Cargo:GetCoordinate().x + end + + return nil + end + + --- Get the y position of the cargo. + -- @param #CARGO_GROUP self + -- @return #number + function CARGO:GetY() + + local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO + + if Cargo then + return Cargo:GetCoordinate().z + end + + return nil + end + + + --- Check if the CargoGroup is alive. -- @param #CARGO_GROUP self -- @return #boolean true if the CargoGroup is alive. diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index c7580db68..1a57a4b2b 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -244,6 +244,52 @@ function DATABASE:FindAirbase( AirbaseName ) end +do -- Zones + + --- Finds a @{Zone} based on the zone name. + -- @param #DATABASE self + -- @param #string ZoneName The name of the zone. + -- @return Core.Zone#ZONE_BASE The found ZONE. + function DATABASE:FindZone( ZoneName ) + + local ZoneFound = self.ZONES[ZoneName] + return ZoneFound + end + + --- Adds a @{Zone} based on the zone name in the DATABASE. + -- @param #DATABASE self + -- @param #string ZoneName The name of the zone. + -- @param Core.Zone#ZONE_BASE Zone The zone. + function DATABASE:AddZone( ZoneName, Zone ) + + if not self.ZONES[ZoneName] then + self.ZONES[ZoneName] = Zone + end + end + + + --- Deletes a @{Zone} from the DATABASE based on the zone name. + -- @param #DATABASE self + -- @param #string ZoneName The name of the zone. + function DATABASE:DeleteZone( ZoneName ) + + self.ZONES[ZoneName] = nil + end + + --- Finds an @{Zone} based on the zone name in the DATABASE. + -- @param #DATABASE self + -- @param #string ZoneName + -- @return Core.Zone#ZONE_BASE The found @{Zone}. + function DATABASE:FindZone( ZoneName ) + + local ZoneFound = self.ZONES[ZoneName] + return ZoneFound + end + + + +end + do -- cargo @@ -1148,7 +1194,7 @@ function DATABASE:_RegisterTemplates() for ZoneID, ZoneData in pairs( env.mission.triggers.zones ) do local ZoneName = ZoneData.name self.ZONENAMES[ZoneName] = ZoneName - self.ZONES[ZoneName] = ZONE:New( ZoneName ) + self:AddZone( ZoneName, ZONE:New( ZoneName ) ) self:I( "Added ZONE " .. ZoneName ) end diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 8c05d8a14..36571c15b 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -114,6 +114,8 @@ function ZONE_BASE:New( ZoneName ) return self end + + --- Returns the name of the zone. -- @param #ZONE_BASE self -- @return #string The name of the zone. @@ -962,6 +964,17 @@ function ZONE:New( ZoneName ) return self end +--- Find a zone in the _DATABASE using the name of the zone. +-- @param #ZONE_BASE self +-- @param #string ZoneName The name of the zone. +-- @return #ZONE_BASE self +function ZONE:FindByName( ZoneName ) + + local ZoneFound = _DATABASE:FindZone( ZoneName ) + return ZoneFound +end + + --- @type ZONE_UNIT -- @field Wrapper.Unit#UNIT ZoneUNIT diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 738b04963..04576498f 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -798,15 +798,16 @@ end -- @param #CONTROLLABLE self -- @param #number Altitude The altitude to hold the position. -- @param #number Speed The speed flying when holding the position. +-- @param Core.Point#COORDINATE Coordinate The coordinate where to orbit. -- @return #CONTROLLABLE self -function CONTROLLABLE:TaskOrbitCircle( Altitude, Speed ) +function CONTROLLABLE:TaskOrbitCircle( Altitude, Speed, Coordinate ) self:F2( { self.ControllableName, Altitude, Speed } ) local DCSControllable = self:GetDCSObject() if DCSControllable then - local ControllablePoint = self:GetVec2() - return self:TaskOrbitCircleAtVec2( ControllablePoint, Altitude, Speed ) + local OrbitVec2 = Coordinate and Coordinate:GetVec2() or self:GetVec2() + return self:TaskOrbitCircleAtVec2( OrbitVec2, Altitude, Speed ) end return nil From 8db5351ad73c975050910d6fd356db24454ee69a Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sat, 12 May 2018 15:42:15 +0200 Subject: [PATCH 104/420] ARTY v0.9.1 Added option to not engage an already assigned target. Fixed bug in _checkname function. --- .../Moose/Functional/Artillery.lua | 80 ++++++++++++++----- 1 file changed, 59 insertions(+), 21 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 4a00da6e5..c38ee1fad 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -385,7 +385,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #string version -ARTY.version="0.9.0" +ARTY.version="0.9.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -525,12 +525,13 @@ end -- @param #string time (Optional) Day time at which the target should be engaged. Passed as a string in format "08:13:45". Current task will be canceled. -- @param #number weapontype (Optional) Type of weapon to be used to attack this target. Default ARTY.WeaponType.Auto, i.e. the DCS logic automatically determins the appropriate weapon. -- @param #string name (Optional) Name of the target. Default is LL DMS coordinate of the target. If the name was already given, the numbering "#01", "#02",... is appended automatically. +-- @param #boolean unique (Optional) Target is unique. If the target name is already known, the target is rejected. Default false. -- @return #string Name of the target. Can be used for further reference, e.g. deleting the target from the list. -- @usage paladin=ARTY:New(GROUP:FindByName("Blue Paladin")) -- paladin:AssignTargetCoord(GROUP:FindByName("Red Targets 1"):GetCoordinate(), 10, 300, 10, 1, "08:02:00", ARTY.WeaponType.Auto, "Target 1") -- paladin:Start() -function ARTY:AssignTargetCoord(coord, prio, radius, nshells, maxengage, time, weapontype, name) - self:F({coord=coord, prio=prio, radius=radius, nshells=nshells, maxengage=maxengage, time=time, weapontype=weapontype, name=name}) +function ARTY:AssignTargetCoord(coord, prio, radius, nshells, maxengage, time, weapontype, name, unique) + self:F({coord=coord, prio=prio, radius=radius, nshells=nshells, maxengage=maxengage, time=time, weapontype=weapontype, name=name, unique=unique}) -- Set default values. nshells=nshells or 5 @@ -539,13 +540,23 @@ function ARTY:AssignTargetCoord(coord, prio, radius, nshells, maxengage, time, w prio=prio or 50 prio=math.max( 1, prio) prio=math.min(100, prio) + if unique==nil then + unique=false + end weapontype=weapontype or ARTY.WeaponType.Auto -- Name of the target. local _name=name or coord:ToStringLLDMS() + local _unique=true -- Check if the name has already been used for another target. If so, the function returns a new unique name. - _name=self:_CheckName(self.targets, _name) + _name,_unique=self:_CheckName(self.targets, _name, not unique) + + -- Target name should be unique and is not. + if unique==true and _unique==false then + self:T(ARTY.id..string.format("%s: target %s should have a unique name but name was already given. Rejecting target!", self.Controllable:GetName(), _name)) + return nil + end -- Time in seconds. local _time=self:_ClockToSeconds(time) @@ -2085,44 +2096,71 @@ end ---- Check if a name is unique. If not, a new unique name is created by adding a running index #01, #02, ... +--- Check if a name is unique. If not, a new unique name can be created by adding a running index #01, #02, ... -- @param #ARTY self -- @param #table givennames Table with entries of already given names. Must contain a .name item. --- @param #string name Desired name. +-- @param #string name Name to check if it already exists in givennames table. +-- @param #boolean makeunique If true, a new unique name is returned by appending the running index. -- @return #string Unique name, which is not already given for another target. -function ARTY:_CheckName(givennames, name) +function ARTY:_CheckName(givennames, name, makeunique) self:F2({givennames=givennames, name=name}) local newname=name local counter=1 + local n=1 + local nmax=100 + if makeunique==nil then + makeunique=true + end + + repeat -- until a unique name is found. - repeat -- We assume the name is unique. - local unique=true + local _unique=true -- Loop over all targets already defined. for _,_target in pairs(givennames) do -- Target name. - local _givenname=givennames.name + local _givenname=_target.name + -- Name is already used by another target. if _givenname==newname then - -- Define new name = "name #01" - newname=string.format("%s #%02d", name, counter) - - -- Increase counter. - counter=counter+1 - + -- Name is already used for another target ==> try again with new name. - unique=false - end + _unique=false + + end + + -- Debug info. + self:T3(ARTY.id..string.format("%d: givenname = %s, newname=%s, unique = %s, makeunique = %s", n, tostring(_givenname), newname, tostring(_unique), tostring(makeunique))) end - until (unique) + -- Create a new name if requested and try again. + if _unique==false and makeunique==true then + + -- Define newname = "name #01" + newname=string.format("%s #%02d", name, counter) + + -- Increase counter. + counter=counter+1 + end + + -- Name is not unique and we don't want to make it unique. + if _unique==false and makeunique==false then + self:T3(ARTY.id..string.format("Name %s is not unique. Return false.", tostring(newname))) + + -- Return + return name, false + end + + -- Increase loop counter. We try max 100 times. + n=n+1 + until (_unique or n==nmax) -- Debug output and return new name. - self:T2(string.format("Original name %s, new name = %s", name, newname)) - return newname + self:T3(ARTY.id..string.format("Original name %s, new name = %s", name, newname)) + return newname, true end --- Check if target is in range. From 0e0ab3507c761928475c0b5fe38c611810a570f8 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sun, 13 May 2018 15:57:28 +0200 Subject: [PATCH 105/420] Okay, fixed the problem with the crashing at the deploy zone. Also added methods to set and/or randomize the pickup speed, pickup radius, deploy speed, deploy radius. --- .../Moose/AI/AI_Cargo_Dispatcher.lua | 145 ++++++++++++++++-- .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 5 + .../AI/AI_Cargo_Dispatcher_Helicopter.lua | 5 + .../Moose/AI/AI_Cargo_Helicopter.lua | 55 +++---- Moose Development/Moose/Core/Zone.lua | 18 +-- 5 files changed, 172 insertions(+), 56 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index dd03c258e..ad72a1cff 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -113,6 +113,125 @@ function AI_CARGO_DISPATCHER:SetHomeZone( HomeZone ) end +--- Sets or randomizes the pickup location for the carrier around the cargo coordinate in a radius defined an outer and optional inner radius. +-- This radius is influencing the location where the carrier will land to pickup the cargo. +-- There are two aspects that are very important to remember and take into account: +-- +-- - Ensure that the outer and inner radius are within reporting radius set by the cargo. +-- For example, if the cargo has a reporting radius of 400 meters, and the outer and inner radius is set to 500 and 450 respectively, +-- then no cargo will be loaded!!! +-- - Also take care of the potential cargo position and possible reasons to crash the carrier. This is especially important +-- for locations which are crowded with other objects, like in the middle of villages or cities. +-- So, for the best operation of cargo operations, always ensure that the cargo is located at open spaces. +-- +-- The default radius is 0, so the center. In case of a polygon zone, a random location will be selected as the center in the zone. +-- @param #AI_CARGO_DISPATCHER self +-- @param #number OuterRadius The outer radius in meters around the cargo coordinate. +-- @param #number InnerRadius (optional) The inner radius in meters around the cargo coordinate. +-- @return #AI_CARGO_DISPATCHER +-- @usage +-- +-- -- Create a new cargo dispatcher +-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone ) +-- +-- -- Set the carrier to land within a band around the cargo coordinate between 500 and 300 meters! +-- AICargoDispatcher:SetPickupRadius( 500, 300 ) +-- +function AI_CARGO_DISPATCHER:SetPickupRadius( OuterRadius, InnerRadius ) + + OuterRadius = OuterRadius or 0 + InnerRadius = InnerRadius or OuterRadius + + self.PickupOuterRadius = OuterRadius + self.PickupInnerRadius = InnerRadius + + return self +end + + +--- Set the speed or randomizes the speed in km/h to pickup the cargo. +-- @param #AI_CARGO_DISPATCHER self +-- @param #number MaxSpeed (optional) The maximum speed to move to the cargo pickup location. +-- @param #number MinSpeed The minimum speed to move to the cargo pickup location. +-- @return #AI_CARGO_DISPATCHER +-- @usage +-- +-- -- Create a new cargo dispatcher +-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone ) +-- +-- -- Set the minimum pickup speed to be 100 km/h and the maximum speed to be 200 km/h. +-- AICargoDispatcher:SetPickupSpeed( 200, 100 ) +-- +function AI_CARGO_DISPATCHER:SetPickupSpeed( MaxSpeed, MinSpeed ) + + MaxSpeed = MaxSpeed or 999 + MinSpeed = MinSpeed or MaxSpeed + + self.PickupMinSpeed = MinSpeed + self.PickupMaxSpeed = MaxSpeed + + return self +end + + +--- Sets or randomizes the deploy location for the carrier around the cargo coordinate in a radius defined an outer and an optional inner radius. +-- This radius is influencing the location where the carrier will land to deploy the cargo. +-- There is an aspect that is very important to remember and take into account: +-- +-- - Take care of the potential cargo position and possible reasons to crash the carrier. This is especially important +-- for locations which are crowded with other objects, like in the middle of villages or cities. +-- So, for the best operation of cargo operations, always ensure that the cargo is located at open spaces. +-- +-- The default radius is 0, so the center. In case of a polygon zone, a random location will be selected as the center in the zone. +-- @param #AI_CARGO_DISPATCHER self +-- @param #number OuterRadius The outer radius in meters around the cargo coordinate. +-- @param #number InnerRadius (optional) The inner radius in meters around the cargo coordinate. +-- @return #AI_CARGO_DISPATCHER +-- @usage +-- +-- -- Create a new cargo dispatcher +-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone ) +-- +-- -- Set the carrier to land within a band around the cargo coordinate between 500 and 300 meters! +-- AICargoDispatcher:SetDeployRadius( 500, 300 ) +-- +function AI_CARGO_DISPATCHER:SetDeployRadius( OuterRadius, InnerRadius ) + + OuterRadius = OuterRadius or 0 + InnerRadius = InnerRadius or OuterRadius + + self.DeployOuterRadius = OuterRadius + self.DeployInnerRadius = InnerRadius + + return self +end + + +--- Sets or randomizes the speed in km/h to deploy the cargo. +-- @param #AI_CARGO_DISPATCHER self +-- @param #number MaxSpeed The maximum speed to move to the cargo deploy location. +-- @param #number MinSpeed (optional) The minimum speed to move to the cargo deploy location. +-- @return #AI_CARGO_DISPATCHER +-- @usage +-- +-- -- Create a new cargo dispatcher +-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone ) +-- +-- -- Set the minimum deploy speed to be 100 km/h and the maximum speed to be 200 km/h. +-- AICargoDispatcher:SetDeploySpeed( 200, 100 ) +-- +function AI_CARGO_DISPATCHER:SetDeploySpeed( MaxSpeed, MinSpeed ) + + MaxSpeed = MaxSpeed or 999 + MinSpeed = MinSpeed or MaxSpeed + + self.DeployMinSpeed = MinSpeed + self.DeployMaxSpeed = MaxSpeed + + return self +end + + --- The Start trigger event, which actually takes action at the specified time interval. -- @param #AI_CARGO_DISPATCHER self @@ -168,16 +287,16 @@ function AI_CARGO_DISPATCHER:onafterMonitor() local Cargo = Cargo -- Cargo.Cargo#CARGO self:F( { Cargo = Cargo:GetName(), UnLoaded = Cargo:IsUnLoaded(), Deployed = Cargo:IsDeployed(), PickupCargo = self.PickupCargo[Cargo] ~= nil } ) if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then - local CargoVec2 = { x = Cargo:GetX(), y = Cargo:GetY() } - local LocationFound = false - for APC, Vec2 in pairs( self.PickupCargo ) do - if Vec2.x == CargoVec2.x and Vec2.y == CargoVec2.y then - LocationFound = true + local CargoCoordinate = Cargo:GetCoordinate() + local CoordinateFree = true + for APC, Coordinate in pairs( self.PickupCargo ) do + if CargoCoordinate:Get2DDistance( Coordinate ) <= 25 then + CoordinateFree = false break end end - if LocationFound == false then - self.PickupCargo[Carrier] = CargoVec2 + if CoordinateFree == true then + self.PickupCargo[Carrier] = CargoCoordinate PickupCargo = Cargo break end @@ -185,13 +304,14 @@ function AI_CARGO_DISPATCHER:onafterMonitor() end if PickupCargo then self.CarrierHome[Carrier] = nil - AI_Cargo:Pickup( PickupCargo:GetCoordinate() ) + local PickupCoordinate = PickupCargo:GetCoordinate():GetRandomCoordinateInRadius( self.PickupOuterRadius, self.PickupInnerRadius ) + AI_Cargo:Pickup( PickupCoordinate, math.random( self.PickupMinSpeed, self.PickupMaxSpeed ) ) break else if self.HomeZone then if not self.CarrierHome[Carrier] then self.CarrierHome[Carrier] = true - AI_Cargo:Home( self.HomeZone:GetRandomPointVec2() ) + AI_Cargo:__Home( 10, self.HomeZone:GetRandomPointVec2() ) end end end @@ -220,10 +340,11 @@ end function AI_CARGO_DISPATCHER:OnAfterLoaded( From, Event, To, APC, Cargo ) self:I( { "Loaded Dispatcher", APC } ) - local RandomZone = self.SetDeployZones:GetRandomZone() - self:I( { RandomZone = RandomZone } ) + local DeployZone = self.SetDeployZones:GetRandomZone() + self:I( { RandomZone = DeployZone } ) - self.AI_Cargo[APC]:Deploy( RandomZone:GetCoordinate(), 70 ) + local DeployCoordinate = DeployZone:GetCoordinate():GetRandomCoordinateInRadius( self.DeployOuterRadius, self.DeployInnerRadius ) + self.AI_Cargo[APC]:Deploy( DeployCoordinate, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ) ) self.PickupCargo[APC] = nil diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index 67d2a2c92..7081bd45e 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -50,6 +50,11 @@ function AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZones ) self.CombatRadius = 500 + self:SetDeploySpeed( 70, 120 ) + self:SetPickupSpeed( 70, 120 ) + self:SetPickupRadius( 0, 0 ) + self:SetDeployRadius( 0, 0 ) + self:Monitor( 1 ) return self diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index 56c4dae78..062b696a1 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -48,6 +48,11 @@ function AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargo, SetDeployZ local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetHelicopter, SetCargo, SetDeployZones ) ) -- #AI_CARGO_DISPATCHER_HELICOPTER + self:SetDeploySpeed( 200, 150 ) + self:SetPickupSpeed( 200, 150 ) + self:SetPickupRadius( 0, 0 ) + self:SetDeployRadius( 0, 0 ) + self:Monitor( 1 ) return self diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 695deb248..44ef641f5 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -22,7 +22,7 @@ AI_CARGO_HELICOPTER = { Coordinate = nil -- Core.Point#COORDINATE, } -AI_CARGO_HELICOPTER_QUEUE = {} +AI_CARGO_QUEUE = {} --- Creates a new AI_CARGO_HELICOPTER object. -- @param #AI_CARGO_HELICOPTER self @@ -225,43 +225,31 @@ function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordina local HelicopterInZone = false - --- @param Wrapper.Unit#UNIT ZoneUnit - local function EvaluateZone( ZoneUnit ) - - if ZoneUnit:IsAlive() then - local ZoneUnitCategory = ZoneUnit:GetDesc().category - local ZoneGroup = ZoneUnit:GetGroup() - if ZoneUnitCategory == Unit.Category.HELICOPTER then - local State = ZoneGroup:GetState( ZoneGroup, "Landing" ) - self:F({ZoneUnit=ZoneUnit:GetName(), State=State, UnitCategory = Unit.Category.HELICOPTER } ) - if State == true then - HelicopterInZone = true - return false - end - end - end - - return true - end - if Helicopter and Helicopter:IsAlive() then local Distance = Coordinate:DistanceFromPointVec2( Helicopter:GetCoordinate() ) - if Distance > 300 then + if Distance > 500 then self:__Queue( -10, Coordinate ) else - -- This will search the zone and will call the local function "EvaluateZone", which passes a UNIT object. - local Zone = ZONE_RADIUS:New( "Deploy", Coordinate:GetVec2(), 300 ) - Zone:SearchZone( EvaluateZone ) + local ZoneFree = true + + for Helicopter, ZoneQueue in pairs( AI_CARGO_QUEUE ) do + local ZoneQueue = ZoneQueue -- Core.Zone#ZONE_RADIUS + if ZoneQueue:IsCoordinateInZone( Coordinate ) then + ZoneFree = false + end + end - self:F({HelicopterInZone=HelicopterInZone}) + self:F({ZoneFree=ZoneFree}) - if HelicopterInZone == false then + if ZoneFree == true then - Helicopter:SetState( Helicopter, "Landing", true ) - + local ZoneQueue = ZONE_RADIUS:New( Helicopter:GetName(), Coordinate:GetVec2(), 100 ) + + AI_CARGO_QUEUE[Helicopter] = ZoneQueue + local Route = {} -- local CoordinateFrom = Helicopter:GetCoordinate() @@ -310,8 +298,6 @@ function AI_CARGO_HELICOPTER:onafterOrbit( Helicopter, From, Event, To, Coordina if Helicopter and Helicopter:IsAlive() then - Helicopter:ClearState( Helicopter, "Landing" ) - if not self:IsTransporting() then local Route = {} @@ -436,6 +422,7 @@ function AI_CARGO_HELICOPTER:onafterUnload( Helicopter, From, Event, To, Deploye local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT for _, Cargo in pairs( HelicopterUnit:GetCargo() ) do Cargo:UnBoard() + Cargo:SetDeployed( true ) self:__Unboard( 10, Cargo, Deployed ) end end @@ -483,7 +470,6 @@ function AI_CARGO_HELICOPTER:onbeforeUnloaded( Helicopter, From, Event, To, Carg if Deployed == true then for HelicopterUnit, Cargo in pairs( self.Helicopter_Cargo ) do local Cargo = Cargo -- Cargo.Cargo#CARGO - Cargo:SetDeployed( true ) end self.Helicopter_Cargo = {} end @@ -500,7 +486,9 @@ end -- @param Wrapper.Group#GROUP Helicopter function AI_CARGO_HELICOPTER:onafterUnloaded( Helicopter, From, Event, To, Cargo, Deployed ) - self:Orbit( Helicopter:GetCoordinate(), 50 ) + self:Orbit( Helicopter:GetCoordinate(), 50 ) + + AI_CARGO_QUEUE[Helicopter] = nil end @@ -515,7 +503,6 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin if Helicopter and Helicopter:IsAlive() ~= nil then - self:ScheduleOnce( 10, Helicopter.ClearState, Helicopter, Helicopter, "Landing" ) Helicopter:Activate() self.RoutePickup = true @@ -690,7 +677,7 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat Route[#Route+1] = WaypointTo -- Now route the helicopter - Helicopter:Route( Route, 1 ) + Helicopter:Route( Route, 0 ) end diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 36571c15b..cdc58b48a 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -151,10 +151,16 @@ end -- @param Dcs.DCSTypes#Vec3 Vec3 The point to test. -- @return #boolean true if the Vec3 is within the zone. function ZONE_BASE:IsVec3InZone( Vec3 ) - self:F2( Vec3 ) - local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } ) + return InZone +end +--- Returns if a Coordinate is within the zone. +-- @param #ZONE_BASE self +-- @param Core.Point#COORDINATE Coordinate The coordinate to test. +-- @return #boolean true if the coordinate is within the zone. +function ZONE_BASE:IsCoordinateInZone( Coordinate ) + local InZone = self:IsVec2InZone( Coordinate:GetVec2() ) return InZone end @@ -163,10 +169,7 @@ end -- @param Core.Point#POINT_VEC2 PointVec2 The PointVec2 to test. -- @return #boolean true if the PointVec2 is within the zone. function ZONE_BASE:IsPointVec2InZone( PointVec2 ) - self:F2( PointVec2 ) - local InZone = self:IsVec2InZone( PointVec2:GetVec2() ) - return InZone end @@ -175,10 +178,7 @@ end -- @param Core.Point#POINT_VEC3 PointVec3 The PointVec3 to test. -- @return #boolean true if the PointVec3 is within the zone. function ZONE_BASE:IsPointVec3InZone( PointVec3 ) - self:F2( PointVec3 ) - local InZone = self:IsPointVec2InZone( PointVec3 ) - return InZone end @@ -187,8 +187,6 @@ end -- @param #ZONE_BASE self -- @return #nil. function ZONE_BASE:GetVec2() - self:F2( self.ZoneName ) - return nil end From 7598a6ce5cff11edf2bf618e16a9d1c934e142f1 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 14 May 2018 06:53:45 +0200 Subject: [PATCH 106/420] Finish Cargo/AI_Cargo_Helicopter --- Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua | 2 +- Moose Development/Moose/AI/AI_Cargo_Helicopter.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index ad72a1cff..5362ea8a7 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -311,7 +311,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() if self.HomeZone then if not self.CarrierHome[Carrier] then self.CarrierHome[Carrier] = true - AI_Cargo:__Home( 10, self.HomeZone:GetRandomPointVec2() ) + AI_Cargo:__Home( 60, self.HomeZone:GetRandomPointVec2() ) end end end diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 44ef641f5..1efa9651b 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -617,7 +617,7 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin Route[#Route+1] = WaypointTo -- Now route the helicopter - Helicopter:Route( Route, 1 ) + Helicopter:Route( Route, 0 ) end From 48384ac7580ab4c1c74019b6dbda62bbe06e29b4 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 14 May 2018 08:12:15 +0200 Subject: [PATCH 107/420] Added the dynamic creation of a SET_ZONE, but you still need to declare all zones within the mission script. --- Moose Development/Moose/Core/Database.lua | 27 +++++++++++++ Moose Development/Moose/Core/Event.lua | 49 +++++++++++++++++++++++ Moose Development/Moose/Core/Set.lua | 41 +++++++++++++++++++ Moose Development/Moose/Core/Zone.lua | 6 +++ 4 files changed, 123 insertions(+) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 1a57a4b2b..995d243a3 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -96,6 +96,8 @@ function DATABASE:New() self:HandleEvent( EVENTS.Hit, self.AccountHits ) self:HandleEvent( EVENTS.NewCargo ) self:HandleEvent( EVENTS.DeleteCargo ) + self:HandleEvent( EVENTS.NewZone ) + self:HandleEvent( EVENTS.DeleteZone ) -- Follow alive players and clients --self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) -- This is not working anymore!, handling this through the birth event. @@ -1085,6 +1087,31 @@ function DATABASE:OnEventDeleteCargo( EventData ) end +--- Handles the OnEventNewZone event. +-- @param #DATABASE self +-- @param Core.Event#EVENTDATA EventData +function DATABASE:OnEventNewZone( EventData ) + self:F2( { EventData } ) + + if EventData.Zone then + self:AddZone( EventData.Zone ) + end +end + + +--- Handles the OnEventDeleteZone. +-- @param #DATABASE self +-- @param Core.Event#EVENTDATA EventData +function DATABASE:OnEventDeleteZone( EventData ) + self:F2( { EventData } ) + + if EventData.Zone then + self:DeleteZone( EventData.Zone.ZoneName ) + end +end + + + --- Gets the player settings -- @param #DATABASE self -- @param #string PlayerName diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index fafd2d2c5..007624e1a 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -179,6 +179,8 @@ EVENT = { world.event.S_EVENT_NEW_CARGO = world.event.S_EVENT_MAX + 1000 world.event.S_EVENT_DELETE_CARGO = world.event.S_EVENT_MAX + 1001 +world.event.S_EVENT_NEW_ZONE = world.event.S_EVENT_MAX + 1002 +world.event.S_EVENT_DELETE_ZONE = world.event.S_EVENT_MAX + 1003 --- The different types of events supported by MOOSE. -- Use this structure to subscribe to events using the @{Base#BASE.HandleEvent}() method. @@ -209,6 +211,8 @@ EVENTS = { ShootingEnd = world.event.S_EVENT_SHOOTING_END, NewCargo = world.event.S_EVENT_NEW_CARGO, DeleteCargo = world.event.S_EVENT_DELETE_CARGO, + NewZone = world.event.S_EVENT_NEW_ZONE, + DeleteZone = world.event.S_EVENT_DELETE_ZONE, } --- The Event structure @@ -406,6 +410,16 @@ local _EVENTMETA = { Event = "OnEventDeleteCargo", Text = "S_EVENT_DELETE_CARGO" }, + [EVENTS.NewZone] = { + Order = 1, + Event = "OnEventNewZone", + Text = "S_EVENT_NEW_ZONE" + }, + [EVENTS.DeleteZone] = { + Order = 1, + Event = "OnEventDeleteZone", + Text = "S_EVENT_DELETE_ZONE" + }, } @@ -710,6 +724,36 @@ do -- Event Creation world.onEvent( Event ) end + --- Creation of a New Zone Event. + -- @param #EVENT self + -- @param Core.Zone#ZONE_BASE Zone The Zone created. + function EVENT:CreateEventNewZone( Zone ) + self:F( { Zone } ) + + local Event = { + id = EVENTS.NewZone, + time = timer.getTime(), + zone = Zone, + } + + world.onEvent( Event ) + end + + --- Creation of a Zone Deletion Event. + -- @param #EVENT self + -- @param Core.Zone#ZONE_BASE Zone The Zone created. + function EVENT:CreateEventDeleteZone( Zone ) + self:F( { Zone } ) + + local Event = { + id = EVENTS.DeleteZone, + time = timer.getTime(), + zone = Zone, + } + + world.onEvent( Event ) + end + --- Creation of a S_EVENT_PLAYER_ENTER_UNIT Event. -- @param #EVENT self -- @param Wrapper.Unit#UNIT PlayerUnit. @@ -873,6 +917,11 @@ function EVENT:onEvent( Event ) Event.Cargo = Event.cargo Event.CargoName = Event.cargo.Name end + + if Event.zone then + Event.Zone = Event.zone + Event.ZoneName = Event.zone.ZoneName + end local PriorityOrder = EventMeta.Order local PriorityBegin = PriorityOrder == -1 and 5 or 1 diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index b29d1d6fa..71e85a8ee 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -4656,6 +4656,9 @@ function SET_ZONE:FilterStart() end end end + + self:HandleEvent( EVENTS.NewZone ) + self:HandleEvent( EVENTS.DeleteZone ) return self end @@ -4726,3 +4729,41 @@ function SET_ZONE:IsIncludeObject( MZone ) return MZoneInclude end +--- Handles the OnEventNewZone event for the Set. +-- @param #SET_ZONE self +-- @param Core.Event#EVENTDATA EventData +function SET_ZONE:OnEventNewZone( EventData ) --R2.1 + + self:F( { "New Zone", EventData } ) + + if EventData.Zone then + if EventData.Zone and self:IsIncludeObject( EventData.Zone ) then + self:Add( EventData.Zone.ZoneName , EventData.Zone ) + end + end +end + +--- Handles the OnDead or OnCrash event for alive units set. +-- @param #SET_ZONE self +-- @param Core.Event#EVENTDATA EventData +function SET_ZONE:OnEventDeleteZone( EventData ) --R2.1 + self:F3( { EventData } ) + + if EventData.Zone then + local Zone = _DATABASE:FindZone( EventData.Zone.ZoneName ) + if Zone and Zone.ZoneName then + + -- When cargo was deleted, it may probably be because of an S_EVENT_DEAD. + -- However, in the loading logic, an S_EVENT_DEAD is also generated after a Destroy() call. + -- And this is a problem because it will remove all entries from the SET_ZONEs. + -- To prevent this from happening, the Zone object has a flag NoDestroy. + -- When true, the SET_ZONE won't Remove the Zone object from the set. + -- This flag is switched off after the event handlers have been called in the EVENT class. + self:F( { ZoneNoDestroy=Zone.NoDestroy } ) + if Zone.NoDestroy then + else + self:Remove( Zone.ZoneName ) + end + end + end +end diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index cdc58b48a..d3239c039 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -1001,6 +1001,9 @@ function ZONE_UNIT:New( ZoneName, ZoneUNIT, Radius ) self.ZoneUNIT = ZoneUNIT self.LastVec2 = ZoneUNIT:GetVec2() + -- Zone objects are added to the _DATABASE and SET_ZONE objects. + _EVENTDISPATCHER:CreateEventNewZone( self ) + return self end @@ -1089,6 +1092,9 @@ function ZONE_GROUP:New( ZoneName, ZoneGROUP, Radius ) self:F( { ZoneName, ZoneGROUP:GetVec2(), Radius } ) self._.ZoneGROUP = ZoneGROUP + + -- Zone objects are added to the _DATABASE and SET_ZONE objects. + _EVENTDISPATCHER:CreateEventNewZone( self ) return self end From ac72e6fad2cc1208f870074c8ff2839bbb60d2fa Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Tue, 15 May 2018 19:02:17 +0200 Subject: [PATCH 108/420] Zone probability implementation. --- Moose Development/Moose/Core/Set.lua | 35 +++++++++++++++++++++------ Moose Development/Moose/Core/Zone.lua | 21 ++++++++++++++++ 2 files changed, 48 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 71e85a8ee..e00d350db 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -4606,20 +4606,39 @@ end --- Get a random zone from the set. -- @param #SET_ZONE self -- @return Core.Zone#ZONE_BASE The random Zone. +-- @return #nil if no zone in the collection. function SET_ZONE:GetRandomZone() - local Index = self.Index - local ZoneFound = nil - - while not ZoneFound do - local ZoneRandom = math.random( 1, #Index ) - ZoneFound = self.Set[Index[ZoneRandom]] - end + if self:Count() ~= 0 then - return ZoneFound + local Index = self.Index + local ZoneFound = nil -- Core.Zone#ZONE_BASE + + -- Loop until a zone has been found. + -- The :GetZoneMaybe() call will evaluate the probability for the zone to be selected. + -- If the zone is not selected, then nil is returned by :GetZoneMaybe() and the loop continues! + while not ZoneFound do + local ZoneRandom = math.random( 1, #Index ) + ZoneFound = self.Set[Index[ZoneRandom]]:GetZoneMaybe() + end + + return ZoneFound + end + + return nil end +--- Set a zone probability. +-- @param #SET_ZONE self +-- @param #string ZoneName The name of the zone. +function SET_ZONE:SetZoneProbability( ZoneName, ZoneProbability ) + local Zone = self:FindZone( ZoneName ) + Zone:SetZoneProbability( ZoneProbability ) +end + + + --- Builds a set of zones of defined zone prefixes. -- All the zones starting with the given prefixes will be included within the set. diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index d3239c039..11b85b3b2 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -341,6 +341,27 @@ end -- @param #ZONE_BASE self -- @return #ZONE_BASE The zone is selected taking into account the randomization probability factor. -- @return #nil The zone is not selected taking into account the randomization probability factor. +-- @usage +-- +-- local ZoneArray = { ZONE:New( "Zone1" ), ZONE:New( "Zone2" ) } +-- +-- -- We set a zone probability of 70% to the first zone and 30% to the second zone. +-- ZoneArray[1]:SetZoneProbability( 0.5 ) +-- ZoneArray[2]:SetZoneProbability( 0.5 ) +-- +-- local ZoneSelected = nil +-- +-- while ZoneSelected == nil do +-- for _, Zone in pairs( ZoneArray ) do +-- ZoneSelected = Zone:GetZoneMaybe() +-- if ZoneSelected ~= nil then +-- break +-- end +-- end +-- end +-- +-- -- The result should be that Zone1 would be more probable selected than Zone2. +-- function ZONE_BASE:GetZoneMaybe() self:F2() From 4309aa326f7e3bbcb97908fc01bee656a6a30d06 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Tue, 15 May 2018 19:06:55 +0200 Subject: [PATCH 109/420] Finish Cargo/AI_Cargo_Helicopter --- Moose Development/Moose/Core/Zone.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 11b85b3b2..e06713aa2 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -322,7 +322,7 @@ end -- @param #ZONE_BASE self -- @param ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability. function ZONE_BASE:SetZoneProbability( ZoneProbability ) - self:F2( ZoneProbability ) + self:F( { Zone:GetName(), ZoneProbability = ZoneProbability } ) self.ZoneProbability = ZoneProbability or 1 return self From b82e85997f399a0252bc235c3a9575fbe9b2dc3d Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Tue, 15 May 2018 19:13:11 +0200 Subject: [PATCH 110/420] # Conflicts: # Moose Development/Moose/Core/Zone.lua --- Moose Development/Moose/Core/Zone.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index e06713aa2..203105575 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -322,7 +322,7 @@ end -- @param #ZONE_BASE self -- @param ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability. function ZONE_BASE:SetZoneProbability( ZoneProbability ) - self:F( { Zone:GetName(), ZoneProbability = ZoneProbability } ) + self:F( { self:GetName(), ZoneProbability = ZoneProbability } ) self.ZoneProbability = ZoneProbability or 1 return self From 533b5d035e1a6d95a805e3faaa136ef9db1faac9 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Thu, 17 May 2018 08:51:59 +0200 Subject: [PATCH 111/420] - Documentation - Added the methods Added and Removed to the SET - Cleanup of code. --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 7 +- .../Moose/AI/AI_Cargo_Dispatcher.lua | 243 +++++++++++++----- .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 66 ++++- .../AI/AI_Cargo_Dispatcher_Helicopter.lua | 75 +++++- .../Moose/AI/AI_Cargo_Helicopter.lua | 12 +- Moose Development/Moose/Core/Set.lua | 145 ++++++++++- 6 files changed, 456 insertions(+), 92 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index b5d0a3f8a..0cb8aed9b 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -82,11 +82,11 @@ AI_CARGO_APC = { --- Creates a new AI_CARGO_APC object. -- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP CargoCarrier +-- @param Wrapper.Group#GROUP APC -- @param Core.Set#SET_CARGO CargoSet -- @param #number CombatRadius -- @return #AI_CARGO_APC -function AI_CARGO_APC:New( CargoCarrier, CargoSet, CombatRadius ) +function AI_CARGO_APC:New( APC, CargoSet, CombatRadius ) local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_CARGO_APC @@ -189,7 +189,8 @@ function AI_CARGO_APC:New( CargoCarrier, CargoSet, CombatRadius ) self:__Monitor( 1 ) - self:SetCarrier( CargoCarrier ) + + self:SetCarrier( APC ) self.Transporting = false self.Relocating = false diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 5362ea8a7..c0eed577e 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -9,66 +9,115 @@ -- @module AI_Cargo_Dispatcher --- @type AI_CARGO_DISPATCHER --- @extends Core.Fsm#FSM_CONTROLLABLE +-- @extends Core.Fsm#FSM ---- # AI\_CARGO\_DISPATCHER class, extends @{Core.Base#BASE} +--- # AI\_CARGO\_DISPATCHER class, extends @{Core.Fsm#FSM} -- -- === -- -- AI\_CARGO\_DISPATCHER brings a dynamic cargo handling capability for AI groups. -- --- Armoured Personnel APCs (APC), Trucks, Jeeps and other carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. --- The AI\_CARGO\_DISPATCHER module uses the @{Cargo} capabilities within the MOOSE framework. +-- Carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. +-- The AI\_CARGO\_DISPATCHER module uses the @{Cargo} capabilities within the MOOSE framework, to enable Carrier GROUP objects +-- to transport @{Cargo} towards several deploy zones. -- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER object recognize the cargo. -- Please consult the @{Cargo} module for more information. -- +-- ## 1. AI\_CARGO\_DISPATCHER constructor +-- +-- * @{#AI_CARGO_DISPATCHER.New}(): Creates a new AI\_CARGO\_DISPATCHER object. -- +-- ## 2. AI\_CARGO\_DISPATCHER is a FSM -- +-- ![Process](..\Presentations\AI_PATROL\Dia2.JPG) +-- +-- ### 2.1. AI\_CARGO\_DISPATCHER States +-- +-- * **Monitoring**: The process is dispatching. +-- * **Idle**: The process is idle. +-- +-- ### 2.2. AI\_CARGO\_DISPATCHER Events +-- +-- * **Monitor**: Monitor and take action. +-- * **Start**: Start the transport process. +-- * **Stop**: Stop the transport process. +-- * **Pickup**: Pickup cargo. +-- * **Load**: Load the cargo. +-- * **Loaded**: Flag that the cargo is loaded. +-- * **Deploy**: Deploy cargo to a location. +-- * **Unload**: Unload the cargo. +-- * **Unloaded**: Flag that the cargo is unloaded. +-- * **Home**: A Carrier is going home. +-- +-- ## 3. Set the pickup parameters. +-- +-- Several parameters can be set to pickup cargo: +-- +-- * @{#AI_CARGO_DISPATCHER.SetPickupRadius}(): Sets or randomizes the pickup location for the carrier around the cargo coordinate in a radius defined an outer and optional inner radius. +-- * @{#AI_CARGO_DISPATCHER.SetPickupSpeed}(): Set the speed or randomizes the speed in km/h to pickup the cargo. +-- +-- ## 4. Set the deploy parameters. +-- +-- Several parameters can be set to deploy cargo: +-- +-- * @{#AI_CARGO_DISPATCHER.SetDeployRadius}(): Sets or randomizes the deploy location for the carrier around the cargo coordinate in a radius defined an outer and an optional inner radius. +-- * @{#AI_CARGO_DISPATCHER.SetDeploySpeed}(): Set the speed or randomizes the speed in km/h to deploy the cargo. +-- +-- ## 5. Set the home zone when there isn't any more cargo to pickup. +-- +-- A home zone can be specified to where the Carriers will move when there isn't any cargo left for pickup. +-- Use @{#AI_CARGO_DISPATCHER.SetHomeZone}() to specify the home zone. +-- +-- If no home zone is specified, the carriers will wait near the deploy zone for a new pickup command. +-- +-- === +-- -- @field #AI_CARGO_DISPATCHER AI_CARGO_DISPATCHER = { ClassName = "AI_CARGO_DISPATCHER", - SetAPC = nil, + SetCarrier = nil, SetDeployZones = nil, - AI_CARGO_APC = {} + AI_Cargo = {}, + PickupCargo = {} } ---- @type AI_CARGO_DISPATCHER.AI_CARGO_APC --- @map - ---- @field #AI_CARGO_DISPATCHER.AI_CARGO_APC +--- @field #AI_CARGO_DISPATCHER.AI_Cargo AI_CARGO_DISPATCHER.AI_Cargo = {} --- @field #AI_CARGO_DISPATCHER.PickupCargo AI_CARGO_DISPATCHER.PickupCargo = {} - --- Creates a new AI_CARGO_DISPATCHER object. -- @param #AI_CARGO_DISPATCHER self --- @param Core.Set#SET_GROUP SetAPC +-- @param Core.Set#SET_GROUP SetCarrier -- @param Core.Set#SET_CARGO SetCargo -- @param Core.Set#SET_ZONE SetDeployZone -- @return #AI_CARGO_DISPATCHER -- @usage -- -- -- Create a new cargo dispatcher --- SetAPC = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() +-- SetCarrier = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() -- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() -- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() --- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone ) +-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZone ) -- -function AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZones ) +function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZones ) local self = BASE:Inherit( self, FSM:New() ) -- #AI_CARGO_DISPATCHER - self.SetAPC = SetAPC -- Core.Set#SET_GROUP + self.SetCarrier = SetCarrier -- Core.Set#SET_GROUP self.SetCargo = SetCargo -- Core.Set#SET_CARGO self.SetDeployZones = SetDeployZones -- Core.Set#SET_ZONE - self:SetStartState( "Dispatch" ) + self:SetStartState( "Idle" ) + + self:AddTransition( "Monitoring", "Monitor", "Monitoring" ) + + self:AddTransition( "Idle", "Start", "Monitoring" ) + self:AddTransition( "Monitoring", "Stop", "Idle" ) - self:AddTransition( "*", "Monitor", "*" ) self:AddTransition( "*", "Pickup", "*" ) self:AddTransition( "*", "Loading", "*" ) @@ -84,8 +133,16 @@ function AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZones ) self.DeployRadiusInner = 200 self.DeployRadiusOuter = 500 + self.PickupCargo = {} self.CarrierHome = {} + -- Put a Dead event handler on SetCarrier, to ensure that when a carrier is destroyed, that all internal parameters are reset. + function SetCarrier.OnAfterRemoved( SetCarrier, From, Event, To, CarrierName, Carrier ) + self:F( { Carrier = Carrier:GetName() } ) + self.PickupCargo[Carrier] = nil + self.CarrierHome[Carrier] = nil + end + return self end @@ -99,7 +156,7 @@ end -- @usage -- -- -- Create a new cargo dispatcher --- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone ) +-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZone ) -- -- -- Set the home coordinate -- local HomeZone = ZONE:New( "Home" ) @@ -132,7 +189,7 @@ end -- @usage -- -- -- Create a new cargo dispatcher --- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone ) +-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZone ) -- -- -- Set the carrier to land within a band around the cargo coordinate between 500 and 300 meters! -- AICargoDispatcher:SetPickupRadius( 500, 300 ) @@ -157,7 +214,7 @@ end -- @usage -- -- -- Create a new cargo dispatcher --- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone ) +-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZone ) -- -- -- Set the minimum pickup speed to be 100 km/h and the maximum speed to be 200 km/h. -- AICargoDispatcher:SetPickupSpeed( 200, 100 ) @@ -190,7 +247,7 @@ end -- @usage -- -- -- Create a new cargo dispatcher --- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone ) +-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZone ) -- -- -- Set the carrier to land within a band around the cargo coordinate between 500 and 300 meters! -- AICargoDispatcher:SetDeployRadius( 500, 300 ) @@ -215,7 +272,7 @@ end -- @usage -- -- -- Create a new cargo dispatcher --- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone ) +-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZone ) -- -- -- Set the minimum deploy speed to be 100 km/h and the maximum speed to be 200 km/h. -- AICargoDispatcher:SetDeploySpeed( 200, 100 ) @@ -235,64 +292,66 @@ end --- The Start trigger event, which actually takes action at the specified time interval. -- @param #AI_CARGO_DISPATCHER self --- @param Wrapper.Group#GROUP APC --- @return #AI_CARGO_DISPATCHER function AI_CARGO_DISPATCHER:onafterMonitor() - for APCGroupName, Carrier in pairs( self.SetAPC:GetSet() ) do + for CarrierGroupName, Carrier in pairs( self.SetCarrier:GetSet() ) do local Carrier = Carrier -- Wrapper.Group#GROUP local AI_Cargo = self.AI_Cargo[Carrier] if not AI_Cargo then - -- ok, so this APC does not have yet an AI_CARGO_APC object... + -- ok, so this Carrier does not have yet an AI_CARGO handling object... -- let's create one and also declare the Loaded and UnLoaded handlers. self.AI_Cargo[Carrier] = self:AICargo( Carrier, self.SetCargo, self.CombatRadius ) AI_Cargo = self.AI_Cargo[Carrier] - function AI_Cargo.OnAfterPickup( AI_Cargo, APC, From, Event, To, Cargo ) - self:Pickup( APC, Cargo ) + function AI_Cargo.OnAfterPickup( AI_Cargo, Carrier, From, Event, To, Cargo ) + self:Pickup( Carrier, Cargo ) end - function AI_Cargo.OnAfterLoad( AI_Cargo, APC ) - self:Loading( APC ) + function AI_Cargo.OnAfterLoad( AI_Cargo, Carrier ) + self:Loading( Carrier ) end - function AI_Cargo.OnAfterLoaded( AI_Cargo, APC, From, Event, To, Cargo ) - self:Loaded( APC, Cargo ) + function AI_Cargo.OnAfterLoaded( AI_Cargo, Carrier, From, Event, To, Cargo ) + self:Loaded( Carrier, Cargo ) end - function AI_Cargo.OnAfterDeploy( AI_Cargo, APC ) - self:Deploy( APC ) + function AI_Cargo.OnAfterDeploy( AI_Cargo, Carrier ) + self:Deploy( Carrier ) end - function AI_Cargo.OnAfterUnload( AI_Cargo, APC ) - self:Unloading( APC ) + function AI_Cargo.OnAfterUnload( AI_Cargo, Carrier ) + self:Unloading( Carrier ) end - function AI_Cargo.OnAfterUnloaded( AI_Cargo, APC ) - self:Unloaded( APC ) + function AI_Cargo.OnAfterUnloaded( AI_Cargo, Carrier ) + self:Unloaded( Carrier ) end end -- The Pickup sequence ... - -- Check if this APC need to go and Pickup something... + -- Check if this Carrier need to go and Pickup something... self:I( { IsTransporting = AI_Cargo:IsTransporting() } ) if AI_Cargo:IsTransporting() == false then - -- ok, so there is a free APC + -- ok, so there is a free Carrier -- now find the first cargo that is Unloaded local PickupCargo = nil for CargoName, Cargo in pairs( self.SetCargo:GetSet() ) do local Cargo = Cargo -- Cargo.Cargo#CARGO - self:F( { Cargo = Cargo:GetName(), UnLoaded = Cargo:IsUnLoaded(), Deployed = Cargo:IsDeployed(), PickupCargo = self.PickupCargo[Cargo] ~= nil } ) + self:F( { Cargo = Cargo:GetName(), UnLoaded = Cargo:IsUnLoaded(), Deployed = Cargo:IsDeployed(), PickupCargo = self.PickupCargo[Carrier] ~= nil } ) if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then local CargoCoordinate = Cargo:GetCoordinate() local CoordinateFree = true - for APC, Coordinate in pairs( self.PickupCargo ) do - if CargoCoordinate:Get2DDistance( Coordinate ) <= 25 then - CoordinateFree = false - break + for CarrierPickup, Coordinate in pairs( self.PickupCargo ) do + if CarrierPickup:IsAlive() == true then + if CargoCoordinate:Get2DDistance( Coordinate ) <= 25 then + CoordinateFree = false + break + end + else + self.PickupCargo[CarrierPickup] = nil end end if CoordinateFree == true then @@ -319,36 +378,90 @@ function AI_CARGO_DISPATCHER:onafterMonitor() end self:__Monitor( self.MonitorTimeInterval ) +end - return self +--- Start Handler OnBefore for AI_CARGO_DISPATCHER +-- @function [parent=#AI_CARGO_DISPATCHER] OnBeforeStart +-- @param #AI_CARGO_DISPATCHER self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #boolean + +--- Start Handler OnAfter for AI_CARGO_DISPATCHER +-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterStart +-- @param #AI_CARGO_DISPATCHER self +-- @param #string From +-- @param #string Event +-- @param #string To + +--- Start Trigger for AI_CARGO_DISPATCHER +-- @function [parent=#AI_CARGO_DISPATCHER] Start +-- @param #AI_CARGO_DISPATCHER self + +--- Start Asynchronous Trigger for AI_CARGO_DISPATCHER +-- @function [parent=#AI_CARGO_DISPATCHER] __Start +-- @param #AI_CARGO_DISPATCHER self +-- @param #number Delay + +function AI_CARGO_DISPATCHER:onafterStart( From, Event, To ) + self:Monitor() +end + +--- Stop Handler OnBefore for AI_CARGO_DISPATCHER +-- @function [parent=#AI_CARGO_DISPATCHER] OnBeforeStop +-- @param #AI_CARGO_DISPATCHER self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @return #boolean + +--- Stop Handler OnAfter for AI_CARGO_DISPATCHER +-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterStop +-- @param #AI_CARGO_DISPATCHER self +-- @param #string From +-- @param #string Event +-- @param #string To + +--- Stop Trigger for AI_CARGO_DISPATCHER +-- @function [parent=#AI_CARGO_DISPATCHER] Stop +-- @param #AI_CARGO_DISPATCHER self + +--- Stop Asynchronous Trigger for AI_CARGO_DISPATCHER +-- @function [parent=#AI_CARGO_DISPATCHER] __Stop +-- @param #AI_CARGO_DISPATCHER self +-- @param #number Delay + + + +--- Make a Carrier run for a cargo deploy action after the cargo Pickup trigger has been initiated, by default. +-- @param #AI_CARGO_DISPATCHER self +-- @param From +-- @param Event +-- @param To +-- @param Wrapper.Group#GROUP Carrier +-- @param Cargo.Cargo#CARGO Cargo +-- @return #AI_CARGO_DISPATCHER +function AI_CARGO_DISPATCHER:OnAfterPickup( From, Event, To, Carrier, Cargo ) end - ---- Make a APC run for a cargo deploy action after the cargo Pickup trigger has been initiated, by default. +--- Make a Carrier run for a cargo deploy action after the cargo has been loaded, by default. -- @param #AI_CARGO_DISPATCHER self --- @param Wrapper.Group#GROUP APC +-- @param From +-- @param Event +-- @param To +-- @param Wrapper.Group#GROUP Carrier +-- @param Cargo.Cargo#CARGO Cargo -- @return #AI_CARGO_DISPATCHER -function AI_CARGO_DISPATCHER:onafterPickup( From, Event, To, APC, Cargo ) - return self -end +function AI_CARGO_DISPATCHER:OnAfterLoaded( From, Event, To, Carrier, Cargo ) ---- Make a APC run for a cargo deploy action after the cargo has been loaded, by default. --- @param #AI_CARGO_DISPATCHER self --- @param Wrapper.Group#GROUP APC --- @return #AI_CARGO_DISPATCHER -function AI_CARGO_DISPATCHER:OnAfterLoaded( From, Event, To, APC, Cargo ) - - self:I( { "Loaded Dispatcher", APC } ) local DeployZone = self.SetDeployZones:GetRandomZone() - self:I( { RandomZone = DeployZone } ) local DeployCoordinate = DeployZone:GetCoordinate():GetRandomCoordinateInRadius( self.DeployOuterRadius, self.DeployInnerRadius ) - self.AI_Cargo[APC]:Deploy( DeployCoordinate, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ) ) + self.AI_Cargo[Carrier]:Deploy( DeployCoordinate, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ) ) - self.PickupCargo[APC] = nil - - return self + self.PickupCargo[Carrier] = nil end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index 7081bd45e..d7b21b13d 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -14,6 +14,8 @@ --- # AI\_CARGO\_DISPATCHER\_APC class, extends @{Core.Base#BASE} -- +-- ![Banner Image](..\Presentations\AI_CARGO_DISPATCHER_APC\Dia1.JPG) +-- -- === -- -- AI\_CARGO\_DISPATCHER\_APC brings a dynamic cargo handling capability for AI groups. @@ -23,7 +25,54 @@ -- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER\_APC object recognize the cargo. -- Please consult the @{Cargo} module for more information. -- +-- ## 1. AI\_CARGO\_DISPATCHER\_APC constructor +-- +-- * @{#AI_CARGO_DISPATCHER\_APC.New}(): Creates a new AI\_CARGO\_DISPATCHER\_APC object. -- +-- ## 2. AI\_CARGO\_DISPATCHER\_APC is a FSM +-- +-- ![Process](..\Presentations\AI_CARGO_DISPATCHER_APC\Dia3.JPG) +-- +-- ### 2.1. AI\_CARGO\_DISPATCHER\_APC States +-- +-- * **Monitoring**: The process is dispatching. +-- * **Idle**: The process is idle. +-- +-- ### 2.2. AI\_CARGO\_DISPATCHER\_APC Events +-- +-- * **Monitor**: Monitor and take action. +-- * **Start**: Start the transport process. +-- * **Stop**: Stop the transport process. +-- * **Pickup**: Pickup cargo. +-- * **Load**: Load the cargo. +-- * **Loaded**: Flag that the cargo is loaded. +-- * **Deploy**: Deploy cargo to a location. +-- * **Unload**: Unload the cargo. +-- * **Unloaded**: Flag that the cargo is unloaded. +-- * **Home**: A APC is going home. +-- +-- ## 3. Set the pickup parameters. +-- +-- Several parameters can be set to pickup cargo: +-- +-- * @{#AI_CARGO_DISPATCHER\_APC.SetPickupRadius}(): Sets or randomizes the pickup location for the APC around the cargo coordinate in a radius defined an outer and optional inner radius. +-- * @{#AI_CARGO_DISPATCHER\_APC.SetPickupSpeed}(): Set the speed or randomizes the speed in km/h to pickup the cargo. +-- +-- ## 4. Set the deploy parameters. +-- +-- Several parameters can be set to deploy cargo: +-- +-- * @{#AI_CARGO_DISPATCHER\_APC.SetDeployRadius}(): Sets or randomizes the deploy location for the APC around the cargo coordinate in a radius defined an outer and an optional inner radius. +-- * @{#AI_CARGO_DISPATCHER\_APC.SetDeploySpeed}(): Set the speed or randomizes the speed in km/h to deploy the cargo. +-- +-- ## 5. Set the home zone when there isn't any more cargo to pickup. +-- +-- A home zone can be specified to where the APCs will move when there isn't any cargo left for pickup. +-- Use @{#AI_CARGO_DISPATCHER\_APC.SetHomeZone}() to specify the home zone. +-- +-- If no home zone is specified, the APCs will wait near the deploy zone for a new pickup command. +-- +-- === -- -- @field #AI_CARGO_DISPATCHER_APC AI_CARGO_DISPATCHER_APC = { @@ -32,31 +81,30 @@ AI_CARGO_DISPATCHER_APC = { --- Creates a new AI_CARGO_DISPATCHER_APC object. -- @param #AI_CARGO_DISPATCHER_APC self --- @param Core.Set#SET_GROUP SetAPC --- @param Core.Set#SET_CARGO SetCargo --- @param Core.Set#SET_ZONE SetDeployZone +-- @param Core.Set#SET_GROUP SetAPC The collection of APC @{Group}s. +-- @param Core.Set#SET_CARGO SetCargo The collection of @{Cargo} derived objects. +-- @param Core.Set#SET_ZONE SetDeployZone The collection of deploy @{Zone}s, which are used to where the cargo will be deployed by the APCs. +-- @param #number CombatRadius The cargo will be unloaded from the APC and engage the enemy if the enemy is within CombatRadius range. The radius is in meters, the default value is 500 meters. -- @return #AI_CARGO_DISPATCHER_APC -- @usage -- --- -- Create a new cargo dispatcher +-- -- Create a new cargo dispatcher for the set of APCs, with a combatradius of 500. -- SetAPC = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() -- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() -- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() --- AICargoDispatcher = AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo ) +-- AICargoDispatcher = AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZone, 500 ) -- -function AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZones ) +function AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZones, CombatRadius ) local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZones ) ) -- #AI_CARGO_DISPATCHER_APC - self.CombatRadius = 500 + self.CombatRadius = CombatRadius or 500 self:SetDeploySpeed( 70, 120 ) self:SetPickupSpeed( 70, 120 ) self:SetPickupRadius( 0, 0 ) self:SetDeployRadius( 0, 0 ) - self:Monitor( 1 ) - return self end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index 062b696a1..6a130eabe 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -1,5 +1,7 @@ --- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo using Helicopters. -- +-- The @{#AI_CARGO_DISPATCHER_HELICOPTER} classes implements the dynamic dispatching of cargo transportation tasks for helicopters. +-- -- === -- -- ### Author: **FlightControl** @@ -12,18 +14,77 @@ -- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER ---- # AI\_CARGO\_DISPATCHER\_HELICOPTER class, extends @{Core.Base#BASE} +--- # AI\_CARGO\_DISPATCHER\_HELICOPTER class, extends @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} +-- +-- ![Banner Image](..\Presentations\AI_CARGO_DISPATCHER_HELICOPTER\Dia1.JPG) -- -- === -- --- AI\_CARGO\_DISPATCHER\_HELICOPTER brings a dynamic cargo handling capability for AI groups. +-- AI\_CARGO\_DISPATCHER\_HELICOPTER brings a dynamic cargo handling capability for AI helicopter groups. -- -- Helicopters can be mobilized to intelligently transport infantry and other cargo within the simulation. -- The AI\_CARGO\_DISPATCHER\_HELICOPTER module uses the @{Cargo} capabilities within the MOOSE framework. -- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER\_HELICOPTER object recognize the cargo. -- Please consult the @{Cargo} module for more information. -- +-- --- -- +-- ## 1. AI\_CARGO\_DISPATCHER\_HELICOPTER constructor +-- +-- * @{#AI_CARGO_DISPATCHER\_HELICOPTER.New}(): Creates a new AI\_CARGO\_DISPATCHER\_HELICOPTER object. +-- +-- --- +-- +-- ## 2. AI\_CARGO\_DISPATCHER\_HELICOPTER is a FSM +-- +-- ![Process](..\Presentations\AI_CARGO_DISPATCHER_HELICOPTER\Dia3.JPG) +-- +-- ### 2.1. AI\_CARGO\_DISPATCHER\_HELICOPTER States +-- +-- * **Monitoring**: The process is dispatching. +-- * **Idle**: The process is idle. +-- +-- ### 2.2. AI\_CARGO\_DISPATCHER\_HELICOPTER Events +-- +-- * **Monitor**: Monitor and take action. +-- * **Start**: Start the transport process. +-- * **Stop**: Stop the transport process. +-- * **Pickup**: Pickup cargo. +-- * **Load**: Load the cargo. +-- * **Loaded**: Flag that the cargo is loaded. +-- * **Deploy**: Deploy cargo to a location. +-- * **Unload**: Unload the cargo. +-- * **Unloaded**: Flag that the cargo is unloaded. +-- * **Home**: A Helicopter is going home. +-- +-- --- +-- +-- ## 3. Set the pickup parameters. +-- +-- Several parameters can be set to pickup cargo: +-- +-- * @{#AI_CARGO_DISPATCHER\_HELICOPTER.SetPickupRadius}(): Sets or randomizes the pickup location for the helicopter around the cargo coordinate in a radius defined an outer and optional inner radius. +-- * @{#AI_CARGO_DISPATCHER\_HELICOPTER.SetPickupSpeed}(): Set the speed or randomizes the speed in km/h to pickup the cargo. +-- +-- --- +-- +-- ## 4. Set the deploy parameters. +-- +-- Several parameters can be set to deploy cargo: +-- +-- * @{#AI_CARGO_DISPATCHER\_HELICOPTER.SetDeployRadius}(): Sets or randomizes the deploy location for the helicopter around the cargo coordinate in a radius defined an outer and an optional inner radius. +-- * @{#AI_CARGO_DISPATCHER\_HELICOPTER.SetDeploySpeed}(): Set the speed or randomizes the speed in km/h to deploy the cargo. +-- +-- --- +-- +-- ## 5. Set the home zone when there isn't any more cargo to pickup. +-- +-- A home zone can be specified to where the Helicopters will move when there isn't any cargo left for pickup. +-- Use @{#AI_CARGO_DISPATCHER\_HELICOPTER.SetHomeZone}() to specify the home zone. +-- +-- If no home zone is specified, the helicopters will wait near the deploy zone for a new pickup command. +-- +-- === -- -- @field #AI_CARGO_DISPATCHER_HELICOPTER AI_CARGO_DISPATCHER_HELICOPTER = { @@ -32,9 +93,9 @@ AI_CARGO_DISPATCHER_HELICOPTER = { --- Creates a new AI_CARGO_DISPATCHER_HELICOPTER object. -- @param #AI_CARGO_DISPATCHER_HELICOPTER self --- @param Core.Set#SET_GROUP SetHelicopter --- @param Core.Set#SET_CARGO SetCargo --- @param Core.Set#SET_ZONE SetDeployZone +-- @param Core.Set#SET_GROUP SetHelicopter The collection of Helicopter @{Group}s. +-- @param Core.Set#SET_CARGO SetCargo The collection of @{Cargo} derived objects. +-- @param Core.Set#SET_ZONE SetDeployZone The collection of deploy @{Zone}s, which are used to where the cargo will be deployed by the Helicopters. -- @return #AI_CARGO_DISPATCHER_HELICOPTER -- @usage -- @@ -52,8 +113,8 @@ function AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargo, SetDeployZ self:SetPickupSpeed( 200, 150 ) self:SetPickupRadius( 0, 0 ) self:SetDeployRadius( 0, 0 ) - - self:Monitor( 1 ) + + self:__Start( 1 ) return self end diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 1efa9651b..ced18e18e 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -114,6 +114,14 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) -- @param #number Delay + -- We need to capture the Crash events for the helicopters. + -- The helicopter reference is used in the semaphore AI_CARGO_QUEUEU. + -- So, we need to unlock this when the helo is not anymore ... + Helicopter:HandleEvent( EVENTS.Crash, + function( Helicopter, EventData ) + AI_CARGO_QUEUE[Helicopter] = nil + end + ) self:SetCarrier( Helicopter ) @@ -225,7 +233,7 @@ function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordina local HelicopterInZone = false - if Helicopter and Helicopter:IsAlive() then + if Helicopter and Helicopter:IsAlive() == true then local Distance = Coordinate:DistanceFromPointVec2( Helicopter:GetCoordinate() ) @@ -283,6 +291,8 @@ function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordina self:__Queue( -10, Coordinate ) end end + else + AI_CARGO_QUEUE[Helicopter] = nil end end diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index e00d350db..21fdde9dd 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -76,10 +76,35 @@ SET_BASE = { function SET_BASE:New( Database ) -- Inherits from BASE - local self = BASE:Inherit( self, BASE:New() ) -- Core.Set#SET_BASE + local self = BASE:Inherit( self, FSM:New() ) -- Core.Set#SET_BASE self.Database = Database + self:SetStartState( "Started" ) + + --- Added Handler OnAfter for SET_BASE + -- @function [parent=#SET_BASE] OnAfterAdded + -- @param #SET_BASE self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #string ObjectName The name of the object. + -- @param Object The object. + + + self:AddTransition( "*", "Added", "*" ) + + --- Removed Handler OnAfter for SET_BASE + -- @function [parent=#SET_BASE] OnAfterRemoved + -- @param #SET_BASE self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #string ObjectName The name of the object. + -- @param Object The object. + + self:AddTransition( "*", "Removed", "*" ) + self.YieldInterval = 10 self.TimeInterval = 0.001 @@ -148,7 +173,8 @@ end --- Removes a @{Base#BASE} object from the @{Set#SET_BASE} and derived classes, based on the Object Name. -- @param #SET_BASE self -- @param #string ObjectName -function SET_BASE:Remove( ObjectName ) +-- @param NoTriggerEvent (optional) When `true`, the :Remove() method will not trigger a **Removed** event. +function SET_BASE:Remove( ObjectName, NoTriggerEvent ) self:F2( { ObjectName = ObjectName } ) local Object = self.Set[ObjectName] @@ -161,9 +187,11 @@ function SET_BASE:Remove( ObjectName ) break end end - + -- When NoTriggerEvent is true, then no Removed event will be triggered. + if not NoTriggerEvent then + self:Removed( ObjectName, Object ) + end end - end @@ -177,10 +205,12 @@ function SET_BASE:Add( ObjectName, 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 - self:Remove( ObjectName ) + self:Remove( ObjectName, true ) end self.Set[ObjectName] = Object table.insert( self.Index, ObjectName ) + + self:Added( ObjectName, Object ) end --- Adds a @{Base#BASE} object in the @{Set#SET_BASE}, using the Object Name as the index. @@ -425,7 +455,7 @@ end -- @param #SET_BASE self -- @param Core.Event#EVENTDATA Event function SET_BASE:_EventOnDeadOrCrash( Event ) - self:F3( { Event } ) + self:F( { Event } ) if Event.IniDCSUnit then local ObjectName, Object = self:FindInDatabase( Event ) @@ -675,6 +705,52 @@ end -- * @{#SET_GROUP.ForEachGroupPartlyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence partly in a @{Zone}, providing the GROUP and optional parameters to the called function. -- * @{#SET_GROUP.ForEachGroupNotInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. -- +-- +-- ## 5. SET_GROUP trigger events on the GROUP objects. +-- +-- The SET is derived from the FSM class, which provides extra capabilities to track the contents of the GROUP objects in the SET_GROUP. +-- +-- ### 5.1. When a GROUP object crashes or is dead, the SET_GROUP will trigger a **Dead** event. +-- +-- You can handle the event using the OnBefore and OnAfter event handlers. +-- The event handlers need to have the paramters From, Event, To, GroupObject. +-- The GroupObject is the GROUP object that is dead and within the SET_GROUP, and is passed as a parameter to the event handler. +-- See the following example: +-- +-- -- Create the SetCarrier SET_GROUP collection. +-- +-- local SetHelicopter = SET_GROUP:New():FilterPrefixes( "Helicopter" ):FilterStart() +-- +-- -- Put a Dead event handler on SetCarrier, to ensure that when a carrier is destroyed, that all internal parameters are reset. +-- +-- function SetHelicopter:OnAfterDead( From, Event, To, GroupObject ) +-- self:F( { GroupObject = GroupObject:GetName() } ) +-- end +-- +-- While this is a good example, there is a catch. +-- Imageine you want to execute the code above, the the self would need to be from the object declared outside (above) the OnAfterDead method. +-- So, the self would need to contain another object. Fortunately, this can be done, but you must use then the **`.`** notation for the method. +-- See the modified example: +-- +-- -- Now we have a constructor of the class AI_CARGO_DISPATCHER, that receives the SetHelicopter as a parameter. +-- -- Within that constructor, we want to set an enclosed event handler OnAfterDead for SetHelicopter. +-- -- But within the OnAfterDead method, we want to refer to the self variable of the AI_CARGO_DISPATCHER. +-- +-- function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZones ) +-- +-- local self = BASE:Inherit( self, FSM:New() ) -- #AI_CARGO_DISPATCHER +-- +-- -- Put a Dead event handler on SetCarrier, to ensure that when a carrier is destroyed, that all internal parameters are reset. +-- -- Note the "." notation, and the explicit declaration of SetHelicopter, which would be using the ":" notation the implicit self variable declaration. +-- +-- function SetHelicopter.OnAfterDead( SetHelicopter, From, Event, To, GroupObject ) +-- SetHelicopter:F( { GroupObject = GroupObject:GetName() } ) +-- self.PickupCargo[GroupObject] = nil -- So here I clear the PickupCargo table entry of the self object AI_CARGO_DISPATCHER. +-- self.CarrierHome[GroupObject] = nil +-- end +-- +-- end +-- -- === -- @field #SET_GROUP SET_GROUP SET_GROUP = { @@ -946,7 +1022,7 @@ end -- @param #SET_GROUP self -- @param Core.Event#EVENTDATA Event function SET_GROUP:_EventOnDeadOrCrash( Event ) - self:F3( { Event } ) + self:F( { Event } ) if Event.IniDCSUnit then local ObjectName, Object = self:FindInDatabase( Event ) @@ -1409,6 +1485,59 @@ do -- SET_UNIT -- -- * @{#SET_UNIT.GetTypeNames}(): Retrieve the type names of the @{Unit}s in the SET, delimited by a comma. -- + -- ## 4. SET_UNIT iterators + -- + -- Once the filters have been defined and the SET_UNIT has been built, you can iterate the SET_UNIT with the available iterator methods. + -- The iterator methods will walk the SET_UNIT set, and call for each element within the set a function that you provide. + -- The following iterator methods are currently available within the SET_UNIT: + -- + -- * @{#SET_UNIT.ForEachUnit}: Calls a function for each alive group it finds within the SET_UNIT. + -- * @{#SET_UNIT.ForEachUnitInZone}: Iterate the SET_UNIT and call an iterator function for each **alive** UNIT object presence completely in a @{Zone}, providing the UNIT object and optional parameters to the called function. + -- * @{#SET_UNIT.ForEachUnitNotInZone}: Iterate the SET_UNIT and call an iterator function for each **alive** UNIT object presence not in a @{Zone}, providing the UNIT object and optional parameters to the called function. + -- + -- ## 5. SET_UNIT trigger events on the UNIT objects. + -- + -- The SET is derived from the FSM class, which provides extra capabilities to track the contents of the UNIT objects in the SET_UNIT. + -- + -- ### 5.1. When a UNIT object crashes or is dead, the SET_UNIT will trigger a **Dead** event. + -- + -- You can handle the event using the OnBefore and OnAfter event handlers. + -- The event handlers need to have the paramters From, Event, To, GroupObject. + -- The GroupObject is the UNIT object that is dead and within the SET_UNIT, and is passed as a parameter to the event handler. + -- See the following example: + -- + -- -- Create the SetCarrier SET_UNIT collection. + -- + -- local SetHelicopter = SET_UNIT:New():FilterPrefixes( "Helicopter" ):FilterStart() + -- + -- -- Put a Dead event handler on SetCarrier, to ensure that when a carrier unit is destroyed, that all internal parameters are reset. + -- + -- function SetHelicopter:OnAfterDead( From, Event, To, UnitObject ) + -- self:F( { UnitObject = UnitObject:GetName() } ) + -- end + -- + -- While this is a good example, there is a catch. + -- Imageine you want to execute the code above, the the self would need to be from the object declared outside (above) the OnAfterDead method. + -- So, the self would need to contain another object. Fortunately, this can be done, but you must use then the **`.`** notation for the method. + -- See the modified example: + -- + -- -- Now we have a constructor of the class AI_CARGO_DISPATCHER, that receives the SetHelicopter as a parameter. + -- -- Within that constructor, we want to set an enclosed event handler OnAfterDead for SetHelicopter. + -- -- But within the OnAfterDead method, we want to refer to the self variable of the AI_CARGO_DISPATCHER. + -- + -- function ACLASS:New( SetCarrier, SetCargo, SetDeployZones ) + -- + -- local self = BASE:Inherit( self, FSM:New() ) -- #AI_CARGO_DISPATCHER + -- + -- -- Put a Dead event handler on SetCarrier, to ensure that when a carrier is destroyed, that all internal parameters are reset. + -- -- Note the "." notation, and the explicit declaration of SetHelicopter, which would be using the ":" notation the implicit self variable declaration. + -- + -- function SetHelicopter.OnAfterDead( SetHelicopter, From, Event, To, UnitObject ) + -- SetHelicopter:F( { UnitObject = UnitObject:GetName() } ) + -- self.array[UnitObject] = nil -- So here I clear the array table entry of the self object ACLASS. + -- end + -- + -- end -- === -- @field #SET_UNIT SET_UNIT SET_UNIT = { @@ -1649,6 +1778,8 @@ do -- SET_UNIT 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! From d07d063265981db09ff4133b604e50e5b2bff7d4 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Thu, 17 May 2018 10:24:59 +0200 Subject: [PATCH 112/420] Removed Start(). Now Start() needs to be called outside the logic. --- Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua | 2 -- 1 file changed, 2 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index 6a130eabe..f7a7a07c3 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -114,8 +114,6 @@ function AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargo, SetDeployZ self:SetPickupRadius( 0, 0 ) self:SetDeployRadius( 0, 0 ) - self:__Start( 1 ) - return self end From d05973f487844ef010fceac5d8587785547eed09 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sat, 19 May 2018 06:12:37 +0200 Subject: [PATCH 113/420] Finish Cargo/AI_Cargo_Helicopter --- .../Moose/AI/AI_Cargo_Dispatcher.lua | 41 ++++++++++++------- Moose Development/Moose/Cargo/Cargo.lua | 11 +++++ Moose Development/Moose/Cargo/CargoGroup.lua | 9 ++-- 3 files changed, 43 insertions(+), 18 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index c0eed577e..e3da2ee29 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -308,7 +308,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() self:Pickup( Carrier, Cargo ) end - function AI_Cargo.OnAfterLoad( AI_Cargo, Carrier ) + function AI_Cargo.OnAfterLoad( AI_Cargo, Carrier, From, Event, To, Cargo ) self:Loading( Carrier ) end @@ -316,16 +316,16 @@ function AI_CARGO_DISPATCHER:onafterMonitor() self:Loaded( Carrier, Cargo ) end - function AI_Cargo.OnAfterDeploy( AI_Cargo, Carrier ) - self:Deploy( Carrier ) + function AI_Cargo.OnAfterDeploy( AI_Cargo, Carrier, From, Event, To, Cargo ) + self:Deploy( Carrier, Cargo ) end - function AI_Cargo.OnAfterUnload( AI_Cargo, Carrier ) - self:Unloading( Carrier ) + function AI_Cargo.OnAfterUnload( AI_Cargo, Carrier, From, Event, To, Cargo ) + self:Unloading( Carrier, Cargo ) end - function AI_Cargo.OnAfterUnloaded( AI_Cargo, Carrier ) - self:Unloaded( Carrier ) + function AI_Cargo.OnAfterUnloaded( AI_Cargo, Carrier, From, Event, To, Cargo ) + self:Unloaded( Carrier, Cargo ) end end @@ -405,7 +405,7 @@ end -- @param #number Delay function AI_CARGO_DISPATCHER:onafterStart( From, Event, To ) - self:Monitor() + self:__Monitor( -1 ) end --- Stop Handler OnBefore for AI_CARGO_DISPATCHER @@ -434,16 +434,27 @@ end ---- Make a Carrier run for a cargo deploy action after the cargo Pickup trigger has been initiated, by default. +--- Loaded Handler OnAfter for AI_CARGO_DISPATCHER +-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterLoaded -- @param #AI_CARGO_DISPATCHER self --- @param From --- @param Event --- @param To +-- @param #string From +-- @param #string Event +-- @param #string To -- @param Wrapper.Group#GROUP Carrier -- @param Cargo.Cargo#CARGO Cargo --- @return #AI_CARGO_DISPATCHER -function AI_CARGO_DISPATCHER:OnAfterPickup( From, Event, To, Carrier, Cargo ) -end + +--- Unloaded Handler OnAfter for AI_CARGO_DISPATCHER +-- @function [parent=#AI_CARGO_DISPATCHER] OnAfterUnloaded +-- @param #AI_CARGO_DISPATCHER self +-- @param #string From +-- @param #string Event +-- @param #string To +-- @param Wrapper.Group#GROUP Carrier +-- @param Cargo.Cargo#CARGO Cargo + + + + --- Make a Carrier run for a cargo deploy action after the cargo has been loaded, by default. diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 8869a14cc..f0f99dcdb 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -352,6 +352,17 @@ do -- CARGO return self.Name end + --- Get the current active object representing or being the Cargo. + -- @param #CARGO self + -- @return Wrapper.Positionable#POSITIONABLE The object representing or being the Cargo. + function CARGO:GetObject() + if self:IsLoaded() then + return self.CargoCarrier + else + return self.CargoObject + end + end + --- Get the object name of the Cargo. -- @param #CARGO self -- @return #string The object name of the Cargo. diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 4780a8f55..ed84fd75f 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -107,7 +107,7 @@ do -- CARGO_GROUP self.CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID) -- Now we spawn the new group based on the template created. - _DATABASE:Spawn( GroupTemplate ) + self.CargoObject = _DATABASE:Spawn( GroupTemplate ) self:SetWeight( WeightGroup ) self.CargoLimit = 10 @@ -169,7 +169,10 @@ do -- CARGO_GROUP _DATABASE:Spawn( GroupTemplate ) end end + + self.CargoObject = nil end + end @@ -209,12 +212,12 @@ do -- CARGO_GROUP end -- Then we register the new group in the database - self.CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID) + self.CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID ) self:F( { "Regroup", GroupTemplate } ) -- Now we spawn the new group based on the template created. - _DATABASE:Spawn( GroupTemplate ) + self.CargoObject = _DATABASE:Spawn( GroupTemplate ) end end From d5d5d52bd5e5e21c683a4b7c91ad7f5268fe1fc0 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sat, 19 May 2018 06:55:19 +0200 Subject: [PATCH 114/420] Added Task Status Change Events as part of Dispatcher logic!!!! This is great! --- .../Moose/Tasking/DetectionManager.lua | 42 +++++++++++++++++++ .../Moose/Tasking/Task_A2A_Dispatcher.lua | 19 ++++++++- .../Moose/Tasking/Task_A2G_Dispatcher.lua | 17 ++++++++ .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 18 ++++++++ .../Moose/Tasking/Task_Manager.lua | 42 +++++++++++++++++++ 5 files changed, 137 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Tasking/DetectionManager.lua b/Moose Development/Moose/Tasking/DetectionManager.lua index 6252dc0a4..5e95771dd 100644 --- a/Moose Development/Moose/Tasking/DetectionManager.lua +++ b/Moose Development/Moose/Tasking/DetectionManager.lua @@ -122,6 +122,48 @@ do -- DETECTION MANAGER -- @function [parent=#DETECTION_MANAGER] __Stop -- @param #DETECTION_MANAGER self -- @param #number Delay + + self:AddTransition( "Started", "Success", "Started" ) + + --- Success Handler OnAfter for DETECTION_MANAGER + -- @function [parent=#DETECTION_MANAGER] OnAfterSuccess + -- @param #DETECTION_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Tasking.Task#TASK Task + + + self:AddTransition( "Started", "Failed", "Started" ) + + --- Failed Handler OnAfter for DETECTION_MANAGER + -- @function [parent=#DETECTION_MANAGER] OnAfterFailed + -- @param #DETECTION_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Tasking.Task#TASK Task + + + self:AddTransition( "Started", "Aborted", "Started" ) + + --- Aborted Handler OnAfter for DETECTION_MANAGER + -- @function [parent=#DETECTION_MANAGER] OnAfterAborted + -- @param #DETECTION_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Tasking.Task#TASK Task + + self:AddTransition( "Started", "Cancelled", "Started" ) + + --- Cancelled Handler OnAfter for DETECTION_MANAGER + -- @function [parent=#DETECTION_MANAGER] OnAfterCancelled + -- @param #DETECTION_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Tasking.Task#TASK Task self:AddTransition( "Started", "Report", "Started" ) diff --git a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua index a3fc20c88..e385fdd02 100644 --- a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua @@ -200,6 +200,7 @@ do -- TASK_A2A_DISPATCHER self.Detection:SetRefreshTimeInterval( 30 ) self:AddTransition( "Started", "Assign", "Started" ) + --- OnAfter Transition Handler for Event Assign. -- @function [parent=#TASK_A2A_DISPATCHER] OnAfterAssign @@ -210,7 +211,7 @@ do -- TASK_A2A_DISPATCHER -- @param Tasking.Task_A2A#TASK_A2A Task -- @param Wrapper.Unit#UNIT TaskUnit -- @param #string PlayerName - + self:__Start( 5 ) return self @@ -561,6 +562,22 @@ do -- TASK_A2A_DISPATCHER Task:SetTargetZone( DetectedZone, DetectedItem.Coordinate.y, DetectedItem.Coordinate.Heading ) Task:SetDispatcher( self ) Mission:AddTask( Task ) + + function Task.OnEnterSuccess( Task, From, Event, To ) + self:Success( Task ) + end + + function Task.onenterCancelled( Task, From, Event, To ) + self:Cancelled( Task ) + end + + function Task.onenterFailed( Task, From, Event, To ) + self:Failed( Task ) + end + + function Task.onenterAborted( Task, From, Event, To ) + self:Aborted( Task ) + end TaskReport:Add( Task:GetName() ) else diff --git a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua index 256ca2db2..dce26950b 100644 --- a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua @@ -761,6 +761,23 @@ do -- TASK_A2G_DISPATCHER Task:SetDispatcher( self ) Task:UpdateTaskInfo( DetectedItem ) Mission:AddTask( Task ) + + function Task.OnEnterSuccess( Task, From, Event, To ) + self:Success( Task ) + end + + function Task.onenterCancelled( Task, From, Event, To ) + self:Cancelled( Task ) + end + + function Task.onenterFailed( Task, From, Event, To ) + self:Failed( Task ) + end + + function Task.onenterAborted( Task, From, Event, To ) + self:Aborted( Task ) + end + TaskReport:Add( Task:GetName() ) else diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index 7b3967d5e..1b8dd610f 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -578,6 +578,24 @@ do -- TASK_CARGO_DISPATCHER else Transport.Task:SetDeployZones( self.DefaultDeployZones or {} ) end + + function Transport.Task.OnEnterSuccess( Task, From, Event, To ) + self:Success( Task ) + end + + function Transport.Task.onenterCancelled( Task, From, Event, To ) + self:Cancelled( Task ) + end + + function Transport.Task.onenterFailed( Task, From, Event, To ) + self:Failed( Task ) + end + + function Transport.Task.onenterAborted( Task, From, Event, To ) + self:Aborted( Task ) + end + + end end diff --git a/Moose Development/Moose/Tasking/Task_Manager.lua b/Moose Development/Moose/Tasking/Task_Manager.lua index 340fd8233..fe61dac24 100644 --- a/Moose Development/Moose/Tasking/Task_Manager.lua +++ b/Moose Development/Moose/Tasking/Task_Manager.lua @@ -111,6 +111,48 @@ do -- TASK_MANAGER self:AddTransition( "Started", "Manage", "Started" ) + self:AddTransition( "Started", "Success", "Started" ) + + --- Success Handler OnAfter for TASK_MANAGER + -- @function [parent=#TASK_MANAGER] OnAfterSuccess + -- @param #TASK_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Tasking.Task#TASK Task + + + self:AddTransition( "Started", "Failed", "Started" ) + + --- Failed Handler OnAfter for TASK_MANAGER + -- @function [parent=#TASK_MANAGER] OnAfterFailed + -- @param #TASK_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Tasking.Task#TASK Task + + + self:AddTransition( "Started", "Aborted", "Started" ) + + --- Aborted Handler OnAfter for TASK_MANAGER + -- @function [parent=#TASK_MANAGER] OnAfterAborted + -- @param #TASK_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Tasking.Task#TASK Task + + self:AddTransition( "Started", "Cancelled", "Started" ) + + --- Cancelled Handler OnAfter for TASK_MANAGER + -- @function [parent=#TASK_MANAGER] OnAfterCancelled + -- @param #TASK_MANAGER self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Tasking.Task#TASK Task + self:SetRefreshTimeInterval( 30 ) return self From cdecf4a4c9879e430739ddd26ad7b2644820c6f3 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sat, 19 May 2018 08:08:08 +0200 Subject: [PATCH 115/420] Finish Cargo/AI_Cargo_Helicopter --- Moose Development/Moose/Core/Spawn.lua | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 5797ce1d0..fca5971f5 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -444,15 +444,30 @@ end -- and any spaces before and after the resulting name are removed. -- IMPORTANT! This method MUST be the first used after :New !!! -- @param #SPAWN self +-- @param #boolean KeepUnitNames (optional) If true, the unit names are kept, false or not provided to make new unit names. -- @return #SPAWN self -function SPAWN:InitKeepUnitNames() +function SPAWN:InitKeepUnitNames( KeepUnitNames ) self:F( ) - self.SpawnInitKeepUnitNames = true + self.SpawnInitKeepUnitNames = KeepUnitNames or true return self end + +--- Flags that the spawned groups must be spawned late activated. +-- @param #SPAWN self +-- @param #boolean LateActivated (optional) If true, the spawned groups are late activated. +-- @return #SPAWN self +function SPAWN:InitLateActivated( LateActivated ) + self:F( ) + + self.LateActivated = LateActivated or true + + return self +end + + --- Defines the Heading for the new spawned units. -- The heading can be given as one fixed degree, or can be randomized between minimum and maximum degrees. -- @param #SPAWN self @@ -1915,7 +1930,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) --R2.2 SpawnTemplate.groupId = nil --SpawnTemplate.lateActivation = false - SpawnTemplate.lateActivation = false + SpawnTemplate.lateActivation = self.LateActivated or false if SpawnTemplate.CategoryID == Group.Category.GROUND then self:T3( "For ground units, visible needs to be false..." ) From 0b378063c0d6ca225ebad763a783c3e4285dff12 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sun, 20 May 2018 08:37:02 +0200 Subject: [PATCH 116/420] - Fixed infantry deploying when helicopter was hit. - Fixed further landing coordinate lockups. --- .../Moose/AI/AI_Cargo_Helicopter.lua | 65 +++++++++++------ Moose Development/Moose/Wrapper/Group.lua | 72 +++++++++++++++++++ 2 files changed, 115 insertions(+), 22 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index ced18e18e..643d4e529 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -115,7 +115,7 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) -- We need to capture the Crash events for the helicopters. - -- The helicopter reference is used in the semaphore AI_CARGO_QUEUEU. + -- The helicopter reference is used in the semaphore AI_CARGO_QUEUE. -- So, we need to unlock this when the helo is not anymore ... Helicopter:HandleEvent( EVENTS.Crash, function( Helicopter, EventData ) @@ -123,6 +123,20 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) end ) + -- We need to capture the Land events for the helicopters. + -- The helicopter reference is used in the semaphore AI_CARGO_QUEUE. + -- So, we need to unlock this when the helo has landed, which can be anywhere ... + -- But only free the landing coordinate after 1 minute, to ensure that all helos have left. + Helicopter:HandleEvent( EVENTS.Land, + function( Helicopter, EventData ) + self:ScheduleOnce( 60, + function( Helicopter ) + AI_CARGO_QUEUE[Helicopter] = nil + end, Helicopter + ) + end + ) + self:SetCarrier( Helicopter ) return self @@ -169,19 +183,6 @@ function AI_CARGO_HELICOPTER:SetCarrier( Helicopter ) end end - - function Helicopter:OnEventHit( EventData ) - local AICargoTroops = self:GetState( self, "AI_CARGO_HELICOPTER" ) - if AICargoTroops then - self:F( { OnHitLoaded = AICargoTroops:Is( "Loaded" ) } ) - if AICargoTroops:Is( "Loaded" ) or AICargoTroops:Is( "Boarding" ) then - -- There are enemies within combat range. Unload the Helicopter. - AICargoTroops:Unload() - end - end - end - - function Helicopter:OnEventLand( EventData ) AICargo:Landed() end @@ -203,19 +204,34 @@ end -- @param #number Speed function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To ) + Helicopter:F( { Name = Helicopter:GetName() } ) + if Helicopter and Helicopter:IsAlive() then + -- S_EVENT_LAND is directly called in two situations: + -- 1 - When the helo lands normally on the ground. + -- 2 - when the helo is hit and goes RTB or even when it is destroyed. + -- For point 2, this is an issue, the infantry may not unload in this case! + -- So we check if the helo is on the ground, and velocity< 5. + -- Only then the infantry can unload (and load too, for consistency)! + + self:F( { Helicopter:GetName(), Height = Helicopter:GetHeight( true ), Velocity = Helicopter:GetVelocityKMH() } ) + if self.RoutePickup == true then - self:Load( Helicopter:GetPointVec2() ) - self.RoutePickup = false - self.Relocating = true + if Helicopter:GetHeight( true ) <= 2 and Helicopter:GetVelocityKMH() < 5 then + self:Load( Helicopter:GetPointVec2() ) + self.RoutePickup = false + self.Relocating = true + end end if self.RouteDeploy == true then - self:Unload( true ) - self.RouteDeploy = false - self.Transporting = false - self.Relocating = false + if Helicopter:GetHeight( true ) <= 2 and Helicopter:GetVelocityKMH() < 5 then + self:Unload( true ) + self.RouteDeploy = false + self.Transporting = false + self.Relocating = false + end end end @@ -498,7 +514,12 @@ function AI_CARGO_HELICOPTER:onafterUnloaded( Helicopter, From, Event, To, Cargo self:Orbit( Helicopter:GetCoordinate(), 50 ) - AI_CARGO_QUEUE[Helicopter] = nil + -- Free the coordinate zone after 30 seconds, so that the original helicopter can fly away first. + self:ScheduleOnce( 30, + function( Helicopter ) + AI_CARGO_QUEUE[Helicopter] = nil + end, Helicopter + ) end diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index c595a5dbb..6f45f873d 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -456,6 +456,78 @@ function GROUP:GetSize() return nil end + +--- Returns the average velocity Vec3 vector. +-- @param Wrapper.Group#GROUP self +-- @return Dcs.DCSTypes#Vec3 The velocity Vec3 vector +-- @return #nil The GROUP is not existing or alive. +function GROUP:GetVelocityVec3() + self:F2( self.GroupName ) + + local DCSGroup = self:GetDCSObject() + + if DCSGroup and DCSGroup:isExist() then + local GroupUnits = DCSGroup:getUnits() + local GroupCount = #GroupUnits + + local VelocityVec3 = { x = 0, y = 0, z = 0 } + + for _, DCSUnit in pairs( GroupUnits ) do + local UnitVelocityVec3 = DCSUnit:getVelocity() + VelocityVec3.x = VelocityVec3.x + UnitVelocityVec3.x + VelocityVec3.y = VelocityVec3.y + UnitVelocityVec3.y + VelocityVec3.z = VelocityVec3.z + UnitVelocityVec3.z + end + + VelocityVec3.x = VelocityVec3.x / GroupCount + VelocityVec3.y = VelocityVec3.y / GroupCount + VelocityVec3.z = VelocityVec3.z / GroupCount + + return VelocityVec3 + end + + BASE:E( { "Cannot GetVelocityVec3", Group = self, Alive = self:IsAlive() } ) + + return nil +end + + +--- Returns the average group height in meters. +-- @param Wrapper.Group#GROUP self +-- @param #boolean FromGround Measure from the ground or from sea level. Provide **true** for measuring from the ground. **false** or **nil** if you measure from sea level. +-- @return Dcs.DCSTypes#Vec3 The height of the group. +-- @return #nil The GROUP is not existing or alive. +function GROUP:GetHeight( FromGround ) + self:F2( self.GroupName ) + + local DCSGroup = self:GetDCSObject() + + if DCSGroup then + local GroupUnits = DCSGroup:getUnits() + local GroupCount = #GroupUnits + + local GroupHeight = 0 + + for _, DCSUnit in pairs( GroupUnits ) do + local GroupPosition = DCSUnit:getPosition() + + if FromGround == true then + local LandHeight = land.getHeight( { GroupPosition.p.x, GroupPosition.p.z } ) + GroupHeight = GroupHeight + ( GroupPosition.p.y - LandHeight ) + else + GroupHeight = GroupHeight + GroupPosition.p.y + end + end + + return GroupHeight / GroupCount + end + + return nil +end + + + + --- --- Returns the initial size of the DCS Group. -- If some of the DCS Units of the DCS Group are destroyed, the initial size of the DCS Group is unchanged. From adbbeafef9a768b282f520c103f425f572834bf8 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sun, 20 May 2018 09:08:34 +0200 Subject: [PATCH 117/420] Removed one obscolete line. --- Moose Development/Moose/Core/Event.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 054f5617c..5b42a1d8a 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -212,7 +212,6 @@ EVENTS = { MarkAdded = world.event.S_EVENT_MARK_ADDED, MarkChange = world.event.S_EVENT_MARK_CHANGE, MarkRemoved = world.event.S_EVENT_MARK_REMOVED, - ShootingEnd = world.event.S_EVENT_SHOOTING_END, NewCargo = world.event.S_EVENT_NEW_CARGO, DeleteCargo = world.event.S_EVENT_DELETE_CARGO, NewZone = world.event.S_EVENT_NEW_ZONE, From d8a189b1fd33769d1109d67feb891bf4b655c192 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 21 May 2018 09:02:15 +0200 Subject: [PATCH 118/420] Finish ZONE_tags --- Moose Development/Moose/Cargo/CargoGroup.lua | 2 +- Moose Development/Moose/Core/Database.lua | 51 +++++++++++++----- Moose Development/Moose/Core/Zone.lua | 56 ++++++++++++++++++++ Moose Development/Moose/Moose.lua | 3 +- 4 files changed, 97 insertions(+), 15 deletions(-) diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index ed84fd75f..5af3978c4 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -78,7 +78,7 @@ do -- CARGO_GROUP self.CargoGroup:Destroy() local GroupName = CargoGroup:GetName() - self.CargoName = GroupName:match("(.*)~CARGO") or GroupName + self.CargoName = Name self.CargoTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplate( GroupName ) ) local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate ) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 995d243a3..90197b34d 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -289,8 +289,37 @@ do -- Zones end + --- Private method that registers new ZONE_BASE derived objects within the DATABASE Object. + -- @param #DATABASE self + -- @return #DATABASE self + function DATABASE:_RegisterZones() -end + for ZoneID, ZoneData in pairs( env.mission.triggers.zones ) do + local ZoneName = ZoneData.name + + self:I( { "Register ZONE:", Name = ZoneName } ) + local Zone = ZONE:New( ZoneName ) + self.ZONENAMES[ZoneName] = ZoneName + self:AddZone( ZoneName, Zone ) + end + + for ZoneGroupName, ZoneGroup in pairs( self.GROUPS ) do + if ZoneGroupName:match("~ZONE_POLYGON") then + local ZoneName1 = ZoneGroupName:match("(.*)~ZONE_POLYGON") + local ZoneName2 = ZoneGroupName:match(".*~ZONE_POLYGON(.*)") + local ZoneName = ZoneName1 .. ( ZoneName2 or "" ) + + self:I( { "Register ZONE_POLYGON:", Name = ZoneName } ) + local Zone_Polygon = ZONE_POLYGON:New( ZoneName, ZoneGroup ) + self.ZONENAMES[ZoneName] = ZoneName + self:AddZone( ZoneName, Zone_Polygon ) + end + end + + end + + +end -- zone do -- cargo @@ -341,20 +370,23 @@ do -- cargo --- Private method that registers new Static Templates within the DATABASE Object. -- @param #DATABASE self -- @return #DATABASE self - function DATABASE:RegisterCargos() + function DATABASE:_RegisterCargos() for CargoGroupName, CargoGroup in pairs( self.GROUPS ) do if self:IsCargo( CargoGroupName ) then local CargoInfo = CargoGroupName:match("~CARGO(.*)") local CargoParam = CargoInfo and CargoInfo:match( "%((.*)%)") - local CargoName = CargoGroupName:match("(.*)~CARGO") + local CargoName1 = CargoGroupName:match("(.*)~CARGO%(.*%)") + local CargoName2 = CargoGroupName:match(".*~CARGO%(.*%)(.*)") + self:E({CargoName1 = CargoName1, CargoName2 = CargoName2 }) + local CargoName = CargoName1 .. ( CargoName2 or "" ) local Type = CargoParam and CargoParam:match( "T=([%a%d ]+),?") local Name = CargoParam and CargoParam:match( "N=([%a%d]+),?") or CargoName local LoadRadius = CargoParam and tonumber( CargoParam:match( "RR=([%a%d]+),?") ) local NearRadius = CargoParam and tonumber( CargoParam:match( "NR=([%a%d]+),?") ) - self:F({"Register CargoGroup:",Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius}) + self:I({"Register CargoGroup:",Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius}) CARGO_GROUP:New( CargoGroup, Type, Name, LoadRadius, NearRadius ) end end @@ -371,11 +403,11 @@ do -- cargo local NearRadius = CargoParam and tonumber( CargoParam:match( "NR=([%a%d]+),?") ) if Category == "SLING" then - self:F({"Register CargoSlingload:",Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius}) + self:I({"Register CargoSlingload:",Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius}) CARGO_SLINGLOAD:New( CargoStatic, Type, Name, LoadRadius, NearRadius ) else if Category == "CRATE" then - self:F({"Register CargoCrate:",Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius}) + self:I({"Register CargoCrate:",Type=Type,Name=Name,LoadRadius=LoadRadius,NearRadius=NearRadius}) CARGO_CRATE:New( CargoStatic, Type, Name, LoadRadius, NearRadius ) end end @@ -1218,13 +1250,6 @@ function DATABASE:_RegisterTemplates() end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then end --for coa_name, coa_data in pairs(mission.coalition) do - for ZoneID, ZoneData in pairs( env.mission.triggers.zones ) do - local ZoneName = ZoneData.name - self.ZONENAMES[ZoneName] = ZoneName - self:AddZone( ZoneName, ZONE:New( ZoneName ) ) - self:I( "Added ZONE " .. ZoneName ) - end - return self end diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 203105575..938fde87a 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -956,6 +956,30 @@ end -- The ZONE class, defined by the zone name as defined within the Mission Editor. -- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. -- +-- ## ZONE constructor +-- +-- * @{#ZONE.New}(): Constructor. This will search for a trigger zone with the name given, and will return for you a ZONE object. +-- +-- ## Declare a ZONE directly in the DCS mission editor! +-- +-- You can declare a ZONE using the DCS mission editor by adding a trigger zone in the mission editor. +-- +-- Then during mission startup, when loading Moose.lua, this trigger zone will be detected as a ZONE declaration. +-- Within the background, a ZONE object will be created within the @{Database}. +-- The ZONE name will be the trigger zone name. +-- +-- So, you can search yourself for the ZONE object by using the @{#ZONE.FindByName}() method. +-- In this example, `local TriggerZone = ZONE:FindByName( "DefenseZone" )` would return the ZONE object +-- that was created at mission startup, and reference it into the `TriggerZone` local object. +-- +-- Refer to mission `ZON-110` for a demonstration. +-- +-- This is especially handy if you want to quickly setup a SET_ZONE... +-- So when you would declare `local SetZone = SET_ZONE:New():FilterPrefixes( "Defense" ):FilterStart()`, +-- then SetZone would contain the ZONE object `DefenseZone` as part of the zone collection, +-- without much scripting overhead!!! +-- +-- -- @field #ZONE ZONE = { ClassName="ZONE", @@ -1455,6 +1479,26 @@ end -- The ZONE_POLYGON class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. -- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. -- +-- ## Declare a ZONE_POLYGON directly in the DCS mission editor! +-- +-- You can declare a ZONE_POLYGON using the DCS mission editor by adding the ~ZONE_POLYGON tag in the group name. +-- +-- So, imagine you have a group declared in the mission editor, with group name `DefenseZone~ZONE_POLYGON`. +-- Then during mission startup, when loading Moose.lua, this group will be detected as a ZONE_POLYGON declaration. +-- Within the background, a ZONE_POLYGON object will be created within the @{Database} using the properties of the group. +-- The ZONE_POLYGON name will be the group name without the ~ZONE_POLYGON tag. +-- +-- So, you can search yourself for the ZONE_POLYGON by using the @{#ZONE_POLYGON.FindByName}() method. +-- In this example, `local PolygonZone = ZONE_POLYGON:FindByName( "DefenseZone" )` would return the ZONE_POLYGON object +-- that was created at mission startup, and reference it into the `PolygonZone` local object. +-- +-- Mission `ZON-510` shows a demonstration of this feature or method. +-- +-- This is especially handy if you want to quickly setup a SET_ZONE... +-- So when you would declare `local SetZone = SET_ZONE:New():FilterPrefixes( "Defense" ):FilterStart()`, +-- then SetZone would contain the ZONE_POLYGON object `DefenseZone` as part of the zone collection, +-- without much scripting overhead!!! +-- -- @field #ZONE_POLYGON ZONE_POLYGON = { ClassName="ZONE_POLYGON", @@ -1495,3 +1539,15 @@ function ZONE_POLYGON:NewFromGroupName( GroupName ) return self end + +--- Find a polygon zone in the _DATABASE using the name of the polygon zone. +-- @param #ZONE_POLYGON self +-- @param #string ZoneName The name of the polygon zone. +-- @return #ZONE_POLYGON self +function ZONE_POLYGON:FindByName( ZoneName ) + + local ZoneFound = _DATABASE:FindZone( ZoneName ) + return ZoneFound +end + + diff --git a/Moose Development/Moose/Moose.lua b/Moose Development/Moose/Moose.lua index 6a748922a..05f3b8cf3 100644 --- a/Moose Development/Moose/Moose.lua +++ b/Moose Development/Moose/Moose.lua @@ -13,5 +13,6 @@ _DATABASE = DATABASE:New() -- Core.Database#DATABASE _SETTINGS = SETTINGS:Set() _SETTINGS:SetPlayerMenuOn() -_DATABASE:RegisterCargos() +_DATABASE:_RegisterCargos() +_DATABASE:_RegisterZones() From be48f7751cc1e2458bee278f4e5e66876a3ac5dd Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 21 May 2018 20:39:13 +0200 Subject: [PATCH 119/420] Test --- .appveyor/appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.appveyor/appveyor.yml b/.appveyor/appveyor.yml index ce7a2f7b6..e393ede16 100644 --- a/.appveyor/appveyor.yml +++ b/.appveyor/appveyor.yml @@ -61,7 +61,7 @@ build_script: $project = Invoke-RestMethod -method Post -Uri "$apiUrl/builds" -Headers $headers -Body $RequestBody } - ps: | - if( $env:appveyor_repo_branch -eq 'master' ) + if( $env:appveyor_repo_branch -eq 'master' -or $env:appveyor_repo_branch -eq 'develop' ) { $apiUrl = 'https://ci.appveyor.com/api' $token = 'qts80b5kpq0ooj4x6vvw' @@ -69,7 +69,7 @@ build_script: "Authorization" = "Bearer $token" "Content-type" = "application/json" } - $RequestBody = @{ accountName = 'FlightControl-Master'; projectSlug = 'moose-docs'; branch = 'master'; environmentVariables = @{} } | ConvertTo-Json + $RequestBody = @{ accountName = 'FlightControl-Master'; projectSlug = 'moose-docs'; branch = branch = "$env:appveyor_repo_branch"; environmentVariables = @{} } | ConvertTo-Json # get project with last build details $project = Invoke-RestMethod -method Post -Uri "$apiUrl/builds" -Headers $headers -Body $RequestBody } From 25e4d171ab92904d84fb24ce001866468d3ef137 Mon Sep 17 00:00:00 2001 From: Frank Date: Mon, 21 May 2018 21:43:19 +0200 Subject: [PATCH 120/420] Added GetSpeedMax function --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 2 +- .../Moose/Wrapper/Controllable.lua | 10 +++---- Moose Development/Moose/Wrapper/Group.lua | 30 +++++++++++++++++++ Moose Development/Moose/Wrapper/Unit.lua | 17 +++++++++++ 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 0cb8aed9b..95a5ba04d 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -609,7 +609,7 @@ function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate ) if Coordinate then self.RoutePickup = true - local Waypoints = APC:TaskGroundOnRoad( Coordinate, 150, "Line abreast" ) + local Waypoints = APC:TaskGroundOnRoad( Coordinate, APC:GetSpeedMax()*0.8, "Line abreast" ) local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Pickup", self ) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 04576498f..0318c6c47 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1958,17 +1958,17 @@ do -- Route methods -- Route, ground waypoints along roads. local route={} - table.insert(route, FromCoordinate:WaypointGround(Speed, Formation)) + table.insert(route, FromCoordinate:WaypointGround(Speed, "Off Road")) -- Convert coordinates to ground waypoints and insert into table. for _, coord in ipairs(path) do - table.insert(route, coord:WaypointGround(Speed, Formation)) + table.insert(route, coord:WaypointGround(Speed, "On Road")) end -- Add the final coordinate because the final coordinate in path is last point on road. local dist=ToCoordinate:Get2DDistance(path[#path]) if dist>10 then - table.insert(route, ToCoordinate:WaypointGround(Speed, "Vee")) + table.insert(route, ToCoordinate:WaypointGround(Speed, "Off Road")) end -- Route controllable to destination. @@ -2059,7 +2059,7 @@ do -- Route methods PointFrom.y = ControllablePoint.y PointFrom.type = "Turning Point" PointFrom.action = Formation or "Cone" - PointFrom.speed = 20 / 1.6 + PointFrom.speed = 20 / 3.6 local PointTo = {} @@ -2084,7 +2084,7 @@ do -- Route methods if Speed then PointTo.speed = Speed else - PointTo.speed = 20 / 1.6 + PointTo.speed = 20 / 3.6 end local Points = { PointFrom, PointTo } diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 6f45f873d..dcbee2b90 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -350,6 +350,36 @@ function GROUP:GetCountry() return nil end +--- Returns the maximum speed of the group. +-- If the group is heterogenious and consists of different units, the max speed of the slowest unit is returned. +-- @param #GROUP self +-- @return #number Speed in km/h. +function GROUP:GetSpeedMax() + self:F2( self.GroupName ) + + local DCSGroup = self:GetDCSObject() + if DCSGroup then + + local Units=self:GetUnits() + + local speedmax=nil + + for _,unit in pairs(Units) do + local unit=unit --Wrapper.Unit#UNIT + local speed=unit:GetSpeedMax() + if speedmax==nil then + speedmax=speed + elseif speed The list of @{Unit} objects of the @{Group}. diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 1c4236dc3..bc5a89e3f 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -401,6 +401,23 @@ function UNIT:GetNumber() return nil end + +--- Returns the unit's max speed in km/h derived from the DCS descriptors. +-- @param #UNIT self +-- @return #number Speed in km/h. +function UNIT:GetSpeedMax() + self:F2( self.UnitName ) + + local Desc = self:GetDesc() + + if Desc then + local SpeedMax = Desc.speedMax + return SpeedMax*3.6 + end + + return nil +end + --- Returns the unit's group if it exist and nil otherwise. -- @param Wrapper.Unit#UNIT self -- @return Wrapper.Group#GROUP The Group of the Unit. From 6a4741d0dce8b8dee0d60c0b2d9572a2bb205635 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 21 May 2018 22:34:19 +0200 Subject: [PATCH 121/420] New version --- Moose Development/Moose/Dcs/DCSAirbase.lua | 2 +- Moose Development/Moose/Dcs/DCSCoalitionObject.lua | 2 +- Moose Development/Moose/Dcs/DCSCommand.lua | 2 +- Moose Development/Moose/Dcs/DCSController.lua | 2 +- Moose Development/Moose/Dcs/DCSGroup.lua | 2 +- Moose Development/Moose/Dcs/DCSObject.lua | 2 +- Moose Development/Moose/Dcs/DCSStaticObject.lua | 2 +- Moose Development/Moose/Dcs/DCSTask.lua | 2 +- Moose Development/Moose/Dcs/DCSTime.lua | 2 +- Moose Development/Moose/Dcs/DCSTypes.lua | 2 +- Moose Development/Moose/Dcs/DCSUnit.lua | 2 +- Moose Development/Moose/Dcs/DCSVec3.lua | 2 +- Moose Development/Moose/Dcs/DCSZone.lua | 2 +- Moose Development/Moose/Dcs/DCScoalition.lua | 2 +- Moose Development/Moose/Dcs/DCScountry.lua | 2 +- Moose Development/Moose/Dcs/DCSenv.lua | 2 +- Moose Development/Moose/Dcs/DCSland.lua | 2 +- Moose Development/Moose/Dcs/DCStimer.lua | 2 +- Moose Development/Moose/Dcs/DCStrigger.lua | 2 +- Moose Development/Moose/Dcs/DCSworld.lua | 2 +- 20 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Moose Development/Moose/Dcs/DCSAirbase.lua b/Moose Development/Moose/Dcs/DCSAirbase.lua index d47ddc067..192444a06 100644 --- a/Moose Development/Moose/Dcs/DCSAirbase.lua +++ b/Moose Development/Moose/Dcs/DCSAirbase.lua @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- @module DCSAirbase +-- @module DCS.DCSAirbase --- Represents airbases: airdromes, helipads and ships with flying decks or landing pads. diff --git a/Moose Development/Moose/Dcs/DCSCoalitionObject.lua b/Moose Development/Moose/Dcs/DCSCoalitionObject.lua index 119c9a807..b16ca63c2 100644 --- a/Moose Development/Moose/Dcs/DCSCoalitionObject.lua +++ b/Moose Development/Moose/Dcs/DCSCoalitionObject.lua @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- @module DCSCoalitionObject +-- @module DCS.DCSCoalitionObject --- @type CoalitionObject -- @extends Dcs.DCSWrapper.Object#Object diff --git a/Moose Development/Moose/Dcs/DCSCommand.lua b/Moose Development/Moose/Dcs/DCSCommand.lua index 5e0040332..89b2b6f9e 100644 --- a/Moose Development/Moose/Dcs/DCSCommand.lua +++ b/Moose Development/Moose/Dcs/DCSCommand.lua @@ -1,4 +1,4 @@ ---- @module DCSCommand +--- @module DCS.DCSCommand --- @type Command diff --git a/Moose Development/Moose/Dcs/DCSController.lua b/Moose Development/Moose/Dcs/DCSController.lua index 09b8d4b1e..99666ef97 100644 --- a/Moose Development/Moose/Dcs/DCSController.lua +++ b/Moose Development/Moose/Dcs/DCSController.lua @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- @module DCSController +-- @module DCS.DCSController --- Controller is an object that performs A.I.-routines. Other words controller is an instance of A.I.. Controller stores current main task, active enroute tasks and behavior options. Controller performs commands. Please, read DCS A-10C GUI Manual EN.pdf chapter "Task Planning for Unit Groups", page 91 to understand A.I. system of DCS:A-10C. -- diff --git a/Moose Development/Moose/Dcs/DCSGroup.lua b/Moose Development/Moose/Dcs/DCSGroup.lua index 04f7818bf..0e1798e07 100644 --- a/Moose Development/Moose/Dcs/DCSGroup.lua +++ b/Moose Development/Moose/Dcs/DCSGroup.lua @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- @module DCSGroup +-- @module DCS.DCSGroup --- Represents group of Units. -- @type Group diff --git a/Moose Development/Moose/Dcs/DCSObject.lua b/Moose Development/Moose/Dcs/DCSObject.lua index 281e2781a..fb09234d4 100644 --- a/Moose Development/Moose/Dcs/DCSObject.lua +++ b/Moose Development/Moose/Dcs/DCSObject.lua @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- @module DCSObject +-- @module DCS.DCSObject --- @type Object -- @field #Object.Category Category diff --git a/Moose Development/Moose/Dcs/DCSStaticObject.lua b/Moose Development/Moose/Dcs/DCSStaticObject.lua index 5dc220412..e01f017da 100644 --- a/Moose Development/Moose/Dcs/DCSStaticObject.lua +++ b/Moose Development/Moose/Dcs/DCSStaticObject.lua @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- @module DCSStaticObject +-- @module DCS.DCSStaticObject ------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Dcs/DCSTask.lua b/Moose Development/Moose/Dcs/DCSTask.lua index 4b4cd277a..825b9f0bc 100644 --- a/Moose Development/Moose/Dcs/DCSTask.lua +++ b/Moose Development/Moose/Dcs/DCSTask.lua @@ -1,4 +1,4 @@ ---- @module DCSTask +--- @module DCS.DCSTask --- A task descriptor (internal structure for DCS World) diff --git a/Moose Development/Moose/Dcs/DCSTime.lua b/Moose Development/Moose/Dcs/DCSTime.lua index d70d337ec..766f53317 100644 --- a/Moose Development/Moose/Dcs/DCSTime.lua +++ b/Moose Development/Moose/Dcs/DCSTime.lua @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- @module DCSTime +-- @module DCS.DCSTime --- @type ModelTime -- @extends #number diff --git a/Moose Development/Moose/Dcs/DCSTypes.lua b/Moose Development/Moose/Dcs/DCSTypes.lua index 176725302..bb00369e4 100644 --- a/Moose Development/Moose/Dcs/DCSTypes.lua +++ b/Moose Development/Moose/Dcs/DCSTypes.lua @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- @module DCSTypes +-- @module DCS.DCSTypes diff --git a/Moose Development/Moose/Dcs/DCSUnit.lua b/Moose Development/Moose/Dcs/DCSUnit.lua index e5e86aba2..723b653d1 100644 --- a/Moose Development/Moose/Dcs/DCSUnit.lua +++ b/Moose Development/Moose/Dcs/DCSUnit.lua @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- @module DCSUnit +-- @module DCS.DCSUnit --- @type Unit -- @extends Dcs.DCSCoalitionWrapper.Object#CoalitionObject diff --git a/Moose Development/Moose/Dcs/DCSVec3.lua b/Moose Development/Moose/Dcs/DCSVec3.lua index 6de2c20aa..0128e1a7a 100644 --- a/Moose Development/Moose/Dcs/DCSVec3.lua +++ b/Moose Development/Moose/Dcs/DCSVec3.lua @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- @module DCSVec3 +-- @module DCS.DCSVec3 diff --git a/Moose Development/Moose/Dcs/DCSZone.lua b/Moose Development/Moose/Dcs/DCSZone.lua index 4b65bd76e..6070d8cc9 100644 --- a/Moose Development/Moose/Dcs/DCSZone.lua +++ b/Moose Development/Moose/Dcs/DCSZone.lua @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- @module DCSZone +-- @module DCS.DCSZone diff --git a/Moose Development/Moose/Dcs/DCScoalition.lua b/Moose Development/Moose/Dcs/DCScoalition.lua index e51508a3e..309492fd0 100644 --- a/Moose Development/Moose/Dcs/DCScoalition.lua +++ b/Moose Development/Moose/Dcs/DCScoalition.lua @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- @module DCScoalition +-- @module DCS.DCScoalition --- @type coalition -- @field #coalition.side side diff --git a/Moose Development/Moose/Dcs/DCScountry.lua b/Moose Development/Moose/Dcs/DCScountry.lua index e390894cd..32ff4c34e 100644 --- a/Moose Development/Moose/Dcs/DCScountry.lua +++ b/Moose Development/Moose/Dcs/DCScountry.lua @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- @module DCScountry +-- @module DCS.DCScountry --- @type country -- @field #country.id id diff --git a/Moose Development/Moose/Dcs/DCSenv.lua b/Moose Development/Moose/Dcs/DCSenv.lua index c6fe98776..8b71f0f9e 100644 --- a/Moose Development/Moose/Dcs/DCSenv.lua +++ b/Moose Development/Moose/Dcs/DCSenv.lua @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- @module env +-- @module DCS.DCSenv --- @type env diff --git a/Moose Development/Moose/Dcs/DCSland.lua b/Moose Development/Moose/Dcs/DCSland.lua index 6391142af..5e4d2c77c 100644 --- a/Moose Development/Moose/Dcs/DCSland.lua +++ b/Moose Development/Moose/Dcs/DCSland.lua @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- @module land +-- @module DCS.land --- @type land -- @field #land.SurfaceType SurfaceType diff --git a/Moose Development/Moose/Dcs/DCStimer.lua b/Moose Development/Moose/Dcs/DCStimer.lua index 2d0f4a16c..91fb3b93d 100644 --- a/Moose Development/Moose/Dcs/DCStimer.lua +++ b/Moose Development/Moose/Dcs/DCStimer.lua @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- @module DCStimer +-- @module DCS.DCStimer --- @type timer diff --git a/Moose Development/Moose/Dcs/DCStrigger.lua b/Moose Development/Moose/Dcs/DCStrigger.lua index 7bf5360be..c1638c5bb 100644 --- a/Moose Development/Moose/Dcs/DCStrigger.lua +++ b/Moose Development/Moose/Dcs/DCStrigger.lua @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- @module DCStrigger +-- @module DCS.DCStrigger trigger = {} --#timer diff --git a/Moose Development/Moose/Dcs/DCSworld.lua b/Moose Development/Moose/Dcs/DCSworld.lua index 5bcb203d2..80fed64a2 100644 --- a/Moose Development/Moose/Dcs/DCSworld.lua +++ b/Moose Development/Moose/Dcs/DCSworld.lua @@ -1,5 +1,5 @@ ------------------------------------------------------------------------------- --- @module DCSWorld +-- @module DCS.DCSWorld --- @type world -- @field #world.event event From 9dc329f151eb5f80db70ab8244ed0d09c21ee99f Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 21 May 2018 22:37:12 +0200 Subject: [PATCH 122/420] New version --- .appveyor/appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.appveyor/appveyor.yml b/.appveyor/appveyor.yml index e393ede16..17c1c054b 100644 --- a/.appveyor/appveyor.yml +++ b/.appveyor/appveyor.yml @@ -1,4 +1,4 @@ -version: 3.9.1.{build} +version: 2.4.a.{build} shallow_clone: true skip_branch_with_pr: false skip_commits: @@ -69,7 +69,7 @@ build_script: "Authorization" = "Bearer $token" "Content-type" = "application/json" } - $RequestBody = @{ accountName = 'FlightControl-Master'; projectSlug = 'moose-docs'; branch = branch = "$env:appveyor_repo_branch"; environmentVariables = @{} } | ConvertTo-Json + $RequestBody = @{ accountName = 'FlightControl-Master'; projectSlug = 'moose-docs'; branch = "$env:appveyor_repo_branch"; environmentVariables = @{} } | ConvertTo-Json # get project with last build details $project = Invoke-RestMethod -method Post -Uri "$apiUrl/builds" -Headers $headers -Body $RequestBody } From 51192f9a4c36c2370f1ca50a86738fec5ed896c8 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 21 May 2018 22:39:05 +0200 Subject: [PATCH 123/420] moose-docs --- .appveyor/appveyor.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.appveyor/appveyor.yml b/.appveyor/appveyor.yml index 17c1c054b..30b7b02bf 100644 --- a/.appveyor/appveyor.yml +++ b/.appveyor/appveyor.yml @@ -75,7 +75,6 @@ build_script: } - test: off # test_script: # - cmd: luacheck "Moose Development\Moose\moose.lua" "Moose Mission Setup\moose.lua" From 3d48dee23fee9b44f0f90d2739e96b6494daeaf1 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 21 May 2018 22:42:28 +0200 Subject: [PATCH 124/420] 2.4.a --- .appveyor/appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.appveyor/appveyor.yml b/.appveyor/appveyor.yml index 30b7b02bf..7fcd96a10 100644 --- a/.appveyor/appveyor.yml +++ b/.appveyor/appveyor.yml @@ -17,6 +17,7 @@ environment: platform: - x64 + init: - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod ` https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | ` From e78cd5675284b84fde04b006e2fd254f9c69612d Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 21 May 2018 22:51:43 +0200 Subject: [PATCH 125/420] Core modules --- Moose Development/Moose/Core/Base.lua | 2 +- Moose Development/Moose/Core/Database.lua | 2 +- Moose Development/Moose/Core/Event.lua | 2 +- Moose Development/Moose/Core/Fsm.lua | 2 +- Moose Development/Moose/Core/Goal.lua | 2 +- Moose Development/Moose/Core/Menu.lua | 2 +- Moose Development/Moose/Core/Message.lua | 2 +- Moose Development/Moose/Core/Point.lua | 2 +- Moose Development/Moose/Core/Radio.lua | 2 +- Moose Development/Moose/Core/ScheduleDispatcher.lua | 2 +- Moose Development/Moose/Core/Scheduler.lua | 2 +- Moose Development/Moose/Core/Set.lua | 2 +- Moose Development/Moose/Core/Settings.lua | 2 +- Moose Development/Moose/Core/Spawn.lua | 2 +- Moose Development/Moose/Core/SpawnStatic.lua | 2 +- Moose Development/Moose/Core/Spot.lua | 2 +- Moose Development/Moose/Core/UserFlag.lua | 2 +- Moose Development/Moose/Core/UserSound.lua | 2 +- Moose Development/Moose/Core/Velocity.lua | 2 +- Moose Development/Moose/Core/Zone.lua | 2 +- 20 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index 555786ab4..8dc4eb0e8 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -9,7 +9,7 @@ -- -- === -- --- @module Base +-- @module Core.Base diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 90197b34d..ecd6cddbd 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -7,7 +7,7 @@ -- -- === -- --- @module Database +-- @module Core.Database --- @type DATABASE diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 5b42a1d8a..01804da7a 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -164,7 +164,7 @@ -- -- === -- --- @module Event +-- @module Core.Event --- The EVENT structure diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index 62b80aee2..e3b2aacb1 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -64,7 +64,7 @@ -- -- === -- --- @module Fsm +-- @module Core.Fsm do -- FSM diff --git a/Moose Development/Moose/Core/Goal.lua b/Moose Development/Moose/Core/Goal.lua index ff6976877..e4373438e 100644 --- a/Moose Development/Moose/Core/Goal.lua +++ b/Moose Development/Moose/Core/Goal.lua @@ -10,7 +10,7 @@ -- -- === -- --- @module Goal +-- @module Core.Goal do -- Goal diff --git a/Moose Development/Moose/Core/Menu.lua b/Moose Development/Moose/Core/Menu.lua index 65c0025cd..03eb1c156 100644 --- a/Moose Development/Moose/Core/Menu.lua +++ b/Moose Development/Moose/Core/Menu.lua @@ -30,7 +30,7 @@ -- -- === -- --- @module Menu +-- @module Core.Menu MENU_INDEX = {} diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 11a1e19dc..904711e7b 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -4,7 +4,7 @@ -- -- === -- --- @module Message +-- @module Core.Message --- The MESSAGE class -- @type MESSAGE diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 5060600d6..5d914d338 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -26,7 +26,7 @@ -- -- ### Contributions: -- --- @module Point +-- @module Core.Point diff --git a/Moose Development/Moose/Core/Radio.lua b/Moose Development/Moose/Core/Radio.lua index 1d41d25b7..61eb02a76 100644 --- a/Moose Development/Moose/Core/Radio.lua +++ b/Moose Development/Moose/Core/Radio.lua @@ -32,7 +32,7 @@ -- -- ### Author: Hugues "Grey_Echo" Bousquet -- --- @module Radio +-- @module Core.Radio --- # RADIO class, extends @{Base#BASE} diff --git a/Moose Development/Moose/Core/ScheduleDispatcher.lua b/Moose Development/Moose/Core/ScheduleDispatcher.lua index 57073a516..20f3b5185 100644 --- a/Moose Development/Moose/Core/ScheduleDispatcher.lua +++ b/Moose Development/Moose/Core/ScheduleDispatcher.lua @@ -30,7 +30,7 @@ -- ### Contributions: - -- ### Authors: FlightControl : Design & Programming -- --- @module ScheduleDispatcher +-- @module Core.ScheduleDispatcher --- The SCHEDULEDISPATCHER structure -- @type SCHEDULEDISPATCHER diff --git a/Moose Development/Moose/Core/Scheduler.lua b/Moose Development/Moose/Core/Scheduler.lua index e576aa2aa..246244b29 100644 --- a/Moose Development/Moose/Core/Scheduler.lua +++ b/Moose Development/Moose/Core/Scheduler.lua @@ -39,7 +39,7 @@ -- -- === -- --- @module Scheduler +-- @module Core.Scheduler --- The SCHEDULER class diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 21fdde9dd..1c7e8df5e 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -30,7 +30,7 @@ -- -- === -- --- @module Set +-- @module Core.Set --- @type SET_BASE diff --git a/Moose Development/Moose/Core/Settings.lua b/Moose Development/Moose/Core/Settings.lua index 8b8a2c601..a8b9ba76d 100644 --- a/Moose Development/Moose/Core/Settings.lua +++ b/Moose Development/Moose/Core/Settings.lua @@ -16,7 +16,7 @@ -- -- * **FlightControl**: Design & Programming -- --- @module Settings +-- @module Core.Settings --- @type SETTINGS diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index fca5971f5..c260e6c9a 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -21,7 +21,7 @@ -- -- === -- --- @module Spawn +-- @module Core.Spawn --- SPAWN Class diff --git a/Moose Development/Moose/Core/SpawnStatic.lua b/Moose Development/Moose/Core/SpawnStatic.lua index 8019902af..22ae07cfc 100644 --- a/Moose Development/Moose/Core/SpawnStatic.lua +++ b/Moose Development/Moose/Core/SpawnStatic.lua @@ -29,7 +29,7 @@ -- -- === -- --- @module SpawnStatic +-- @module Core.SpawnStatic diff --git a/Moose Development/Moose/Core/Spot.lua b/Moose Development/Moose/Core/Spot.lua index 10edb6cee..19ca73263 100644 --- a/Moose Development/Moose/Core/Spot.lua +++ b/Moose Development/Moose/Core/Spot.lua @@ -38,7 +38,7 @@ -- -- === -- --- @module Spot +-- @module Core.Spot do diff --git a/Moose Development/Moose/Core/UserFlag.lua b/Moose Development/Moose/Core/UserFlag.lua index 05fc7b29e..f1b09589c 100644 --- a/Moose Development/Moose/Core/UserFlag.lua +++ b/Moose Development/Moose/Core/UserFlag.lua @@ -10,7 +10,7 @@ -- -- === -- --- @module UserFlag +-- @module Core.UserFlag do -- UserFlag diff --git a/Moose Development/Moose/Core/UserSound.lua b/Moose Development/Moose/Core/UserSound.lua index 808b78a5f..dcd6c58df 100644 --- a/Moose Development/Moose/Core/UserSound.lua +++ b/Moose Development/Moose/Core/UserSound.lua @@ -10,7 +10,7 @@ -- -- === -- --- @module UserSound +-- @module Core.UserSound do -- UserSound diff --git a/Moose Development/Moose/Core/Velocity.lua b/Moose Development/Moose/Core/Velocity.lua index 88d047126..ecc4b5ea6 100644 --- a/Moose Development/Moose/Core/Velocity.lua +++ b/Moose Development/Moose/Core/Velocity.lua @@ -7,7 +7,7 @@ -- -- === -- --- @module Velocity +-- @module Core.Velocity do -- Velocity diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 938fde87a..a8dc2f641 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -34,7 +34,7 @@ -- -- === -- --- @module Zone +-- @module Core.Zone --- @type ZONE_BASE From fe2f59660d2ae623ec7fbba6034b647ef22663da Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Tue, 22 May 2018 11:15:51 +0200 Subject: [PATCH 126/420] AI test --- Moose Development/Moose/AI/AI_A2A_Cap.lua | 2 +- Moose Development/Moose/AI/AI_A2A_Dispatcher.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A_Cap.lua b/Moose Development/Moose/AI/AI_A2A_Cap.lua index 9b90b489e..6375f5ee8 100644 --- a/Moose Development/Moose/AI/AI_A2A_Cap.lua +++ b/Moose Development/Moose/AI/AI_A2A_Cap.lua @@ -1,6 +1,6 @@ --- **AI** -- (R2.2) - Models the process of Combat Air Patrol (CAP) for airplanes. -- --- This is a class used in the @{AI_A2A_Dispatcher}. +-- This is a class used in the @{AI.AI_A2A_Dispatcher}. -- -- === -- diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index 96fc731d1..6594ea9bb 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -155,7 +155,7 @@ -- ### Authors: **FlightControl** rework of GCICAP + introduction of new concepts (squadrons). -- ### Authors: **Stonehouse**, **SNAFU** in terms of the advice, documentation, and the original GCICAP script. -- --- @module AI_A2A_Dispatcher +-- @module AI.AI_A2A_Dispatcher From 1b39f5d6e6ab8abbcc9a33313b6f9053dc5328c4 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Wed, 23 May 2018 10:42:27 +0200 Subject: [PATCH 127/420] Optimized comments and documentation set --- Moose Development/Moose/AI/AI_A2A.lua | 8 +- Moose Development/Moose/AI/AI_A2A_Cap.lua | 20 +-- .../Moose/AI/AI_A2A_Dispatcher.lua | 28 ++--- Moose Development/Moose/AI/AI_A2A_Gci.lua | 10 +- Moose Development/Moose/AI/AI_A2A_Patrol.lua | 20 +-- Moose Development/Moose/AI/AI_BAI.lua | 20 +-- Moose Development/Moose/AI/AI_Balancer.lua | 18 +-- Moose Development/Moose/AI/AI_CAP.lua | 18 +-- Moose Development/Moose/AI/AI_CAS.lua | 20 +-- Moose Development/Moose/AI/AI_Cargo_APC.lua | 6 +- .../Moose/AI/AI_Cargo_Airplane.lua | 4 +- .../Moose/AI/AI_Cargo_Dispatcher.lua | 2 +- .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 6 +- .../Moose/AI/AI_Cargo_Dispatcher_Airplane.lua | 4 +- .../AI/AI_Cargo_Dispatcher_Helicopter.lua | 4 +- .../Moose/AI/AI_Cargo_Helicopter.lua | 4 +- Moose Development/Moose/AI/AI_Formation.lua | 22 ++-- Moose Development/Moose/AI/AI_Patrol.lua | 26 ++-- .../Moose/Actions/Act_Account.lua | 12 +- .../Moose/Actions/Act_Assist.lua | 6 +- Moose Development/Moose/Actions/Act_Route.lua | 2 +- Moose Development/Moose/Cargo/Cargo.lua | 16 +-- Moose Development/Moose/Cargo/CargoCrate.lua | 8 +- Moose Development/Moose/Cargo/CargoGroup.lua | 22 ++-- .../Moose/Cargo/CargoSlingload.lua | 8 +- Moose Development/Moose/Cargo/CargoUnit.lua | 6 +- Moose Development/Moose/Core/Base.lua | 4 +- Moose Development/Moose/Core/Database.lua | 2 +- Moose Development/Moose/Core/Event.lua | 14 +-- Moose Development/Moose/Core/Fsm.lua | 10 +- Moose Development/Moose/Core/Goal.lua | 2 +- Moose Development/Moose/Core/Menu.lua | 8 +- Moose Development/Moose/Core/Message.lua | 4 +- Moose Development/Moose/Core/Point.lua | 6 +- Moose Development/Moose/Core/Radio.lua | 16 +-- Moose Development/Moose/Core/Report.lua | 13 ++ Moose Development/Moose/Core/Scheduler.lua | 2 +- Moose Development/Moose/Core/Set.lua | 96 +++++++------- Moose Development/Moose/Core/Settings.lua | 2 +- Moose Development/Moose/Core/Spawn.lua | 118 +++++++++--------- Moose Development/Moose/Core/SpawnStatic.lua | 2 +- Moose Development/Moose/Core/Spot.lua | 4 +- Moose Development/Moose/Core/UserFlag.lua | 2 +- Moose Development/Moose/Core/UserSound.lua | 6 +- Moose Development/Moose/Core/Velocity.lua | 4 +- Moose Development/Moose/Core/Zone.lua | 62 ++++----- .../Moose/Functional/ATC_Ground.lua | 6 +- .../Moose/Functional/CleanUp.lua | 4 +- .../Moose/Functional/Detection.lua | 20 +-- Moose Development/Moose/Functional/Escort.lua | 2 +- .../Moose/Functional/MissileTrainer.lua | 2 +- .../Moose/Functional/Protect.lua | 2 +- .../Moose/Functional/PseudoATC.lua | 2 +- Moose Development/Moose/Functional/RAT.lua | 4 +- Moose Development/Moose/Functional/Range.lua | 12 +- .../Moose/Functional/Scoring.lua | 28 ++--- .../Moose/Functional/ZoneGoal.lua | 2 +- .../Moose/Tasking/CommandCenter.lua | 2 +- .../Moose/Tasking/DetectionManager.lua | 16 +-- Moose Development/Moose/Tasking/Mission.lua | 4 +- Moose Development/Moose/Tasking/Task.lua | 50 ++++---- Moose Development/Moose/Tasking/TaskInfo.lua | 2 +- .../Moose/Tasking/TaskZoneCapture.lua | 4 +- Moose Development/Moose/Tasking/Task_A2A.lua | 20 +-- .../Moose/Tasking/Task_A2A_Dispatcher.lua | 10 +- Moose Development/Moose/Tasking/Task_A2G.lua | 14 +-- .../Moose/Tasking/Task_A2G_Dispatcher.lua | 16 +-- .../Moose/Tasking/Task_CARGO.lua | 4 +- .../Moose/Tasking/Task_Cargo_CSAR.lua | 2 +- .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 6 +- .../Moose/Tasking/Task_Cargo_Transport.lua | 2 +- .../Moose/Tasking/Task_Manager.lua | 6 +- .../Moose/Tasking/Task_Pickup.lua | 10 +- Moose Development/Moose/Wrapper/Airbase.lua | 4 +- Moose Development/Moose/Wrapper/Client.lua | 4 +- .../Moose/Wrapper/Controllable.lua | 12 +- Moose Development/Moose/Wrapper/Group.lua | 44 +++---- .../Moose/Wrapper/Identifiable.lua | 4 +- Moose Development/Moose/Wrapper/Object.lua | 4 +- .../Moose/Wrapper/Positionable.lua | 12 +- Moose Development/Moose/Wrapper/Scenery.lua | 4 +- Moose Development/Moose/Wrapper/Static.lua | 10 +- Moose Development/Moose/Wrapper/Unit.lua | 16 +-- 83 files changed, 538 insertions(+), 525 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A.lua b/Moose Development/Moose/AI/AI_A2A.lua index 1309b52bc..22507ffb3 100644 --- a/Moose Development/Moose/AI/AI_A2A.lua +++ b/Moose Development/Moose/AI/AI_A2A.lua @@ -8,7 +8,7 @@ -- -- === -- --- @module AI_A2A +-- @module AI.AI_A2A --BASE:TraceClass("AI_A2A") @@ -18,7 +18,7 @@ --- # AI_A2A class, extends @{Fsm#FSM_CONTROLLABLE} -- --- The AI_A2A class implements the core functions to operate an AI @{Group} A2A tasking. +-- The AI_A2A class implements the core functions to operate an AI @{Wrapper.Group} A2A tasking. -- -- -- ## AI_A2A constructor @@ -295,8 +295,8 @@ end --- Sets (modifies) the minimum and maximum speed of the patrol. -- @param #AI_A2A self --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h. -- @return #AI_A2A self function AI_A2A:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed ) self:F2( { PatrolMinSpeed, PatrolMaxSpeed } ) diff --git a/Moose Development/Moose/AI/AI_A2A_Cap.lua b/Moose Development/Moose/AI/AI_A2A_Cap.lua index 6375f5ee8..0451051a2 100644 --- a/Moose Development/Moose/AI/AI_A2A_Cap.lua +++ b/Moose Development/Moose/AI/AI_A2A_Cap.lua @@ -8,7 +8,7 @@ -- -- === -- --- @module AI_A2A_Cap +-- @module AI.AI_A2A_Cap --BASE:TraceClass("AI_A2A_CAP") @@ -16,14 +16,14 @@ -- @extends AI.AI_A2A_Patrol#AI_A2A_PATROL ---- # AI_A2A_CAP class, extends @{AI_CAP#AI_PATROL_ZONE} +--- # AI_A2A_CAP class, extends @{AI.AI_A2A_Patrol#AI_A2A_PATROL} -- --- The AI_A2A_CAP class implements the core functions to patrol a @{Zone} by an AI @{Group} or @{Group} +-- The AI_A2A_CAP class implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group} -- and automatically engage any airborne enemies that are within a certain range or within a certain zone. -- -- ![Process](..\Presentations\AI_CAP\Dia3.JPG) -- --- The AI_A2A_CAP is assigned a @{Group} and this must be done before the AI_A2A_CAP process can be started using the **Start** event. +-- The AI_A2A_CAP is assigned a @{Wrapper.Group} and this must be done before the AI_A2A_CAP process can be started using the **Start** event. -- -- ![Process](..\Presentations\AI_CAP\Dia4.JPG) -- @@ -73,8 +73,8 @@ -- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. -- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. -- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. --- * **@{#AI_A2A_CAP.Destroy}**: The AI has destroyed a bogey @{Unit}. --- * **@{#AI_A2A_CAP.Destroyed}**: The AI has destroyed all bogeys @{Unit}s assigned in the CAS task. +-- * **@{#AI_A2A_CAP.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}. +-- * **@{#AI_A2A_CAP.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task. -- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- -- ## 3. Set the Range of Engagement @@ -108,10 +108,10 @@ AI_A2A_CAP = { -- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. -- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Group} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Group} in km/h. --- @param Dcs.DCSTypes#Speed EngageMinSpeed The minimum speed of the @{Group} in km/h when engaging a target. --- @param Dcs.DCSTypes#Speed EngageMaxSpeed The maximum speed of the @{Group} in km/h when engaging a target. +-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h. +-- @param Dcs.DCSTypes#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target. +-- @param Dcs.DCSTypes#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target. -- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO -- @return #AI_A2A_CAP function AI_A2A_CAP:New( AICap, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, PatrolAltType ) diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index 6594ea9bb..047922014 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -165,7 +165,7 @@ do -- AI_A2A_DISPATCHER -- @type AI_A2A_DISPATCHER -- @extends Tasking.DetectionManager#DETECTION_MANAGER - --- # AI\_A2A\_DISPATCHER class, extends @{Tasking#DETECTION_MANAGER} + --- # AI\_A2A\_DISPATCHER class, extends @{Tasking.DetectionManage#DETECTION_MANAGER} -- -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia1.JPG) -- @@ -346,7 +346,7 @@ do -- AI_A2A_DISPATCHER -- -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia9.JPG) -- - -- If it’s a cold war then the **borders of red and blue territory** need to be defined using a @{zone} object derived from @{Zone#ZONE_BASE}. + -- If it’s a cold war then the **borders of red and blue territory** need to be defined using a @{zone} object derived from @{Core.Zone#ZONE_BASE}. -- If a hot war is chosen then **no borders** actually need to be defined using the helicopter units other than -- it makes it easier sometimes for the mission maker to envisage where the red and blue territories roughly are. -- In a hot war the borders are effectively defined by the ground based radar coverage of a coalition. @@ -544,7 +544,7 @@ do -- AI_A2A_DISPATCHER -- * As the CAP flights wander around within the zone waiting to be tasked, these zones need to be large enough that the aircraft are not constantly turning -- but do not have to be big and numerous enough to completely cover a border. -- - -- * CAP zones can be of any type, and are derived from the @{Zone#ZONE_BASE} class. Zones can be @{Zone#ZONE}, @{Zone#ZONE_POLYGON}, @{Zone#ZONE_UNIT}, @{Zone#ZONE_GROUP}, etc. + -- * CAP zones can be of any type, and are derived from the @{Core.Zone#ZONE_BASE} class. Zones can be @{Core.Zone#ZONE}, @{Core.Zone#ZONE_POLYGON}, @{Core.Zone#ZONE_UNIT}, @{Core.Zone#ZONE_GROUP}, etc. -- This allows to setup **static, moving and/or complex zones** wherein aircraft will perform the CAP. -- -- * Typically 20000-50000 metres width is used and they are spaced so that aircraft in the zone waiting for tasks don’t have to far to travel to protect their coalitions important targets. @@ -1142,7 +1142,7 @@ do -- AI_A2A_DISPATCHER --- Define a border area to simulate a **cold war** scenario. -- A **cold war** is one where CAP aircraft patrol their territory but will not attack enemy aircraft or launch GCI aircraft unless enemy aircraft enter their territory. In other words the EWR may detect an enemy aircraft but will only send aircraft to attack it if it crosses the border. -- A **hot war** is one where CAP aircraft will intercept any detected enemy aircraft and GCI aircraft will launch against detected enemy aircraft without regard for territory. In other words if the ground radar can detect the enemy aircraft then it will send CAP and GCI aircraft to attack it. - -- If it’s a cold war then the **borders of red and blue territory** need to be defined using a @{zone} object derived from @{Zone#ZONE_BASE}. This method needs to be used for this. + -- If it’s a cold war then the **borders of red and blue territory** need to be defined using a @{zone} object derived from @{Core.Zone#ZONE_BASE}. This method needs to be used for this. -- If a hot war is chosen then **no borders** actually need to be defined using the helicopter units other than it makes it easier sometimes for the mission maker to envisage where the red and blue territories roughly are. In a hot war the borders are effectively defined by the ground based radar coverage of a coalition. Set the noborders parameter to 1 -- @param #AI_A2A_DISPATCHER self -- @param Core.Zone#ZONE_BASE BorderZone An object derived from ZONE_BASE, or a list of objects derived from ZONE_BASE. @@ -1423,11 +1423,11 @@ do -- AI_A2A_DISPATCHER -- You need to specify here EXACTLY the name of the airbase as you see it in the mission editor. -- Examples are `"Batumi"` or `"Tbilisi-Lochini"`. -- EXACTLY the airbase name, between quotes `""`. - -- To ease the airbase naming when using the LDT editor and IntelliSense, the @{Airbase#AIRBASE} class contains enumerations of the airbases of each map. + -- To ease the airbase naming when using the LDT editor and IntelliSense, the @{Wrapper.Airbase#AIRBASE} class contains enumerations of the airbases of each map. -- - -- * Caucasus: @{Airbase#AIRBASE.Caucaus} - -- * Nevada or NTTR: @{Airbase#AIRBASE.Nevada} - -- * Normandy: @{Airbase#AIRBASE.Normandy} + -- * Caucasus: @{Wrapper.Airbase#AIRBASE.Caucaus} + -- * Nevada or NTTR: @{Wrapper.Airbase#AIRBASE.Nevada} + -- * Normandy: @{Wrapper.Airbase#AIRBASE.Normandy} -- -- @param #string TemplatePrefixes A string or an array of strings specifying the **prefix names of the templates** (not going to explain what is templates here again). -- Examples are `{ "104th", "105th" }` or `"104th"` or `"Template 1"` or `"BLUE PLANES"`. @@ -1512,7 +1512,7 @@ do -- AI_A2A_DISPATCHER --- Set a CAP for a Squadron. -- @param #AI_A2A_DISPATCHER self -- @param #string SquadronName The squadron name. - -- @param Core.Zone#ZONE_BASE Zone The @{Zone} object derived from @{Zone#ZONE_BASE} that defines the zone wherein the CAP will be executed. + -- @param Core.Zone#ZONE_BASE Zone The @{Zone} object derived from @{Core.Zone#ZONE_BASE} that defines the zone wherein the CAP will be executed. -- @param #number FloorAltitude The minimum altitude at which the cap can be executed. -- @param #number CeilingAltitude the maximum altitude at which the cap can be executed. -- @param #number PatrolMinSpeed The minimum speed at which the cap can be executed. @@ -2391,7 +2391,7 @@ do -- AI_A2A_DISPATCHER --- Set the default tanker where defenders will Refuel in the air. -- @param #AI_A2A_DISPATCHER self - -- @param #strig TankerName A string defining the group name of the Tanker as defined within the Mission Editor. + -- @param #string TankerName A string defining the group name of the Tanker as defined within the Mission Editor. -- @return #AI_A2A_DISPATCHER -- @usage -- @@ -2414,7 +2414,7 @@ do -- AI_A2A_DISPATCHER --- Set the squadron tanker where defenders will Refuel in the air. -- @param #AI_A2A_DISPATCHER self -- @param #string SquadronName The name of the squadron. - -- @param #strig TankerName A string defining the group name of the Tanker as defined within the Mission Editor. + -- @param #string TankerName A string defining the group name of the Tanker as defined within the Mission Editor. -- @return #AI_A2A_DISPATCHER -- @usage -- @@ -2470,7 +2470,7 @@ do -- AI_A2A_DISPATCHER --- Creates an SWEEP task when there are targets for it. -- @param #AI_A2A_DISPATCHER self -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem - -- @return Set#SET_UNIT TargetSetUnit: The target set of units. + -- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units. -- @return #nil If there are no targets to be set. function AI_A2A_DISPATCHER:EvaluateSWEEP( DetectedItem ) self:F( { DetectedItem.ItemID } ) @@ -2891,7 +2891,7 @@ do -- AI_A2A_DISPATCHER --- Creates an ENGAGE task when there are human friendlies airborne near the targets. -- @param #AI_A2A_DISPATCHER self -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem - -- @return Set#SET_UNIT TargetSetUnit: The target set of units. + -- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units. -- @return #nil If there are no targets to be set. function AI_A2A_DISPATCHER:EvaluateENGAGE( DetectedItem ) self:F( { DetectedItem.ItemID } ) @@ -2918,7 +2918,7 @@ do -- AI_A2A_DISPATCHER --- Creates an GCI task when there are targets for it. -- @param #AI_A2A_DISPATCHER self -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem - -- @return Set#SET_UNIT TargetSetUnit: The target set of units. + -- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units. -- @return #nil If there are no targets to be set. function AI_A2A_DISPATCHER:EvaluateGCI( DetectedItem ) self:F( { DetectedItem.ItemID } ) diff --git a/Moose Development/Moose/AI/AI_A2A_Gci.lua b/Moose Development/Moose/AI/AI_A2A_Gci.lua index e714fcfe6..98670a381 100644 --- a/Moose Development/Moose/AI/AI_A2A_Gci.lua +++ b/Moose Development/Moose/AI/AI_A2A_Gci.lua @@ -8,7 +8,7 @@ -- -- === -- --- @module AI_A2A_GCI +-- @module AI.AI_A2A_GCI @@ -16,13 +16,13 @@ -- @extends AI.AI_A2A#AI_A2A ---- # AI_A2A_GCI class, extends @{AI_A2A#AI_A2A} +--- # AI_A2A_GCI class, extends @{AI.AI_A2A#AI_A2A} -- -- The AI_A2A_GCI class implements the core functions to intercept intruders. The Engage function will intercept intruders. -- -- ![Process](..\Presentations\AI_GCI\Dia3.JPG) -- --- The AI_A2A_GCI is assigned a @{Group} and this must be done before the AI_A2A_GCI process can be started using the **Start** event. +-- The AI_A2A_GCI is assigned a @{Wrapper.Group} and this must be done before the AI_A2A_GCI process can be started using the **Start** event. -- -- ![Process](..\Presentations\AI_GCI\Dia4.JPG) -- @@ -72,8 +72,8 @@ -- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. -- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. -- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. --- * **@{#AI_A2A_GCI.Destroy}**: The AI has destroyed a bogey @{Unit}. --- * **@{#AI_A2A_GCI.Destroyed}**: The AI has destroyed all bogeys @{Unit}s assigned in the CAS task. +-- * **@{#AI_A2A_GCI.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}. +-- * **@{#AI_A2A_GCI.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task. -- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- -- ## 3. Set the Range of Engagement diff --git a/Moose Development/Moose/AI/AI_A2A_Patrol.lua b/Moose Development/Moose/AI/AI_A2A_Patrol.lua index 927c5fd32..e741e9fba 100644 --- a/Moose Development/Moose/AI/AI_A2A_Patrol.lua +++ b/Moose Development/Moose/AI/AI_A2A_Patrol.lua @@ -8,19 +8,19 @@ -- -- === -- --- @module AI_A2A_Patrol +-- @module AI.AI_A2A_Patrol --- @type AI_A2A_PATROL -- @extends AI.AI_A2A#AI_A2A ---- # AI_A2A_PATROL class, extends @{Fsm#FSM_CONTROLLABLE} +--- # AI_A2A_PATROL class, extends @{AI.AI_A2A#AI_A2A} -- --- The AI_A2A_PATROL class implements the core functions to patrol a @{Zone} by an AI @{Group} or @{Group}. +-- The AI_A2A_PATROL class implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group}. -- -- ![Process](..\Presentations\AI_PATROL\Dia3.JPG) -- --- The AI_A2A_PATROL is assigned a @{Group} and this must be done before the AI_A2A_PATROL process can be started using the **Start** event. +-- The AI_A2A_PATROL is assigned a @{Wrapper.Group} and this must be done before the AI_A2A_PATROL process can be started using the **Start** event. -- -- ![Process](..\Presentations\AI_PATROL\Dia4.JPG) -- @@ -93,7 +93,7 @@ -- * @{#AI_A2A_PATROL.SetDetectionOff}(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased. -- -- The detection frequency can be set with @{#AI_A2A_PATROL.SetRefreshTimeInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection. --- Use the method @{#AI_A2A_PATROL.GetDetectedUnits}() to obtain a list of the @{Unit}s detected by the AI. +-- Use the method @{#AI_A2A_PATROL.GetDetectedUnits}() to obtain a list of the @{Wrapper.Unit}s detected by the AI. -- -- The detection can be filtered to potential targets in a specific zone. -- Use the method @{#AI_A2A_PATROL.SetDetectionZone}() to set the zone where targets need to be detected. @@ -128,8 +128,8 @@ AI_A2A_PATROL = { -- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. -- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Group} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Group} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h. -- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO -- @return #AI_A2A_PATROL self -- @usage @@ -236,8 +236,8 @@ end --- Sets (modifies) the minimum and maximum speed of the patrol. -- @param #AI_A2A_PATROL self --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Group} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Group} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h. -- @return #AI_A2A_PATROL self function AI_A2A_PATROL:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed ) self:F2( { PatrolMinSpeed, PatrolMaxSpeed } ) @@ -358,7 +358,7 @@ function AI_A2A_PATROL.Resume( AIPatrol ) AIPatrol:F( { "AI_A2A_PATROL.Resume:", AIPatrol:GetName() } ) if AIPatrol:IsAlive() then - local _AI_A2A = AIPatrol:GetState( AIPatrol, "AI_A2A" ) -- #AI_A2A + local _AI_A2A = AIPatrol:GetState( AIPatrol, "AI_A2A" ) -- AI.AI_A2A#AI_A2A _AI_A2A:__Reset( 1 ) _AI_A2A:__Route( 5 ) end diff --git a/Moose Development/Moose/AI/AI_BAI.lua b/Moose Development/Moose/AI/AI_BAI.lua index ccb0603bc..5d8ed3df6 100644 --- a/Moose Development/Moose/AI/AI_BAI.lua +++ b/Moose Development/Moose/AI/AI_BAI.lua @@ -21,25 +21,25 @@ -- -- === -- --- @module AI_Bai +-- @module AI.AI_Bai --- AI_BAI_ZONE class -- @type AI_BAI_ZONE --- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. +-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Wrapper.Controllable} patrolling. -- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. -- @extends AI.AI_Patrol#AI_PATROL_ZONE ---- # AI_BAI_ZONE class, extends @{AI_Patrol#AI_PATROL_ZONE} +--- # AI_BAI_ZONE class, extends @{AI.AI_Patrol#AI_PATROL_ZONE} -- --- AI_BAI_ZONE derives from the @{AI_Patrol#AI_PATROL_ZONE}, inheriting its methods and behaviour. +-- AI_BAI_ZONE derives from the @{AI.AI_Patrol#AI_PATROL_ZONE}, inheriting its methods and behaviour. -- --- The AI_BAI_ZONE class implements the core functions to provide BattleGround Air Interdiction in an Engage @{Zone} by an AIR @{Controllable} or @{Group}. +-- The AI_BAI_ZONE class implements the core functions to provide BattleGround Air Interdiction in an Engage @{Zone} by an AIR @{Wrapper.Controllable} or @{Wrapper.Group}. -- The AI_BAI_ZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone. -- -- ![HoldAndEngage](..\Presentations\AI_BAI\Dia3.JPG) -- --- The AI_BAI_ZONE is assigned a @{Group} and this must be done before the AI_BAI_ZONE process can be started through the **Start** event. +-- The AI_BAI_ZONE is assigned a @{Wrapper.Group} and this must be done before the AI_BAI_ZONE process can be started through the **Start** event. -- -- ![Start Event](..\Presentations\AI_BAI\Dia4.JPG) -- @@ -112,8 +112,8 @@ -- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. -- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. -- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. --- * **@{#AI_BAI_ZONE.Destroy}**: The AI has destroyed a target @{Unit}. --- * **@{#AI_BAI_ZONE.Destroyed}**: The AI has destroyed all target @{Unit}s assigned in the BOMB task. +-- * **@{#AI_BAI_ZONE.Destroy}**: The AI has destroyed a target @{Wrapper.Unit}. +-- * **@{#AI_BAI_ZONE.Destroyed}**: The AI has destroyed all target @{Wrapper.Unit}s assigned in the BOMB task. -- * **Status**: The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- -- ## 3. Modify the Engage Zone behaviour to pinpoint a **map object** or **scenery object** @@ -142,8 +142,8 @@ AI_BAI_ZONE = { -- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. -- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h. -- @param Core.Zone#ZONE_BASE EngageZone The zone where the engage will happen. -- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO -- @return #AI_BAI_ZONE self diff --git a/Moose Development/Moose/AI/AI_Balancer.lua b/Moose Development/Moose/AI/AI_Balancer.lua index 18bb5fa65..e14ec568f 100644 --- a/Moose Development/Moose/AI/AI_Balancer.lua +++ b/Moose Development/Moose/AI/AI_Balancer.lua @@ -21,7 +21,7 @@ -- -- === -- --- @module AI_Balancer +-- @module AI.AI_Balancer --- @type AI_BALANCER -- @field Core.Set#SET_CLIENT SetClient @@ -30,7 +30,7 @@ -- @extends Core.Fsm#FSM_SET ---- # AI_BALANCER class, extends @{Fsm#FSM_SET} +--- # AI_BALANCER class, extends @{Core.Fsm#FSM_SET} -- -- The AI_BALANCER class monitors and manages as many replacement AI groups as there are -- CLIENTS in a SET_CLIENT collection, which are not occupied by human players. @@ -78,8 +78,8 @@ -- However, there are 2 additional options that you can use to customize the destroy behaviour. -- When a human player joins a slot, you can configure to let the AI return to: -- --- * @{#AI_BALANCER.ReturnToHomeAirbase}: Returns the AI to the **home** @{Airbase#AIRBASE}. --- * @{#AI_BALANCER.ReturnToNearestAirbases}: Returns the AI to the **nearest friendly** @{Airbase#AIRBASE}. +-- * @{#AI_BALANCER.ReturnToHomeAirbase}: Returns the AI to the **home** @{Wrapper.Airbase#AIRBASE}. +-- * @{#AI_BALANCER.ReturnToNearestAirbases}: Returns the AI to the **nearest friendly** @{Wrapper.Airbase#AIRBASE}. -- -- Note that when AI returns to an airbase, the AI_BALANCER will trigger the **Return** event and the AI will return, -- otherwise the AI_BALANCER will trigger a **Destroy** event, and the AI will be destroyed. @@ -141,10 +141,10 @@ function AI_BALANCER:InitSpawnInterval( Earliest, Latest ) return self end ---- Returns the AI to the nearest friendly @{Airbase#AIRBASE}. +--- Returns the AI to the nearest friendly @{Wrapper.Airbase#AIRBASE}. -- @param #AI_BALANCER self --- @param Dcs.DCSTypes#Distance ReturnThresholdRange If there is an enemy @{Client#CLIENT} within the ReturnThresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}. --- @param Core.Set#SET_AIRBASE ReturnAirbaseSet The SET of @{Set#SET_AIRBASE}s to evaluate where to return to. +-- @param Dcs.DCSTypes#Distance ReturnThresholdRange If there is an enemy @{Wrapper.Client#CLIENT} within the ReturnThresholdRange given in meters, the AI will not return to the nearest @{Wrapper.Airbase#AIRBASE}. +-- @param Core.Set#SET_AIRBASE ReturnAirbaseSet The SET of @{Core.Set#SET_AIRBASE}s to evaluate where to return to. function AI_BALANCER:ReturnToNearestAirbases( ReturnThresholdRange, ReturnAirbaseSet ) self.ToNearestAirbase = true @@ -152,9 +152,9 @@ function AI_BALANCER:ReturnToNearestAirbases( ReturnThresholdRange, ReturnAirbas self.ReturnAirbaseSet = ReturnAirbaseSet end ---- Returns the AI to the home @{Airbase#AIRBASE}. +--- Returns the AI to the home @{Wrapper.Airbase#AIRBASE}. -- @param #AI_BALANCER self --- @param Dcs.DCSTypes#Distance ReturnThresholdRange If there is an enemy @{Client#CLIENT} within the ReturnThresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}. +-- @param Dcs.DCSTypes#Distance ReturnThresholdRange If there is an enemy @{Wrapper.Client#CLIENT} within the ReturnThresholdRange given in meters, the AI will not return to the nearest @{Wrapper.Airbase#AIRBASE}. function AI_BALANCER:ReturnToHomeAirbase( ReturnThresholdRange ) self.ToHomeAirbase = true diff --git a/Moose Development/Moose/AI/AI_CAP.lua b/Moose Development/Moose/AI/AI_CAP.lua index 8dc70fd40..9df9ddf8e 100644 --- a/Moose Development/Moose/AI/AI_CAP.lua +++ b/Moose Development/Moose/AI/AI_CAP.lua @@ -25,23 +25,23 @@ -- -- === -- --- @module AI_Cap +-- @module AI.AI_Cap --- @type AI_CAP_ZONE --- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. +-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Wrapper.Controllable} patrolling. -- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. -- @extends AI.AI_Patrol#AI_PATROL_ZONE ---- # AI_CAP_ZONE class, extends @{AI_CAP#AI_PATROL_ZONE} +--- # AI_CAP_ZONE class, extends @{AI.AI_Patrol#AI_PATROL_ZONE} -- --- The AI_CAP_ZONE class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group} +-- The AI_CAP_ZONE class implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Controllable} or @{Wrapper.Group} -- and automatically engage any airborne enemies that are within a certain range or within a certain zone. -- -- ![Process](..\Presentations\AI_CAP\Dia3.JPG) -- --- The AI_CAP_ZONE is assigned a @{Group} and this must be done before the AI_CAP_ZONE process can be started using the **Start** event. +-- The AI_CAP_ZONE is assigned a @{Wrapper.Group} and this must be done before the AI_CAP_ZONE process can be started using the **Start** event. -- -- ![Process](..\Presentations\AI_CAP\Dia4.JPG) -- @@ -91,8 +91,8 @@ -- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. -- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. -- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. --- * **@{#AI_CAP_ZONE.Destroy}**: The AI has destroyed a bogey @{Unit}. --- * **@{#AI_CAP_ZONE.Destroyed}**: The AI has destroyed all bogeys @{Unit}s assigned in the CAS task. +-- * **@{#AI_CAP_ZONE.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}. +-- * **@{#AI_CAP_ZONE.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task. -- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- -- ## 3. Set the Range of Engagement @@ -127,8 +127,8 @@ AI_CAP_ZONE = { -- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. -- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h. -- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO -- @return #AI_CAP_ZONE self function AI_CAP_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) diff --git a/Moose Development/Moose/AI/AI_CAS.lua b/Moose Development/Moose/AI/AI_CAS.lua index 2450d02ff..74e18c3b5 100644 --- a/Moose Development/Moose/AI/AI_CAS.lua +++ b/Moose Development/Moose/AI/AI_CAS.lua @@ -23,25 +23,25 @@ -- -- === -- --- @module AI_Cas +-- @module AI.AI_Cas --- AI_CAS_ZONE class -- @type AI_CAS_ZONE --- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. +-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Wrapper.Controllable} patrolling. -- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. -- @extends AI.AI_Patrol#AI_PATROL_ZONE ---- # AI_CAS_ZONE class, extends @{AI_Patrol#AI_PATROL_ZONE} +--- # AI_CAS_ZONE class, extends @{AI.AI_Patrol#AI_PATROL_ZONE} -- --- AI_CAS_ZONE derives from the @{AI_Patrol#AI_PATROL_ZONE}, inheriting its methods and behaviour. +-- AI_CAS_ZONE derives from the @{AI.AI_Patrol#AI_PATROL_ZONE}, inheriting its methods and behaviour. -- --- The AI_CAS_ZONE class implements the core functions to provide Close Air Support in an Engage @{Zone} by an AIR @{Controllable} or @{Group}. +-- The AI_CAS_ZONE class implements the core functions to provide Close Air Support in an Engage @{Zone} by an AIR @{Wrapper.Controllable} or @{Wrapper.Group}. -- The AI_CAS_ZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone. -- -- ![HoldAndEngage](..\Presentations\AI_CAS\Dia3.JPG) -- --- The AI_CAS_ZONE is assigned a @{Group} and this must be done before the AI_CAS_ZONE process can be started through the **Start** event. +-- The AI_CAS_ZONE is assigned a @{Wrapper.Group} and this must be done before the AI_CAS_ZONE process can be started through the **Start** event. -- -- ![Start Event](..\Presentations\AI_CAS\Dia4.JPG) -- @@ -114,8 +114,8 @@ -- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. -- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. -- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. --- * **@{#AI_CAS_ZONE.Destroy}**: The AI has destroyed a target @{Unit}. --- * **@{#AI_CAS_ZONE.Destroyed}**: The AI has destroyed all target @{Unit}s assigned in the CAS task. +-- * **@{#AI_CAS_ZONE.Destroy}**: The AI has destroyed a target @{Wrapper.Unit}. +-- * **@{#AI_CAS_ZONE.Destroyed}**: The AI has destroyed all target @{Wrapper.Unit}s assigned in the CAS task. -- * **Status**: The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. -- -- === @@ -132,8 +132,8 @@ AI_CAS_ZONE = { -- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. -- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h. -- @param Core.Zone#ZONE_BASE EngageZone The zone where the engage will happen. -- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO -- @return #AI_CAS_ZONE self diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 0cb8aed9b..db1a05e55 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -6,13 +6,13 @@ -- -- === -- --- @module AI_Cargo_APC +-- @module AI.AI_Cargo_APC --- @type AI_CARGO_APC -- @extends Core.Fsm#FSM_CONTROLLABLE ---- # AI\_CARGO\_APC class, extends @{Core.Base#BASE} +--- # AI\_CARGO\_APC class, extends @{Core.Fsm#FSM_CONTROLLABLE} -- -- === -- @@ -656,7 +656,7 @@ function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate ) end ---- @param #AI_CARGO_HELICOPTER self +--- @param #AI_CARGO_APC self -- @param Wrapper.Group#GROUP APC -- @param From -- @param Event diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index a55bf0d10..e216d0aff 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -6,13 +6,13 @@ -- -- === -- --- @module AI_Cargo_Airplane +-- @module AI.AI_Cargo_Airplane --- @type AI_CARGO_AIRPLANE -- @extends Core.Fsm#FSM_CONTROLLABLE ---- # AI\_CARGO\_AIRPLANE class, extends @{Core.Base@BASE} +--- # AI\_CARGO\_AIRPLANE class, extends @{Core.Fsm#FSM_CONTROLLABLE} -- -- === -- diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index e3da2ee29..a3858a7c7 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -6,7 +6,7 @@ -- -- === -- --- @module AI_Cargo_Dispatcher +-- @module AI.AI_Cargo_Dispatcher --- @type AI_CARGO_DISPATCHER -- @extends Core.Fsm#FSM diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index d7b21b13d..b2808924a 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -6,13 +6,13 @@ -- -- === -- --- @module AI_Cargo_Dispatcher_APC +-- @module AI.AI_Cargo_Dispatcher_APC --- @type AI_CARGO_DISPATCHER_APC -- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER ---- # AI\_CARGO\_DISPATCHER\_APC class, extends @{Core.Base#BASE} +--- # AI\_CARGO\_DISPATCHER\_APC class, extends @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} -- -- ![Banner Image](..\Presentations\AI_CARGO_DISPATCHER_APC\Dia1.JPG) -- @@ -81,7 +81,7 @@ AI_CARGO_DISPATCHER_APC = { --- Creates a new AI_CARGO_DISPATCHER_APC object. -- @param #AI_CARGO_DISPATCHER_APC self --- @param Core.Set#SET_GROUP SetAPC The collection of APC @{Group}s. +-- @param Core.Set#SET_GROUP SetAPC The collection of APC @{Wrapper.Group}s. -- @param Core.Set#SET_CARGO SetCargo The collection of @{Cargo} derived objects. -- @param Core.Set#SET_ZONE SetDeployZone The collection of deploy @{Zone}s, which are used to where the cargo will be deployed by the APCs. -- @param #number CombatRadius The cargo will be unloaded from the APC and engage the enemy if the enemy is within CombatRadius range. The radius is in meters, the default value is 500 meters. diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua index 4deaef1d9..6007e9973 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua @@ -6,13 +6,13 @@ -- -- === -- --- @module AI_Cargo_Dispatcher_Airplane +-- @module AI.AI_Cargo_Dispatcher_Airplane --- @type AI_CARGO_DISPATCHER_AIRPLANE -- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER ---- # AI\_CARGO\_DISPATCHER\_AIRPLANE class, extends @{Core.Base#BASE} +--- # AI\_CARGO\_DISPATCHER\_AIRPLANE class, extends @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} -- -- === -- diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index f7a7a07c3..aa3b264fc 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -8,7 +8,7 @@ -- -- === -- --- @module AI_Cargo_Dispatcher_Helicopter +-- @module AI.AI_Cargo_Dispatcher_Helicopter --- @type AI_CARGO_DISPATCHER_HELICOPTER -- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER @@ -93,7 +93,7 @@ AI_CARGO_DISPATCHER_HELICOPTER = { --- Creates a new AI_CARGO_DISPATCHER_HELICOPTER object. -- @param #AI_CARGO_DISPATCHER_HELICOPTER self --- @param Core.Set#SET_GROUP SetHelicopter The collection of Helicopter @{Group}s. +-- @param Core.Set#SET_GROUP SetHelicopter The collection of Helicopter @{Wrapper.Group}s. -- @param Core.Set#SET_CARGO SetCargo The collection of @{Cargo} derived objects. -- @param Core.Set#SET_ZONE SetDeployZone The collection of deploy @{Zone}s, which are used to where the cargo will be deployed by the Helicopters. -- @return #AI_CARGO_DISPATCHER_HELICOPTER diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 643d4e529..555cb858b 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -6,13 +6,13 @@ -- -- === -- --- @module AI_Cargo_Helicopter +-- @module AI.AI_Cargo_Helicopter --- @type AI_CARGO_HELICOPTER -- @extends Core.Fsm#FSM_CONTROLLABLE ---- # AI\_CARGO\_TROOPS class, extends @{Core.Base@BASE} +--- # AI\_CARGO\_TROOPS class, extends @{Core.Fsm#FSM_CONTROLLABLE} -- -- === -- diff --git a/Moose Development/Moose/AI/AI_Formation.lua b/Moose Development/Moose/AI/AI_Formation.lua index 7e5b2828e..db5e6edad 100644 --- a/Moose Development/Moose/AI/AI_Formation.lua +++ b/Moose Development/Moose/AI/AI_Formation.lua @@ -6,7 +6,7 @@ -- -- === -- --- AI_FORMATION makes AI @{GROUP}s fly in formation of various compositions. +-- AI_FORMATION makes AI @{Wrapper.Group}s fly in formation of various compositions. -- The AI_FORMATION class models formations in a different manner than the internal DCS formation logic!!! -- The purpose of the class is to: -- @@ -45,13 +45,13 @@ -- -- === -- --- @module AI_Formation +-- @module AI.AI_Formation --- AI_FORMATION class -- @type AI_FORMATION --- @extends Fsm#FSM_SET --- @field Unit#UNIT FollowUnit --- @field Set#SET_GROUP FollowGroupSet +-- @extends Core.Fsm#FSM_SET +-- @field Wrapper.Unit#UNIT FollowUnit +-- @field Core.Set#SET_GROUP FollowGroupSet -- @field #string FollowName -- @field #AI_FORMATION.MODE FollowMode The mode the escort is in. -- @field Scheduler#SCHEDULER FollowScheduler The instance of the SCHEDULER class. @@ -61,9 +61,9 @@ -- @field DCSTypes#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the FollowGroup. ---- # AI_FORMATION class, extends @{Fsm#FSM_SET} +--- # AI_FORMATION class, extends @{Core.Fsm#FSM_SET} -- --- The #AI_FORMATION class allows you to build large formations, make AI follow a @{Client#CLIENT} (player) leader or a @{Unit#UNIT} (AI) leader. +-- The #AI_FORMATION class allows you to build large formations, make AI follow a @{Wrapper.Client#CLIENT} (player) leader or a @{Unit#UNIT} (AI) leader. -- -- AI_FORMATION makes AI @{GROUP}s fly in formation of various compositions. -- The AI_FORMATION class models formations in a different manner than the internal DCS formation logic!!! @@ -89,7 +89,7 @@ -- -- Create a new SPAWN object with the @{#AI_FORMATION.New} method: -- --- * @{Follow#AI_FORMATION.New}(): Creates a new AI_FORMATION object from a @{Group#GROUP} for a @{Client#CLIENT} or a @{Unit#UNIT}, with an optional briefing text. +-- * @{Follow#AI_FORMATION.New}(): Creates a new AI_FORMATION object from a @{Wrapper.Group#GROUP} for a @{Wrapper.Client#CLIENT} or a @{Unit#UNIT}, with an optional briefing text. -- -- ## Formation methods -- @@ -147,7 +147,7 @@ AI_FORMATION = { --- AI_FORMATION class constructor for an AI group -- @param #AI_FORMATION self --- @param Unit#UNIT FollowUnit The UNIT leading the FolllowGroupSet. +-- @param Wrapper.Unit#UNIT FollowUnit The UNIT leading the FolllowGroupSet. -- @param Core.Set#SET_GROUP FollowGroupSet The group AI escorting the FollowUnit. -- @param #string FollowName Name of the escort. -- @return #AI_FORMATION self @@ -155,8 +155,8 @@ function AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefin local self = BASE:Inherit( self, FSM_SET:New( FollowGroupSet ) ) self:F( { FollowUnit, FollowGroupSet, FollowName } ) - self.FollowUnit = FollowUnit -- Unit#UNIT - self.FollowGroupSet = FollowGroupSet -- Set#SET_GROUP + self.FollowUnit = FollowUnit -- Wrapper.Unit#UNIT + self.FollowGroupSet = FollowGroupSet -- Core.Set#SET_GROUP self:SetFlightRandomization( 2 ) diff --git a/Moose Development/Moose/AI/AI_Patrol.lua b/Moose Development/Moose/AI/AI_Patrol.lua index 4cc6e7d6e..77026ce02 100644 --- a/Moose Development/Moose/AI/AI_Patrol.lua +++ b/Moose Development/Moose/AI/AI_Patrol.lua @@ -30,27 +30,27 @@ -- -- === -- --- @module AI_Patrol +-- @module AI.AI_Patrol --- AI_PATROL_ZONE class -- @type AI_PATROL_ZONE --- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Controllable} patrolling. +-- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Wrapper.Controllable} patrolling. -- @field Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @field Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. -- @field Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @field Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. --- @field Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. +-- @field Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. +-- @field Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h. -- @field Core.Spawn#SPAWN CoordTest -- @extends Core.Fsm#FSM_CONTROLLABLE ---- # AI_PATROL_ZONE class, extends @{Fsm#FSM_CONTROLLABLE} +--- # AI_PATROL_ZONE class, extends @{Core.Fsm#FSM_CONTROLLABLE} -- --- The AI_PATROL_ZONE class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}. +-- The AI_PATROL_ZONE class implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Controllable} or @{Wrapper.Group}. -- -- ![Process](..\Presentations\AI_PATROL\Dia3.JPG) -- --- The AI_PATROL_ZONE is assigned a @{Group} and this must be done before the AI_PATROL_ZONE process can be started using the **Start** event. +-- The AI_PATROL_ZONE is assigned a @{Wrapper.Group} and this must be done before the AI_PATROL_ZONE process can be started using the **Start** event. -- -- ![Process](..\Presentations\AI_PATROL\Dia4.JPG) -- @@ -123,7 +123,7 @@ -- * @{#AI_PATROL_ZONE.SetDetectionOff}(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased. -- -- The detection frequency can be set with @{#AI_PATROL_ZONE.SetRefreshTimeInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection. --- Use the method @{#AI_PATROL_ZONE.GetDetectedUnits}() to obtain a list of the @{Unit}s detected by the AI. +-- Use the method @{#AI_PATROL_ZONE.GetDetectedUnits}() to obtain a list of the @{Wrapper.Unit}s detected by the AI. -- -- The detection can be filtered to potential targets in a specific zone. -- Use the method @{#AI_PATROL_ZONE.SetDetectionZone}() to set the zone where targets need to be detected. @@ -157,8 +157,8 @@ AI_PATROL_ZONE = { -- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. -- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. -- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h. -- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO -- @return #AI_PATROL_ZONE self -- @usage @@ -454,8 +454,8 @@ end --- Sets (modifies) the minimum and maximum speed of the patrol. -- @param #AI_PATROL_ZONE self --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. +-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h. -- @return #AI_PATROL_ZONE self function AI_PATROL_ZONE:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed ) self:F2( { PatrolMinSpeed, PatrolMaxSpeed } ) @@ -564,7 +564,7 @@ end --- Gets a list of @{Unit#UNIT}s that were detected by the AI. -- No filtering is applied, so, ANY detected UNIT can be in this list. --- It is up to the mission designer to use the @{Unit} class and methods to filter the targets. +-- It is up to the mission designer to use the @{Wrapper.Unit} class and methods to filter the targets. -- @param #AI_PATROL_ZONE self -- @return #table The list of @{Unit#UNIT}s function AI_PATROL_ZONE:GetDetectedUnits() diff --git a/Moose Development/Moose/Actions/Act_Account.lua b/Moose Development/Moose/Actions/Act_Account.lua index 809a28690..98b84d819 100644 --- a/Moose Development/Moose/Actions/Act_Account.lua +++ b/Moose Development/Moose/Actions/Act_Account.lua @@ -1,4 +1,4 @@ ---- **Actions** - ACT_ACCOUNT_ classes **account for** (detect, count & report) various DCS events occuring on @{Unit}s. +--- **Actions** - ACT_ACCOUNT_ classes **account for** (detect, count & report) various DCS events occuring on @{Wrapper.Unit}s. -- -- ![Banner Image](..\Presentations\ACT_ACCOUNT\Dia1.JPG) -- @@ -55,7 +55,7 @@ do -- ACT_ACCOUNT -- These state transition methods need to provide a return value, which is specified at the function description. -- -- @type ACT_ACCOUNT - -- @field Set#SET_UNIT TargetSetUnit + -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends Core.Fsm#FSM_PROCESS ACT_ACCOUNT = { ClassName = "ACT_ACCOUNT", @@ -151,7 +151,7 @@ do -- ACT_ACCOUNT_DEADS -- * @{#ACT_ACCOUNT_DEADS.New}(): Creates a new ACT_ACCOUNT_DEADS object. -- -- @type ACT_ACCOUNT_DEADS - -- @field Set#SET_UNIT TargetSetUnit + -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends #ACT_ACCOUNT ACT_ACCOUNT_DEADS = { ClassName = "ACT_ACCOUNT_DEADS", @@ -160,7 +160,7 @@ do -- ACT_ACCOUNT_DEADS --- Creates a new DESTROY process. -- @param #ACT_ACCOUNT_DEADS self - -- @param Set#SET_UNIT TargetSetUnit + -- @param Core.Set#SET_UNIT TargetSetUnit -- @param #string TaskName function ACT_ACCOUNT_DEADS:New() -- Inherits from BASE @@ -285,7 +285,7 @@ do -- ACT_ACCOUNT_DEADS end --- @param #ACT_ACCOUNT_DEADS self - -- @param Event#EVENTDATA EventData + -- @param Core.Event#EVENTDATA EventData function ACT_ACCOUNT_DEADS:onfuncEventDead( EventData ) self:T( { "EventDead", EventData } ) @@ -297,7 +297,7 @@ do -- ACT_ACCOUNT_DEADS --- DCS Events --- @param #ACT_ACCOUNT_DEADS self - -- @param Event#EVENTDATA EventData + -- @param Core.Event#EVENTDATA EventData function ACT_ACCOUNT_DEADS:onfuncEventCrash( EventData ) self:T( { "EventDead", EventData } ) diff --git a/Moose Development/Moose/Actions/Act_Assist.lua b/Moose Development/Moose/Actions/Act_Assist.lua index 7d4e55d85..ccd56e310 100644 --- a/Moose Development/Moose/Actions/Act_Assist.lua +++ b/Moose Development/Moose/Actions/Act_Assist.lua @@ -142,7 +142,7 @@ do -- ACT_ASSIST_SMOKE_TARGETS_ZONE --- ACT_ASSIST_SMOKE_TARGETS_ZONE class -- @type ACT_ASSIST_SMOKE_TARGETS_ZONE - -- @field Set#SET_UNIT TargetSetUnit + -- @field Core.Set#SET_UNIT TargetSetUnit -- @field Core.Zone#ZONE_BASE TargetZone -- @extends #ACT_ASSIST ACT_ASSIST_SMOKE_TARGETS_ZONE = { @@ -158,7 +158,7 @@ do -- ACT_ASSIST_SMOKE_TARGETS_ZONE --- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator. -- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self - -- @param Set#SET_UNIT TargetSetUnit + -- @param Core.Set#SET_UNIT TargetSetUnit -- @param Core.Zone#ZONE_BASE TargetZone function ACT_ASSIST_SMOKE_TARGETS_ZONE:New( TargetSetUnit, TargetZone ) local self = BASE:Inherit( self, ACT_ASSIST:New() ) -- #ACT_ASSIST @@ -177,7 +177,7 @@ do -- ACT_ASSIST_SMOKE_TARGETS_ZONE --- Creates a new target smoking state machine. The process will request from the menu if it accepts the task, if not, the unit is removed from the simulator. -- @param #ACT_ASSIST_SMOKE_TARGETS_ZONE self - -- @param Set#SET_UNIT TargetSetUnit + -- @param Core.Set#SET_UNIT TargetSetUnit -- @param Core.Zone#ZONE_BASE TargetZone -- @return #ACT_ASSIST_SMOKE_TARGETS_ZONE self function ACT_ASSIST_SMOKE_TARGETS_ZONE:Init( TargetSetUnit, TargetZone ) diff --git a/Moose Development/Moose/Actions/Act_Route.lua b/Moose Development/Moose/Actions/Act_Route.lua index 3a4806468..eba201588 100644 --- a/Moose Development/Moose/Actions/Act_Route.lua +++ b/Moose Development/Moose/Actions/Act_Route.lua @@ -62,7 +62,7 @@ -- -- # 1) @{#ACT_ROUTE_ZONE} class, extends @{Fsm.Route#ACT_ROUTE} -- --- The ACT_ROUTE_ZONE class implements the core functions to route an AIR @{Controllable} player @{Unit} to a @{Zone}. +-- The ACT_ROUTE_ZONE class implements the core functions to route an AIR @{Wrapper.Controllable} player @{Wrapper.Unit} to a @{Zone}. -- The player receives on perioding times messages with the coordinates of the route to follow. -- Upon arrival at the zone, a confirmation of arrival is sent, and the process will be ended. -- diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index f0f99dcdb..566ba7beb 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -16,7 +16,7 @@ -- -- === -- --- @module Cargo +-- @module Cargo.Cargo -- Events @@ -152,7 +152,7 @@ do -- CARGO -- @field #boolean Representable This flag defines if the cargo can be represented by a DCS Unit. -- @field #boolean Containable This flag defines if the cargo can be contained within a DCS Unit. - --- # (R2.4) CARGO class, extends @{Fsm#FSM_PROCESS} + --- # (R2.4) CARGO class, extends @{Core.Fsm#FSM_PROCESS} -- -- The CARGO class defines the core functions that defines a cargo object within MOOSE. -- A cargo is a **logical object** defined that is available for transport, and has a life status within a simulation. @@ -708,11 +708,11 @@ do -- CARGO return self end - --- Send a CC message to a @{Group}. + --- Send a CC message to a @{Wrapper.Group}. -- @param #CARGO self -- @param #string Message -- @param Wrapper.Group#GROUP CarrierGroup The Carrier Group. - -- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown. + -- @param #string Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown. function CARGO:MessageToGroup( Message, CarrierGroup, Name ) MESSAGE:New( Message, 20, "Cargo " .. self:GetName() ):ToGroup( CarrierGroup ) @@ -864,11 +864,11 @@ do -- CARGO_REPRESENTABLE return self end - --- Send a message to a @{Group} through a communication channel near the cargo. + --- Send a message to a @{Wrapper.Group} through a communication channel near the cargo. -- @param #CARGO_REPRESENTABLE self -- @param #string Message -- @param Wrapper.Group#GROUP TaskGroup - -- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown. + -- @param #string Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown. function CARGO_REPRESENTABLE:MessageToGroup( Message, TaskGroup, Name ) local CoordinateZone = ZONE_RADIUS:New( "Zone" , self:GetCoordinate():GetVec2(), 500 ) @@ -916,11 +916,11 @@ do -- CARGO_REPORTABLE return self end - --- Send a CC message to a @{Group}. + --- Send a CC message to a @{Wrapper.Group}. -- @param #CARGO_REPORTABLE self -- @param #string Message -- @param Wrapper.Group#GROUP TaskGroup - -- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown. + -- @param #string Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown. function CARGO_REPORTABLE:MessageToGroup( Message, TaskGroup, Name ) MESSAGE:New( Message, 20, "Cargo " .. self:GetName() .. " reporting" ):ToGroup( TaskGroup ) diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua index 7fc3a7293..0256721da 100644 --- a/Moose Development/Moose/Cargo/CargoCrate.lua +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -17,7 +17,7 @@ -- -- === -- --- @module CargoCrate +-- @module Cargo.CargoCrate do -- CARGO_CRATE @@ -25,7 +25,7 @@ do -- CARGO_CRATE -- @type CARGO_CRATE -- @extends Cargo.Cargo#CARGO_REPRESENTABLE - --- # CARGO\_CRATE class, extends @{#CARGO_REPRESENTABLE} + --- # CARGO\_CRATE class, extends @{Cargo.Cargo#CARGO_REPRESENTABLE} -- -- The CARGO\_CRATE class defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. -- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO\_CRATE objects to and from carriers. @@ -165,7 +165,7 @@ do -- CARGO_CRATE end --- Check if Cargo Crate is in the radius for the Cargo to be reported. - -- @param #CARGO self + -- @param #CARGO_CRATE self -- @param Core.Point#COORDINATE Coordinate -- @return #boolean true if the Cargo Crate is within the report radius. function CARGO_CRATE:IsInReportRadius( Coordinate ) @@ -185,7 +185,7 @@ do -- CARGO_CRATE --- Check if Cargo Crate is in the radius for the Cargo to be Boarded or Loaded. - -- @param #CARGO self + -- @param #CARGO_CRATE self -- @param Core.Point#Coordinate Coordinate -- @return #boolean true if the Cargo Crate is within the loading radius. function CARGO_CRATE:IsInLoadRadius( Coordinate ) diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 5af3978c4..b2fab505c 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -1,4 +1,4 @@ ---- **Cargo** -- Management of grouped cargo logistics, which are based on a @{Group} object. +--- **Cargo** -- Management of grouped cargo logistics, which are based on a @{Wrapper.Group} object. -- -- === -- @@ -17,7 +17,7 @@ -- -- === -- --- @module CargoGroup +-- @module Cargo.CargoGroup do -- CARGO_GROUP @@ -27,9 +27,9 @@ do -- CARGO_GROUP -- @field Core.Set#SET_CARGO CargoSet The collection of derived CARGO objects. -- @field #string GroupName The name of the CargoGroup. - --- # CARGO\_GROUP class + --- # CARGO\_GROUP class, extends @{Cargo.Cargo#CARGO_REPORTABLE} -- - -- The CARGO\_GROUP class defines a cargo that is represented by a @{Group} object within the simulator. + -- The CARGO\_GROUP class defines a cargo that is represented by a @{Wrapper.Group} object within the simulator. -- The cargo can be Loaded, UnLoaded, Boarded, UnBoarded to and from Carriers. -- -- The above cargo classes are used by the AI\_CARGO\_ classes to allow AI groups to transport cargo: @@ -53,7 +53,7 @@ do -- CARGO_GROUP } --- CARGO_GROUP constructor. - -- This make a new CARGO_GROUP from a @{Group} object. + -- This make a new CARGO_GROUP from a @{Wrapper.Group} object. -- It will "ungroup" the group object within the sim, and will create a @{Set} of individual Unit objects. -- @param #CARGO_GROUP self -- @param Wrapper.Group#GROUP CargoGroup @@ -233,7 +233,7 @@ do -- CARGO_GROUP if self:IsDestroyed() or self:IsUnLoaded() or self:IsBoarding() or self:IsUnboarding() then Destroyed = true for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do - local Cargo = CargoData -- #CARGO + local Cargo = CargoData -- Cargo.Cargo#CARGO if Cargo:IsAlive() then Destroyed = false else @@ -667,7 +667,7 @@ do -- CARGO_GROUP self:F( { "Respawning" } ) for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do - local Cargo = CargoData -- #CARGO + local Cargo = CargoData -- Cargo.Cargo#CARGO Cargo:Destroy() Cargo:SetStartState( "UnLoaded" ) end @@ -713,7 +713,7 @@ do -- CARGO_GROUP -- @param Utilities.Utils#FLARECOLOR FlareColor function CARGO_GROUP:Flare( FlareColor ) - local Cargo = self.CargoSet:GetFirst() -- #CARGO + local Cargo = self.CargoSet:GetFirst() -- Cargo.Cargo#CARGO if Cargo then Cargo:Flare( FlareColor ) end @@ -725,7 +725,7 @@ do -- CARGO_GROUP -- @param #number Radius The radius of randomization around the center of the first element of the CargoGroup. function CARGO_GROUP:Smoke( SmokeColor, Radius ) - local Cargo = self.CargoSet:GetFirst() -- #CARGO + local Cargo = self.CargoSet:GetFirst() -- Cargo.Cargo#CARGO if Cargo then Cargo:Smoke( SmokeColor, Radius ) @@ -733,14 +733,14 @@ do -- CARGO_GROUP end --- Check if the first element of the CargoGroup is the given @{Zone}. - -- @param #CARGO self + -- @param #CARGO_GROUP self -- @param Core.Zone#ZONE_BASE Zone -- @return #boolean **true** if the first element of the CargoGroup is in the Zone -- @return #boolean **false** if there is no element of the CargoGroup in the Zone. function CARGO_GROUP:IsInZone( Zone ) --self:F( { Zone } ) - local Cargo = self.CargoSet:GetFirst() -- #CARGO + local Cargo = self.CargoSet:GetFirst() -- Cargo.Cargo#CARGO if Cargo then return Cargo:IsInZone( Zone ) diff --git a/Moose Development/Moose/Cargo/CargoSlingload.lua b/Moose Development/Moose/Cargo/CargoSlingload.lua index cb7898175..b16e1ffe6 100644 --- a/Moose Development/Moose/Cargo/CargoSlingload.lua +++ b/Moose Development/Moose/Cargo/CargoSlingload.lua @@ -17,7 +17,7 @@ -- -- === -- --- @module CargoCrate +-- @module Cargo.CargoSlingload do -- CARGO_SLINGLOAD @@ -26,7 +26,7 @@ do -- CARGO_SLINGLOAD -- @type CARGO_SLINGLOAD -- @extends Cargo.Cargo#CARGO_REPRESENTABLE - --- # CARGO\_CRATE class, extends @{#CARGO_REPRESENTABLE} + --- # CARGO\_CRATE class, extends @{Cargo.Cargo#CARGO_REPRESENTABLE} -- -- The CARGO\_CRATE class defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. -- @@ -87,8 +87,8 @@ do -- CARGO_SLINGLOAD --- Check if the cargo can be Slingloaded. - -- @param #CARGO self - function CARGO:CanSlingload() + -- @param #CARGO_SLINGLOAD self + function CARGO_SLINGLOAD:CanSlingload() return true end diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index 85b6ea6cc..43d5a7350 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -1,4 +1,4 @@ ---- **Cargo** -- Management of single cargo logistics, which are based on a @{Unit} object. +--- **Cargo** -- Management of single cargo logistics, which are based on a @{Wrapper.Unit} object. -- -- === -- @@ -17,7 +17,7 @@ -- -- === -- --- @module CargoUnit +-- @module Cargo.CargoUnit do -- CARGO_UNIT @@ -25,7 +25,7 @@ do -- CARGO_UNIT -- @type CARGO_UNIT -- @extends Cargo.Cargo#CARGO_REPRESENTABLE - --- # CARGO\_UNIT class, extends @{#CARGO_REPRESENTABLE} + --- # CARGO\_UNIT class, extends @{Cargo.Cargo#CARGO_REPRESENTABLEE} -- -- The CARGO\_UNIT class defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. -- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO\_UNIT objects to and from carriers. diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index 8dc4eb0e8..f9577a7d1 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -42,8 +42,8 @@ local _ClassID = 0 -- -- ## 1.1) BASE constructor -- --- Any class derived from BASE, will use the @{Base#BASE.New} constructor embedded in the @{Base#BASE.Inherit} method. --- See an example at the @{Base#BASE.New} method how this is done. +-- Any class derived from BASE, will use the @{Core.Base#BASE.New} constructor embedded in the @{Core.Base#BASE.Inherit} method. +-- See an example at the @{Core.Base#BASE.New} method how this is done. -- -- ## 1.2) Trace information for debugging -- diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index ecd6cddbd..ec7c4c9e5 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -13,7 +13,7 @@ --- @type DATABASE -- @extends Core.Base#BASE ---- # DATABASE class, extends @{Base#BASE} +--- # DATABASE class, extends @{Core.Base#BASE} -- -- Mission designers can use the DATABASE class to refer to: -- diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 01804da7a..d786101fa 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -61,8 +61,8 @@ -- So, when the DCS event occurs, the class will be notified of that event. -- There are two functions which you use to subscribe to or unsubscribe from an event. -- --- * @{Base#BASE.HandleEvent}(): Subscribe to a DCS Event. --- * @{Base#BASE.UnHandleEvent}(): Unsubscribe from a DCS Event. +-- * @{Core.Base#BASE.HandleEvent}(): Subscribe to a DCS Event. +-- * @{Core.Base#BASE.UnHandleEvent}(): Unsubscribe from a DCS Event. -- -- Note that for a UNIT, the event will be handled **for that UNIT only**! -- Note that for a GROUP, the event will be handled **for all the UNITs in that GROUP only**! @@ -112,7 +112,7 @@ -- # 2) EVENTS type -- -- The EVENTS structure contains names for all the different DCS events that objects can subscribe to using the --- @{Base#BASE.HandleEvent}() method. +-- @{Core.Base#BASE.HandleEvent}() method. -- -- # 3) EVENTDATA type -- @@ -183,7 +183,7 @@ world.event.S_EVENT_NEW_ZONE = world.event.S_EVENT_MAX + 1002 world.event.S_EVENT_DELETE_ZONE = world.event.S_EVENT_MAX + 1003 --- The different types of events supported by MOOSE. --- Use this structure to subscribe to events using the @{Base#BASE.HandleEvent}() method. +-- Use this structure to subscribe to events using the @{Core.Base#BASE.HandleEvent}() method. -- @type EVENTS EVENTS = { Shot = world.event.S_EVENT_SHOT, @@ -235,7 +235,7 @@ EVENTS = { -- @field #string IniUnitName (UNIT/STATIC) The initiating UNIT name (same as IniDCSUnitName). -- @field Dcs.DCSGroup#Group IniDCSGroup (UNIT) The initiating {DCSGroup#Group}. -- @field #string IniDCSGroupName (UNIT) The initiating Group name. --- @field Wrapper.Group#GROUP IniGroup (UNIT) The initiating MOOSE wrapper @{Group#GROUP} of the initiator Group object. +-- @field Wrapper.Group#GROUP IniGroup (UNIT) The initiating MOOSE wrapper @{Wrapper.Group#GROUP} of the initiator Group object. -- @field #string IniGroupName UNIT) The initiating GROUP name (same as IniDCSGroupName). -- @field #string IniPlayerName (UNIT) The name of the initiating player in case the Unit is a client or player slot. -- @field Dcs.DCScoalition#coalition.side IniCoalition (UNIT) The coalition of the initiator. @@ -250,7 +250,7 @@ EVENTS = { -- @field #string TgtUnitName (UNIT/STATIC) The target UNIT name (same as TgtDCSUnitName). -- @field Dcs.DCSGroup#Group TgtDCSGroup (UNIT) The target {DCSGroup#Group}. -- @field #string TgtDCSGroupName (UNIT) The target Group name. --- @field Wrapper.Group#GROUP TgtGroup (UNIT) The target MOOSE wrapper @{Group#GROUP} of the target Group object. +-- @field Wrapper.Group#GROUP TgtGroup (UNIT) The target MOOSE wrapper @{Wrapper.Group#GROUP} of the target Group object. -- @field #string TgtGroupName (UNIT) The target GROUP name (same as TgtDCSGroupName). -- @field #string TgtPlayerName (UNIT) The name of the target player in case the Unit is a client or player slot. -- @field Dcs.DCScoalition#coalition.side TgtCoalition (UNIT) The coalition of the target. @@ -527,7 +527,7 @@ end ---- Clears all event subscriptions for a @{Base#BASE} derived object. +--- Clears all event subscriptions for a @{Core.Base#BASE} derived object. -- @param #EVENT self -- @param Core.Base#BASE EventObject function EVENT:RemoveAll( EventObject ) diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index e3b2aacb1..e81bb9519 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -52,7 +52,7 @@ -- -- * @{#FSM_TASK}: Models Finite State Machines for @{Task}s. -- * @{#FSM_PROCESS}: Models Finite State Machines for @{Task} actions, which control @{Client}s. --- * @{#FSM_CONTROLLABLE}: Models Finite State Machines for @{Controllable}s, which are @{Group}s, @{Unit}s, @{Client}s. +-- * @{#FSM_CONTROLLABLE}: Models Finite State Machines for @{Wrapper.Controllable}s, which are @{Wrapper.Group}s, @{Wrapper.Unit}s, @{Client}s. -- * @{#FSM_SET}: Models Finite State Machines for @{Set}s. Note that these FSMs control multiple objects!!! So State concerns here -- for multiple objects or the position of the state machine in the process. -- @@ -72,7 +72,7 @@ do -- FSM -- @extends Core.Base#BASE - --- # FSM class, extends @{Base#BASE} + --- # FSM class, extends @{Core.Base#BASE} -- -- A Finite State Machine (FSM) models a process flow that transitions between various **States** through triggered **Events**. -- @@ -410,7 +410,7 @@ do -- FSM return self._Transitions or {} end - --- Set the default @{Process} template with key ProcessName providing the ProcessClass and the process object when it is assigned to a @{Controllable} by the task. + --- Set the default @{Process} template with key ProcessName providing the ProcessClass and the process object when it is assigned to a @{Wrapper.Controllable} by the task. -- @param #FSM self -- @param #table From Can contain a string indicating the From state or a table of strings containing multiple From states. -- @param #string Event The Event name. @@ -806,7 +806,7 @@ do -- FSM_CONTROLLABLE --- # FSM_CONTROLLABLE, extends @{#FSM} -- - -- FSM_CONTROLLABLE class models Finite State Machines for @{Controllable}s, which are @{Group}s, @{Unit}s, @{Client}s. + -- FSM_CONTROLLABLE class models Finite State Machines for @{Wrapper.Controllable}s, which are @{Wrapper.Group}s, @{Wrapper.Unit}s, @{Client}s. -- -- === -- @@ -1116,7 +1116,7 @@ do -- FSM_PROCESS - --- Assign the process to a @{Unit} and activate the process. + --- Assign the process to a @{Wrapper.Unit} and activate the process. -- @param #FSM_PROCESS self -- @param Task.Tasking#TASK Task -- @param Wrapper.Unit#UNIT ProcessUnit diff --git a/Moose Development/Moose/Core/Goal.lua b/Moose Development/Moose/Core/Goal.lua index e4373438e..ba8e9ea1d 100644 --- a/Moose Development/Moose/Core/Goal.lua +++ b/Moose Development/Moose/Core/Goal.lua @@ -18,7 +18,7 @@ do -- Goal -- @extends Core.Fsm#FSM - --- # GOAL class, extends @{Fsm#FSM} + --- # GOAL class, extends @{Core.Fsm#FSM} -- -- GOAL models processes that have an objective with a defined achievement. Derived classes implement the ways how the achievements can be realized. -- diff --git a/Moose Development/Moose/Core/Menu.lua b/Moose Development/Moose/Core/Menu.lua index 03eb1c156..6850b9213 100644 --- a/Moose Development/Moose/Core/Menu.lua +++ b/Moose Development/Moose/Core/Menu.lua @@ -182,7 +182,7 @@ do -- MENU_BASE --- @type MENU_BASE -- @extends Base#BASE - --- # MENU_BASE class, extends @{Base#BASE} + --- # MENU_BASE class, extends @{Core.Base#BASE} -- The MENU_BASE class defines the main MENU class where other MENU classes are derived from. -- This is an abstract class, so don't use it. -- @field #MENU_BASE @@ -286,7 +286,7 @@ do -- MENU_COMMAND_BASE -- @field #function MenuCallHandler -- @extends Core.Menu#MENU_BASE - --- # MENU_COMMAND_BASE class, extends @{Base#BASE} + --- # MENU_COMMAND_BASE class, extends @{Core.Base#BASE} -- ---------------------------------------------------------- -- The MENU_COMMAND_BASE class defines the main MENU class where other MENU COMMAND_ -- classes are derived from, in order to set commands. @@ -469,7 +469,7 @@ do -- MENU_MISSION_COMMAND --- MENU_MISSION constructor. Creates a new radio command item for a complete mission file, which can invoke a function with parameters. -- @param #MENU_MISSION_COMMAND self -- @param #string MenuText The text for the menu. - -- @param Menu#MENU_MISSION ParentMenu The parent menu. + -- @param Core.Menu#MENU_MISSION ParentMenu The parent menu. -- @param CommandMenuFunction A function that is called when the menu key is pressed. -- @param CommandMenuArgument An argument for the function. There can only be ONE argument given. So multiple arguments must be wrapped into a table. See the below example how to do this. -- @return #MENU_MISSION_COMMAND self @@ -695,7 +695,7 @@ do -- MENU_COALITION_COMMAND -- @param #MENU_COALITION_COMMAND self -- @param Dcs.DCSCoalition#coalition.side Coalition The coalition owning the menu. -- @param #string MenuText The text for the menu. - -- @param Menu#MENU_COALITION ParentMenu The parent menu. + -- @param Core.Menu#MENU_COALITION ParentMenu The parent menu. -- @param CommandMenuFunction A function that is called when the menu key is pressed. -- @param CommandMenuArgument An argument for the function. There can only be ONE argument given. So multiple arguments must be wrapped into a table. See the below example how to do this. -- @return #MENU_COALITION_COMMAND diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 904711e7b..d2256e436 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -10,7 +10,7 @@ -- @type MESSAGE -- @extends Core.Base#BASE ---- # MESSAGE class, extends @{Base#BASE} +--- # MESSAGE class, extends @{Core.Base#BASE} -- -- Message System to display Messages to Clients, Coalitions or All. -- Messages are shown on the display panel for an amount of seconds, and will then disappear. @@ -26,7 +26,7 @@ -- Messages are sent: -- -- * To a @{Client} using @{Message#MESSAGE.ToClient}(). --- * To a @{Group} using @{Message#MESSAGE.ToGroup}() +-- * To a @{Wrapper.Group} using @{Message#MESSAGE.ToGroup}() -- * To a coalition using @{Message#MESSAGE.ToCoalition}(). -- * To the red coalition using @{Message#MESSAGE.ToRed}(). -- * To the blue coalition using @{Message#MESSAGE.ToBlue}(). diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 5d914d338..cd8390f7b 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -38,7 +38,7 @@ do -- COORDINATE -- @extends Core.Base#BASE - --- # COORDINATE class, extends @{Base#BASE} + --- # COORDINATE class, extends @{Core.Base#BASE} -- -- COORDINATE defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space. -- @@ -57,7 +57,7 @@ do -- COORDINATE -- * @{#COORDINATE.WaypointAir}(): Build an air route point. -- * @{#COORDINATE.WaypointGround}(): Build a ground route point. -- - -- Route points can be used in the Route methods of the @{Group#GROUP} class. + -- Route points can be used in the Route methods of the @{Wrapper.Group#GROUP} class. -- -- -- ## Smoke, flare, explode, illuminate @@ -1189,7 +1189,7 @@ do -- COORDINATE --- Mark to Group -- @param #COORDINATE self -- @param #string MarkText Free format text that shows the marking clarification. - -- @param Wrapper.Group#GROUP MarkGroup The @{Group} that receives the mark. + -- @param Wrapper.Group#GROUP MarkGroup The @{Wrapper.Group} that receives the mark. -- @return #number The resulting Mark ID which is a number. -- @usage -- local TargetCoord = TargetGroup:GetCoordinate() diff --git a/Moose Development/Moose/Core/Radio.lua b/Moose Development/Moose/Core/Radio.lua index 61eb02a76..4bc0c24a1 100644 --- a/Moose Development/Moose/Core/Radio.lua +++ b/Moose Development/Moose/Core/Radio.lua @@ -18,9 +18,9 @@ -- * They need to be added in .\l10n\DEFAULT\ in you .miz file (wich can be decompressed like a .zip file), -- * For simplicty sake, you can **let DCS' Mission Editor add the file** itself, by creating a new Trigger with the action "Sound to Country", and choosing your sound file and a country you don't use in your mission. -- --- Due to weird DCS quirks, **radio communications behave differently** if sent by a @{Unit#UNIT} or a @{Group#GROUP} or by any other @{Positionable#POSITIONABLE} +-- Due to weird DCS quirks, **radio communications behave differently** if sent by a @{Unit#UNIT} or a @{Wrapper.Group#GROUP} or by any other @{Positionable#POSITIONABLE} -- --- * If the transmitter is a @{Unit#UNIT} or a @{Group#GROUP}, DCS will set the power of the transmission automatically, +-- * If the transmitter is a @{Unit#UNIT} or a @{Wrapper.Group#GROUP}, DCS will set the power of the transmission automatically, -- * If the transmitter is any other @{Positionable#POSITIONABLE}, the transmisison can't be subtitled or looped. -- -- Note that obviously, the **frequency** and the **modulation** of the transmission are important only if the players are piloting an **Advanced System Modelling** enabled aircraft, @@ -35,7 +35,7 @@ -- @module Core.Radio ---- # RADIO class, extends @{Base#BASE} +--- # RADIO class, extends @{Core.Base#BASE} -- -- ## RADIO usage -- @@ -45,14 +45,14 @@ -- * Then, you will **set the relevant parameters** to the transmission (see below), -- * When done, you can actually **broadcast the transmission** (i.e. play the sound) with the @{RADIO.Broadcast}() function. -- --- Methods to set relevant parameters for both a @{Unit#UNIT} or a @{Group#GROUP} or any other @{Positionable#POSITIONABLE} +-- Methods to set relevant parameters for both a @{Unit#UNIT} or a @{Wrapper.Group#GROUP} or any other @{Positionable#POSITIONABLE} -- -- * @{#RADIO.SetFileName}() : Sets the file name of your sound file (e.g. "Noise.ogg"), -- * @{#RADIO.SetFrequency}() : Sets the frequency of your transmission. -- * @{#RADIO.SetModulation}() : Sets the modulation of your transmission. -- * @{#RADIO.SetLoop}() : Choose if you want the transmission to be looped. If you need your transmission to be looped, you might need a @{#BEACON} instead... -- --- Additional Methods to set relevant parameters if the transmiter is a @{Unit#UNIT} or a @{Group#GROUP} +-- Additional Methods to set relevant parameters if the transmiter is a @{Unit#UNIT} or a @{Wrapper.Group#GROUP} -- -- * @{#RADIO.SetSubtitle}() : Set both the subtitle and its duration, -- * @{#RADIO.NewUnitTransmission}() : Shortcut to set all the relevant parameters in one method call @@ -64,7 +64,7 @@ -- -- What is this power thing ? -- --- * If your transmission is sent by a @{Positionable#POSITIONABLE} other than a @{Unit#UNIT} or a @{Group#GROUP}, you can set the power of the antenna, +-- * If your transmission is sent by a @{Positionable#POSITIONABLE} other than a @{Unit#UNIT} or a @{Wrapper.Group#GROUP}, you can set the power of the antenna, -- * Otherwise, DCS sets it automatically, depending on what's available on your Unit, -- * If the player gets **too far** from the transmiter, or if the antenna is **too weak**, the transmission will **fade** and **become noisyer**, -- * This an automated DCS calculation you have no say on, @@ -339,7 +339,7 @@ function RADIO:StopBroadcast() end ---- # BEACON class, extends @{Base#BASE} +--- # BEACON class, extends @{Core.Base#BASE} -- -- After attaching a @{#BEACON} to your @{Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want. -- There are two types of BEACONs available : the AA TACAN Beacon and the general purpose Radio Beacon. @@ -348,7 +348,7 @@ end -- -- ## AA TACAN Beacon usage -- --- This beacon only works with airborne @{Unit#UNIT} or a @{Group#GROUP}. Use @{#BEACON:AATACAN}() to set the beacon parameters and start the beacon. +-- This beacon only works with airborne @{Unit#UNIT} or a @{Wrapper.Group#GROUP}. Use @{#BEACON:AATACAN}() to set the beacon parameters and start the beacon. -- Use @#BEACON:StopAATACAN}() to stop it. -- -- ## General Purpose Radio Beacon usage diff --git a/Moose Development/Moose/Core/Report.lua b/Moose Development/Moose/Core/Report.lua index 4b32cdaf1..c6a66fca8 100644 --- a/Moose Development/Moose/Core/Report.lua +++ b/Moose Development/Moose/Core/Report.lua @@ -1,3 +1,16 @@ +--- **Core** -- **REPORT** class provides a handy means to create messages and reports. +-- +-- === +-- +-- ### Authors: +-- +-- * FlightControl : Design & Programming +-- +-- ### Contributions: +-- +-- @module Core.Report + + --- The REPORT class -- @type REPORT -- @extends Core.Base#BASE diff --git a/Moose Development/Moose/Core/Scheduler.lua b/Moose Development/Moose/Core/Scheduler.lua index 246244b29..98b824205 100644 --- a/Moose Development/Moose/Core/Scheduler.lua +++ b/Moose Development/Moose/Core/Scheduler.lua @@ -48,7 +48,7 @@ -- @extends Core.Base#BASE ---- # SCHEDULER class, extends @{Base#BASE} +--- # SCHEDULER class, extends @{Core.Base#BASE} -- -- The SCHEDULER class creates schedule. -- diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 1c7e8df5e..800511352 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -11,10 +11,10 @@ -- -- Various types of SET_ classes are available: -- --- * @{#SET_UNIT}: Defines a colleciton of @{Unit}s filtered by filter criteria. --- * @{#SET_GROUP}: Defines a collection of @{Group}s filtered by filter criteria. +-- * @{#SET_UNIT}: Defines a colleciton of @{Wrapper.Unit}s filtered by filter criteria. +-- * @{#SET_GROUP}: Defines a collection of @{Wrapper.Group}s filtered by filter criteria. -- * @{#SET_CLIENT}: Defines a collection of @{Client}s filterd by filter criteria. --- * @{#SET_AIRBASE}: Defines a collection of @{Airbase}s filtered by filter criteria. +-- * @{#SET_AIRBASE}: Defines a collection of @{Wrapper.Airbase}s filtered by filter criteria. -- -- These classes are derived from @{#SET_BASE}, which contains the main methods to manage SETs. -- @@ -41,8 +41,8 @@ -- @extends Core.Base#BASE ---- # 1) SET_BASE class, extends @{Base#BASE} --- The @{Set#SET_BASE} class defines the core functions that define a collection of objects. +--- # 1) SET_BASE class, extends @{Core.Base#BASE} +-- The @{Core.Set#SET_BASE} class defines the core functions that define a collection of objects. -- A SET provides iterators to iterate the SET, but will **temporarily** yield the ForEach interator loop at defined **"intervals"** to the mail simulator loop. -- In this way, large loops can be done while not blocking the simulator main processing loop. -- The default **"yield interval"** is after 10 objects processed. @@ -50,11 +50,11 @@ -- -- ## 1.1) Add or remove objects from the SET -- --- Some key core functions are @{Set#SET_BASE.Add} and @{Set#SET_BASE.Remove} to add or remove objects from the SET in your logic. +-- Some key core functions are @{Core.Set#SET_BASE.Add} and @{Core.Set#SET_BASE.Remove} to add or remove objects from the SET in your logic. -- -- ## 1.2) Define the SET iterator **"yield interval"** and the **"time interval"** -- --- Modify the iterator intervals with the @{Set#SET_BASE.SetInteratorIntervals} method. +-- Modify the iterator intervals with the @{Core.Set#SET_BASE.SetInteratorIntervals} method. -- You can set the **"yield interval"**, and the **"time interval"**. (See above). -- -- @field #SET_BASE SET_BASE @@ -118,7 +118,7 @@ function SET_BASE:New( Database ) return self end ---- Finds an @{Base#BASE} object based on the object Name. +--- Finds an @{Core.Base#BASE} object based on the object Name. -- @param #SET_BASE self -- @param #string ObjectName -- @return Core.Base#BASE The Object found. @@ -170,7 +170,7 @@ function SET_BASE:GetSetObjects() -- R2.3 end ---- Removes a @{Base#BASE} object from the @{Set#SET_BASE} and derived classes, based on the Object Name. +--- Removes a @{Core.Base#BASE} object from the @{Core.Set#SET_BASE} and derived classes, based on the Object Name. -- @param #SET_BASE self -- @param #string ObjectName -- @param NoTriggerEvent (optional) When `true`, the :Remove() method will not trigger a **Removed** event. @@ -195,7 +195,7 @@ function SET_BASE:Remove( ObjectName, NoTriggerEvent ) end ---- Adds a @{Base#BASE} object in the @{Set#SET_BASE}, using a given ObjectName as the index. +--- Adds a @{Core.Base#BASE} object in the @{Core.Set#SET_BASE}, using a given ObjectName as the index. -- @param #SET_BASE self -- @param #string ObjectName -- @param Core.Base#BASE Object @@ -213,7 +213,7 @@ function SET_BASE:Add( ObjectName, Object ) self:Added( ObjectName, Object ) end ---- Adds a @{Base#BASE} object in the @{Set#SET_BASE}, using the Object Name as the index. +--- Adds a @{Core.Base#BASE} object in the @{Core.Set#SET_BASE}, using the Object Name as the index. -- @param #SET_BASE self -- @param Wrapper.Object#OBJECT Object -- @return Core.Base#BASE The added BASE Object. @@ -229,7 +229,7 @@ end ---- Gets a @{Base#BASE} object from the @{Set#SET_BASE} and derived classes, based on the Object Name. +--- Gets a @{Core.Base#BASE} object from the @{Core.Set#SET_BASE} and derived classes, based on the Object Name. -- @param #SET_BASE self -- @param #string ObjectName -- @return Core.Base#BASE @@ -242,7 +242,7 @@ function SET_BASE:Get( ObjectName ) return Object end ---- Gets the first object from the @{Set#SET_BASE} and derived classes. +--- Gets the first object from the @{Core.Set#SET_BASE} and derived classes. -- @param #SET_BASE self -- @return Core.Base#BASE function SET_BASE:GetFirst() @@ -253,7 +253,7 @@ function SET_BASE:GetFirst() return FirstObject end ---- Gets the last object from the @{Set#SET_BASE} and derived classes. +--- Gets the last object from the @{Core.Set#SET_BASE} and derived classes. -- @param #SET_BASE self -- @return Core.Base#BASE function SET_BASE:GetLast() @@ -264,7 +264,7 @@ function SET_BASE:GetLast() return LastObject end ---- Gets a random object from the @{Set#SET_BASE} and derived classes. +--- Gets a random object from the @{Core.Set#SET_BASE} and derived classes. -- @param #SET_BASE self -- @return Core.Base#BASE function SET_BASE:GetRandom() @@ -275,7 +275,7 @@ function SET_BASE:GetRandom() end ---- Retrieves the amount of objects in the @{Set#SET_BASE} and derived classes. +--- Retrieves the amount of objects in the @{Core.Set#SET_BASE} and derived classes. -- @param #SET_BASE self -- @return #number Count function SET_BASE:Count() @@ -647,9 +647,9 @@ end --- @type SET_GROUP -- @extends Core.Set#SET_BASE ---- # SET_GROUP class, extends @{Set#SET_BASE} +--- # SET_GROUP class, extends @{Core.Set#SET_BASE} -- --- Mission designers can use the @{Set#SET_GROUP} class to build sets of groups belonging to certain: +-- Mission designers can use the @{Core.Set#SET_GROUP} class to build sets of groups belonging to certain: -- -- * Coalitions -- * Categories @@ -664,7 +664,7 @@ end -- -- ## 2. Add or Remove GROUP(s) from SET_GROUP -- --- GROUPS can be added and removed using the @{Set#SET_GROUP.AddGroupsByName} and @{Set#SET_GROUP.RemoveGroupsByName} respectively. +-- GROUPS can be added and removed using the @{Core.Set#SET_GROUP.AddGroupsByName} and @{Core.Set#SET_GROUP.RemoveGroupsByName} respectively. -- These methods take a single GROUP name or an array of GROUP names to be added or removed from SET_GROUP. -- -- ## 3. SET_GROUP filter criteria @@ -692,7 +692,7 @@ end -- -- Planned filter criteria within development are (so these are not yet available): -- --- * @{#SET_GROUP.FilterZones}: Builds the SET_GROUP with the groups within a @{Zone#ZONE}. +-- * @{#SET_GROUP.FilterZones}: Builds the SET_GROUP with the groups within a @{Core.Zone#ZONE}. -- -- ## 4. SET_GROUP iterators -- @@ -1423,7 +1423,7 @@ do -- SET_UNIT --- @type SET_UNIT -- @extends Core.Set#SET_BASE - --- # 3) SET_UNIT class, extends @{Set#SET_BASE} + --- # 3) SET_UNIT class, extends @{Core.Set#SET_BASE} -- -- Mission designers can use the SET_UNIT class to build sets of units belonging to certain: -- @@ -1441,7 +1441,7 @@ do -- SET_UNIT -- -- ## 3.2) Add or Remove UNIT(s) from SET_UNIT -- - -- UNITs can be added and removed using the @{Set#SET_UNIT.AddUnitsByName} and @{Set#SET_UNIT.RemoveUnitsByName} respectively. + -- UNITs can be added and removed using the @{Core.Set#SET_UNIT.AddUnitsByName} and @{Core.Set#SET_UNIT.RemoveUnitsByName} respectively. -- These methods take a single UNIT name or an array of UNIT names to be added or removed from SET_UNIT. -- -- ## 3.3) SET_UNIT filter criteria @@ -1461,7 +1461,7 @@ do -- SET_UNIT -- -- Planned filter criteria within development are (so these are not yet available): -- - -- * @{#SET_UNIT.FilterZones}: Builds the SET_UNIT with the units within a @{Zone#ZONE}. + -- * @{#SET_UNIT.FilterZones}: Builds the SET_UNIT with the units within a @{Core.Zone#ZONE}. -- -- ## 3.4) SET_UNIT iterators -- @@ -1483,7 +1483,7 @@ do -- SET_UNIT -- -- Various methods exist for a SET_UNIT to perform actions or calculations and retrieve results from the SET_UNIT: -- - -- * @{#SET_UNIT.GetTypeNames}(): Retrieve the type names of the @{Unit}s in the SET, delimited by a comma. + -- * @{#SET_UNIT.GetTypeNames}(): Retrieve the type names of the @{Wrapper.Unit}s in the SET, delimited by a comma. -- -- ## 4. SET_UNIT iterators -- @@ -2392,10 +2392,10 @@ do -- SET_UNIT end - --- Retrieve the type names of the @{Unit}s in the SET, delimited by an optional delimiter. + --- Retrieve the type names of the @{Wrapper.Unit}s in the SET, delimited by an optional delimiter. -- @param #SET_UNIT self -- @param #string Delimiter (optional) The delimiter, which is default a comma. - -- @return #string The types of the @{Unit}s delimited. + -- @return #string The types of the @{Wrapper.Unit}s delimited. function SET_UNIT:GetTypeNames( Delimiter ) Delimiter = Delimiter or ", " @@ -2423,7 +2423,7 @@ do -- SET_STATIC --- @type SET_STATIC -- @extends Core.Set#SET_BASE - --- # 3) SET_STATIC class, extends @{Set#SET_BASE} + --- # 3) SET_STATIC class, extends @{Core.Set#SET_BASE} -- -- Mission designers can use the SET_STATIC class to build sets of Statics belonging to certain: -- @@ -2441,7 +2441,7 @@ do -- SET_STATIC -- -- ## 3.2) Add or Remove STATIC(s) from SET_STATIC -- - -- STATICs can be added and removed using the @{Set#SET_STATIC.AddStaticsByName} and @{Set#SET_STATIC.RemoveStaticsByName} respectively. + -- STATICs can be added and removed using the @{Core.Set#SET_STATIC.AddStaticsByName} and @{Core.Set#SET_STATIC.RemoveStaticsByName} respectively. -- These methods take a single STATIC name or an array of STATIC names to be added or removed from SET_STATIC. -- -- ## 3.3) SET_STATIC filter criteria @@ -2461,7 +2461,7 @@ do -- SET_STATIC -- -- Planned filter criteria within development are (so these are not yet available): -- - -- * @{#SET_STATIC.FilterZones}: Builds the SET_STATIC with the units within a @{Zone#ZONE}. + -- * @{#SET_STATIC.FilterZones}: Builds the SET_STATIC with the units within a @{Core.Zone#ZONE}. -- -- ## 3.4) SET_STATIC iterators -- @@ -3099,9 +3099,9 @@ end ---- # 4) SET_CLIENT class, extends @{Set#SET_BASE} +--- # 4) SET_CLIENT class, extends @{Core.Set#SET_BASE} -- --- Mission designers can use the @{Set#SET_CLIENT} class to build sets of units belonging to certain: +-- Mission designers can use the @{Core.Set#SET_CLIENT} class to build sets of units belonging to certain: -- -- * Coalitions -- * Categories @@ -3117,7 +3117,7 @@ end -- -- ## 4.2) Add or Remove CLIENT(s) from SET_CLIENT -- --- CLIENTs can be added and removed using the @{Set#SET_CLIENT.AddClientsByName} and @{Set#SET_CLIENT.RemoveClientsByName} respectively. +-- CLIENTs can be added and removed using the @{Core.Set#SET_CLIENT.AddClientsByName} and @{Core.Set#SET_CLIENT.RemoveClientsByName} respectively. -- These methods take a single CLIENT name or an array of CLIENT names to be added or removed from SET_CLIENT. -- -- ## 4.3) SET_CLIENT filter criteria @@ -3137,7 +3137,7 @@ end -- -- Planned filter criteria within development are (so these are not yet available): -- --- * @{#SET_CLIENT.FilterZones}: Builds the SET_CLIENT with the clients within a @{Zone#ZONE}. +-- * @{#SET_CLIENT.FilterZones}: Builds the SET_CLIENT with the clients within a @{Core.Zone#ZONE}. -- -- ## 4.4) SET_CLIENT iterators -- @@ -3512,9 +3512,9 @@ end ---- # 4) SET_PLAYER class, extends @{Set#SET_BASE} +--- # 4) SET_PLAYER class, extends @{Core.Set#SET_BASE} -- --- Mission designers can use the @{Set#SET_PLAYER} class to build sets of units belonging to alive players: +-- Mission designers can use the @{Core.Set#SET_PLAYER} class to build sets of units belonging to alive players: -- -- ## 4.1) SET_PLAYER constructor -- @@ -3539,7 +3539,7 @@ end -- -- 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}. +-- * @{#SET_PLAYER.FilterZones}: Builds the SET_PLAYER with the clients within a @{Core.Zone#ZONE}. -- -- ## 4.4) SET_PLAYER iterators -- @@ -3909,9 +3909,9 @@ end --- @type SET_AIRBASE -- @extends Core.Set#SET_BASE ---- # 5) SET_AIRBASE class, extends @{Set#SET_BASE} +--- # 5) SET_AIRBASE class, extends @{Core.Set#SET_BASE} -- --- Mission designers can use the @{Set#SET_AIRBASE} class to build sets of airbases optionally belonging to certain: +-- Mission designers can use the @{Core.Set#SET_AIRBASE} class to build sets of airbases optionally belonging to certain: -- -- * Coalitions -- @@ -3923,7 +3923,7 @@ end -- -- ## 5.2) Add or Remove AIRBASEs from SET_AIRBASE -- --- AIRBASEs can be added and removed using the @{Set#SET_AIRBASE.AddAirbasesByName} and @{Set#SET_AIRBASE.RemoveAirbasesByName} respectively. +-- AIRBASEs can be added and removed using the @{Core.Set#SET_AIRBASE.AddAirbasesByName} and @{Core.Set#SET_AIRBASE.RemoveAirbasesByName} respectively. -- These methods take a single AIRBASE name or an array of AIRBASE names to be added or removed from SET_AIRBASE. -- -- ## 5.3) SET_AIRBASE filter criteria @@ -4139,10 +4139,10 @@ function SET_AIRBASE:ForEachAirbase( IteratorFunction, ... ) return self end ---- Iterate the SET_AIRBASE while identifying the nearest @{Airbase#AIRBASE} from a @{Point#POINT_VEC2}. +--- Iterate the SET_AIRBASE while identifying the nearest @{Wrapper.Airbase#AIRBASE} from a @{Point#POINT_VEC2}. -- @param #SET_AIRBASE self --- @param Core.Point#POINT_VEC2 PointVec2 A @{Point#POINT_VEC2} object from where to evaluate the closest @{Airbase#AIRBASE}. --- @return Wrapper.Airbase#AIRBASE The closest @{Airbase#AIRBASE}. +-- @param Core.Point#POINT_VEC2 PointVec2 A @{Point#POINT_VEC2} object from where to evaluate the closest @{Wrapper.Airbase#AIRBASE}. +-- @return Wrapper.Airbase#AIRBASE The closest @{Wrapper.Airbase#AIRBASE}. function SET_AIRBASE:FindNearestAirbaseFromPointVec2( PointVec2 ) self:F2( PointVec2 ) @@ -4198,9 +4198,9 @@ end --- @type SET_CARGO -- @extends Core.Set#SET_BASE ---- # (R2.1) SET_CARGO class, extends @{Set#SET_BASE} +--- # (R2.1) SET_CARGO class, extends @{Core.Set#SET_BASE} -- --- Mission designers can use the @{Set#SET_CARGO} class to build sets of cargos optionally belonging to certain: +-- Mission designers can use the @{Core.Set#SET_CARGO} class to build sets of cargos optionally belonging to certain: -- -- * Coalitions -- * Types @@ -4214,7 +4214,7 @@ end -- -- ## Add or Remove CARGOs from SET_CARGO -- --- CARGOs can be added and removed using the @{Set#SET_CARGO.AddCargosByName} and @{Set#SET_CARGO.RemoveCargosByName} respectively. +-- CARGOs can be added and removed using the @{Core.Set#SET_CARGO.AddCargosByName} and @{Core.Set#SET_CARGO.RemoveCargosByName} respectively. -- These methods take a single CARGO name or an array of CARGO names to be added or removed from SET_CARGO. -- -- ## SET_CARGO filter criteria @@ -4632,9 +4632,9 @@ end --- @type SET_ZONE -- @extends Core.Set#SET_BASE ---- # SET_ZONE class, extends @{Set#SET_BASE} +--- # SET_ZONE class, extends @{Core.Set#SET_BASE} -- --- Mission designers can use the @{Set#SET_ZONE} class to build sets of zones of various types. +-- Mission designers can use the @{Core.Set#SET_ZONE} class to build sets of zones of various types. -- -- ## SET_ZONE constructor -- @@ -4644,7 +4644,7 @@ end -- -- ## Add or Remove ZONEs from SET_ZONE -- --- ZONEs can be added and removed using the @{Set#SET_ZONE.AddZonesByName} and @{Set#SET_ZONE.RemoveZonesByName} respectively. +-- ZONEs can be added and removed using the @{Core.Set#SET_ZONE.AddZonesByName} and @{Core.Set#SET_ZONE.RemoveZonesByName} respectively. -- These methods take a single ZONE name or an array of ZONE names to be added or removed from SET_ZONE. -- -- ## 5.3) SET_ZONE filter criteria diff --git a/Moose Development/Moose/Core/Settings.lua b/Moose Development/Moose/Core/Settings.lua index a8b9ba76d..86a607fc1 100644 --- a/Moose Development/Moose/Core/Settings.lua +++ b/Moose Development/Moose/Core/Settings.lua @@ -22,7 +22,7 @@ --- @type SETTINGS -- @extends Core.Base#BASE ---- # SETTINGS class, extends @{Base#BASE} +--- # SETTINGS class, extends @{Core.Base#BASE} -- The SETTINGS class takes care of various settings that influence the behaviour of certain functionalities and classes within the MOOSE framework. -- -- === diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index c260e6c9a..a42d6b7c5 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -37,7 +37,7 @@ -- @extends Core.Base#BASE ---- # SPAWN class, extends @{Base#BASE} +--- # SPAWN class, extends @{Core.Base#BASE} -- -- -- ![Banner Image](..\Presentations\SPAWN\SPAWN.JPG) -- @@ -67,8 +67,8 @@ -- **Limits** can be set on how many groups can be spawn in each SPAWN object, -- using the method @{#SPAWN.InitLimit}. SPAWN has 2 kind of limits: -- --- * The maximum amount of @{Unit}s that can be **alive** at the same time... --- * The maximum amount of @{Group}s that can be **spawned**... This is more of a **resource**-type of limit. +-- * The maximum amount of @{Wrapper.Unit}s that can be **alive** at the same time... +-- * The maximum amount of @{Wrapper.Group}s that can be **spawned**... This is more of a **resource**-type of limit. -- -- When new groups get spawned using the **Spawn** methods, -- it will be evaluated whether any limits have been reached. @@ -82,7 +82,7 @@ -- with unlimited resources = :InitLimit( 100, 0 ) and 10 groups are alive, but two groups have only one unit alive in the group, -- then a sequent Spawn(Scheduled) will allow a new group to be spawned!!! -- --- ### IMPORTANT!! If a limit has been reached, it is possible that a **Spawn** method returns **nil**, meaning, no @{Group} had been spawned!!! +-- ### IMPORTANT!! If a limit has been reached, it is possible that a **Spawn** method returns **nil**, meaning, no @{Wrapper.Group} had been spawned!!! -- -- Spawned groups get **the same name** as the name of the template group. -- Spawned units in those groups keep _by default_ **the same name** as the name of the template group. @@ -117,7 +117,7 @@ -- Create a new SPAWN object with the @{#SPAWN.New}() or the @{#SPAWN.NewWithAlias}() methods: -- -- * @{#SPAWN.New}(): Creates a new SPAWN object taking the name of the group that represents the GROUP template (definition). --- * @{#SPAWN.NewWithAlias}(): Creates a new SPAWN object taking the name of the group that represents the GROUP template (definition), and gives each spawned @{Group} an different name. +-- * @{#SPAWN.NewWithAlias}(): Creates a new SPAWN object taking the name of the group that represents the GROUP template (definition), and gives each spawned @{Wrapper.Group} an different name. -- -- It is important to understand how the SPAWN class works internally. The SPAWN object created will contain internally a list of groups that will be spawned and that are already spawned. -- The initialization methods will modify this list of groups so that when a group gets spawned, ALL information is already prepared when spawning. This is done for performance reasons. @@ -149,15 +149,15 @@ -- -- ### Position randomization -- --- * @{#SPAWN.InitRandomizePosition}(): Randomizes the position of @{Group}s that are spawned within a **radius band**, given an Outer and Inner radius, from the point that the spawn happens. --- * @{#SPAWN.InitRandomizeUnits}(): Randomizes the @{Unit}s in the @{Group} that is spawned within a **radius band**, given an Outer and Inner radius. +-- * @{#SPAWN.InitRandomizePosition}(): Randomizes the position of @{Wrapper.Group}s that are spawned within a **radius band**, given an Outer and Inner radius, from the point that the spawn happens. +-- * @{#SPAWN.InitRandomizeUnits}(): Randomizes the @{Wrapper.Unit}s in the @{Wrapper.Group} that is spawned within a **radius band**, given an Outer and Inner radius. -- * @{#SPAWN.InitRandomizeZones}(): Randomizes the spawning between a predefined list of @{Zone}s that are declared using this function. Each zone can be given a probability factor. -- --- ### Enable / Disable AI when spawning a new @{Group} +-- ### Enable / Disable AI when spawning a new @{Wrapper.Group} -- --- * @{#SPAWN.InitAIOn}(): Turns the AI On when spawning the new @{Group} object. --- * @{#SPAWN.InitAIOff}(): Turns the AI Off when spawning the new @{Group} object. --- * @{#SPAWN.InitAIOnOff}(): Turns the AI On or Off when spawning the new @{Group} object. +-- * @{#SPAWN.InitAIOn}(): Turns the AI On when spawning the new @{Wrapper.Group} object. +-- * @{#SPAWN.InitAIOff}(): Turns the AI Off when spawning the new @{Wrapper.Group} object. +-- * @{#SPAWN.InitAIOnOff}(): Turns the AI On or Off when spawning the new @{Wrapper.Group} object. -- -- ### Limit scheduled spawning -- @@ -165,11 +165,11 @@ -- -- ### Delay initial scheduled spawn -- --- * @{#SPAWN.InitDelayOnOff}(): Turns the inital delay On/Off when scheduled spawning the first @{Group} object. --- * @{#SPAWN.InitDelayOn}(): Turns the inital delay On when scheduled spawning the first @{Group} object. --- * @{#SPAWN.InitDelayOff}(): Turns the inital delay Off when scheduled spawning the first @{Group} object. +-- * @{#SPAWN.InitDelayOnOff}(): Turns the inital delay On/Off when scheduled spawning the first @{Wrapper.Group} object. +-- * @{#SPAWN.InitDelayOn}(): Turns the inital delay On when scheduled spawning the first @{Wrapper.Group} object. +-- * @{#SPAWN.InitDelayOff}(): Turns the inital delay Off when scheduled spawning the first @{Wrapper.Group} object. -- --- ### Repeat spawned @{Group}s upon landing +-- ### Repeat spawned @{Wrapper.Group}s upon landing -- -- * @{#SPAWN.InitRepeat}() or @{#SPAWN.InitRepeatOnLanding}(): This method is used to re-spawn automatically the same group after it has landed. -- * @{#SPAWN.InitRepeatOnEngineShutDown}(): This method is used to re-spawn automatically the same group after it has landed and it shuts down the engines at the ramp. @@ -186,9 +186,9 @@ -- * @{#SPAWN.SpawnFromVec3}(): Spawn a new group from a Vec3 coordinate. (The group will can be spawned at a point in the air). -- * @{#SPAWN.SpawnFromVec2}(): Spawn a new group from a Vec2 coordinate. (The group will be spawned at land height ). -- * @{#SPAWN.SpawnFromStatic}(): Spawn a new group from a structure, taking the position of a @{Static}. --- * @{#SPAWN.SpawnFromUnit}(): Spawn a new group taking the position of a @{Unit}. +-- * @{#SPAWN.SpawnFromUnit}(): Spawn a new group taking the position of a @{Wrapper.Unit}. -- * @{#SPAWN.SpawnInZone}(): Spawn a new group in a @{Zone}. --- * @{#SPAWN.SpawnAtAirbase}(): Spawn a new group at an @{Airbase}, which can be an airdrome, ship or helipad. +-- * @{#SPAWN.SpawnAtAirbase}(): Spawn a new group at an @{Wrapper.Airbase}, which can be an airdrome, ship or helipad. -- -- Note that @{#SPAWN.Spawn} and @{#SPAWN.ReSpawn} return a @{GROUP#GROUP.New} object, that contains a reference to the DCSGroup object. -- You can use the @{GROUP} object to do further actions with the DCSGroup. @@ -226,21 +226,21 @@ -- This models AI that has succesfully returned to their airbase, to restart their combat activities. -- Check the @{#SPAWN.InitCleanUp}() for further info. -- --- ## Catch the @{Group} Spawn Event in a callback function! +-- ## Catch the @{Wrapper.Group} Spawn Event in a callback function! -- --- When using the @{#SPAWN.SpawnScheduled)() method, new @{Group}s are created following the spawn time interval parameters. --- When a new @{Group} is spawned, you maybe want to execute actions with that group spawned at the spawn event. +-- When using the @{#SPAWN.SpawnScheduled)() method, new @{Wrapper.Group}s are created following the spawn time interval parameters. +-- When a new @{Wrapper.Group} is spawned, you maybe want to execute actions with that group spawned at the spawn event. -- The SPAWN class supports this functionality through the method @{#SPAWN.OnSpawnGroup}( **function( SpawnedGroup ) end ** ), -- which takes a function as a parameter that you can define locally. --- Whenever a new @{Group} is spawned, the given function is called, and the @{Group} that was just spawned, is given as a parameter. --- As a result, your spawn event handling function requires one parameter to be declared, which will contain the spawned @{Group} object. +-- Whenever a new @{Wrapper.Group} is spawned, the given function is called, and the @{Wrapper.Group} that was just spawned, is given as a parameter. +-- As a result, your spawn event handling function requires one parameter to be declared, which will contain the spawned @{Wrapper.Group} object. -- A coding example is provided at the description of the @{#SPAWN.OnSpawnGroup}( **function( SpawnedGroup ) end ** ) method. -- -- ## Delay the initial spawning -- --- When using the @{#SPAWN.SpawnScheduled)() method, the default behaviour of this method will be that it will spawn the initial (first) @{Group} +-- When using the @{#SPAWN.SpawnScheduled)() method, the default behaviour of this method will be that it will spawn the initial (first) @{Wrapper.Group} -- immediately when :SpawnScheduled() is initiated. The methods @{#SPAWN.InitDelayOnOff}() and @{#SPAWN.InitDelayOn}() can be used to --- activate a delay before the first @{Group} is spawned. For completeness, a method @{#SPAWN.InitDelayOff}() is also available, that +-- activate a delay before the first @{Wrapper.Group} is spawned. For completeness, a method @{#SPAWN.InitDelayOff}() is also available, that -- can be used to switch off the initial delay. Because there is no delay by default, this method would only be used when a -- @{#SPAWN.SpawnScheduledStop}() ; @{#SPAWN.SpawnScheduledStart}() sequence would have been used. -- @@ -270,7 +270,7 @@ SPAWN.Takeoff = { -- @list SpawnZone ---- Creates the main object to spawn a @{Group} defined in the DCS ME. +--- Creates the main object to spawn a @{Wrapper.Group} defined in the DCS ME. -- @param #SPAWN self -- @param #string SpawnTemplatePrefix is the name of the Group in the ME that defines the Template. Each new group will have the name starting with SpawnTemplatePrefix. -- @return #SPAWN @@ -551,9 +551,9 @@ function SPAWN:InitRandomizeRoute( SpawnStartPoint, SpawnEndPoint, SpawnRadius, return self end ---- Randomizes the position of @{Group}s that are spawned within a **radius band**, given an Outer and Inner radius, from the point that the spawn happens. +--- Randomizes the position of @{Wrapper.Group}s that are spawned within a **radius band**, given an Outer and Inner radius, from the point that the spawn happens. -- @param #SPAWN self --- @param #boolean RandomizePosition If true, SPAWN will perform the randomization of the @{Group}s position between a given outer and inner radius. +-- @param #boolean RandomizePosition If true, SPAWN will perform the randomization of the @{Wrapper.Group}s position between a given outer and inner radius. -- @param Dcs.DCSTypes#Distance OuterRadius (optional) The outer radius in meters where the new group will be spawned. -- @param Dcs.DCSTypes#Distance InnerRadius (optional) The inner radius in meters where the new group will NOT be spawned. -- @return #SPAWN @@ -904,7 +904,7 @@ function SPAWN:InitArray( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ) end do -- AI methods - --- Turns the AI On or Off for the @{Group} when spawning. + --- Turns the AI On or Off for the @{Wrapper.Group} when spawning. -- @param #SPAWN self -- @param #boolean AIOnOff A value of true sets the AI On, a value of false sets the AI Off. -- @return #SPAWN The SPAWN object @@ -914,7 +914,7 @@ do -- AI methods return self end - --- Turns the AI On for the @{Group} when spawning. + --- Turns the AI On for the @{Wrapper.Group} when spawning. -- @param #SPAWN self -- @return #SPAWN The SPAWN object function SPAWN:InitAIOn() @@ -922,7 +922,7 @@ do -- AI methods return self:InitAIOnOff( true ) end - --- Turns the AI Off for the @{Group} when spawning. + --- Turns the AI Off for the @{Wrapper.Group} when spawning. -- @param #SPAWN self -- @return #SPAWN The SPAWN object function SPAWN:InitAIOff() @@ -933,8 +933,8 @@ do -- AI methods end -- AI methods do -- Delay methods - --- Turns the Delay On or Off for the first @{Group} scheduled spawning. - -- The default value is that for scheduled spawning, there is an initial delay when spawning the first @{Group}. + --- Turns the Delay On or Off for the first @{Wrapper.Group} scheduled spawning. + -- The default value is that for scheduled spawning, there is an initial delay when spawning the first @{Wrapper.Group}. -- @param #SPAWN self -- @param #boolean DelayOnOff A value of true sets the Delay On, a value of false sets the Delay Off. -- @return #SPAWN The SPAWN object @@ -944,7 +944,7 @@ do -- Delay methods return self end - --- Turns the Delay On for the @{Group} when spawning. + --- Turns the Delay On for the @{Wrapper.Group} when spawning. -- @param #SPAWN self -- @return #SPAWN The SPAWN object function SPAWN:InitDelayOn() @@ -952,7 +952,7 @@ do -- Delay methods return self:InitDelayOnOff( true ) end - --- Turns the Delay Off for the @{Group} when spawning. + --- Turns the Delay Off for the @{Wrapper.Group} when spawning. -- @param #SPAWN self -- @return #SPAWN The SPAWN object function SPAWN:InitDelayOff() @@ -1172,7 +1172,7 @@ end --- Allows to place a CallFunction hook when a new group spawns. -- The provided method will be called when a new group is spawned, including its given parameters. --- The first parameter of the SpawnFunction is the @{Group#GROUP} that was spawned. +-- The first parameter of the SpawnFunction is the @{Wrapper.Group#GROUP} that was spawned. -- @param #SPAWN self -- @param #function SpawnCallBackFunction The function to be called when a group spawns. -- @param SpawnFunctionArguments A random amount of arguments to be provided to the function when the group spawns. @@ -1200,28 +1200,28 @@ function SPAWN:OnSpawnGroup( SpawnCallBackFunction, ... ) return self end ---- Will spawn a group at an @{Airbase}. +--- Will spawn a group at an @{Wrapper.Airbase}. -- This method is mostly advisable to be used if you want to simulate spawning units at an airbase. -- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. -- You can use the returned group to further define the route to be followed. -- --- The @{Airbase#AIRBASE} object must refer to a valid airbase known in the sim. +-- The @{Wrapper.Airbase#AIRBASE} object must refer to a valid airbase known in the sim. -- You can use the following enumerations to search for the pre-defined airbases on the current known maps of DCS: -- --- * @{Airbase#AIRBASE.Caucasus}: The airbases on the Caucasus map. --- * @{Airbase#AIRBASE.Nevada}: The airbases on the Nevada (NTTR) map. --- * @{Airbase#AIRBASE.Normandy}: The airbases on the Normandy map. +-- * @{Wrapper.Airbase#AIRBASE.Caucasus}: The airbases on the Caucasus map. +-- * @{Wrapper.Airbase#AIRBASE.Nevada}: The airbases on the Nevada (NTTR) map. +-- * @{Wrapper.Airbase#AIRBASE.Normandy}: The airbases on the Normandy map. -- --- Use the method @{Airbase#AIRBASE.FindByName}() to retrieve the airbase object. +-- Use the method @{Wrapper.Airbase#AIRBASE.FindByName}() to retrieve the airbase object. -- The known AIRBASE objects are automatically imported at mission start by MOOSE. -- Therefore, there isn't any New() constructor defined for AIRBASE objects. -- -- Ships and Farps are added within the mission, and are therefore not known. --- For these AIRBASE objects, there isn't an @{Airbase#AIRBASE} enumeration defined. --- You need to provide the **exact name** of the airbase as the parameter to the @{Airbase#AIRBASE.FindByName}() method! +-- For these AIRBASE objects, there isn't an @{Wrapper.Airbase#AIRBASE} enumeration defined. +-- You need to provide the **exact name** of the airbase as the parameter to the @{Wrapper.Airbase#AIRBASE.FindByName}() method! -- -- @param #SPAWN self --- @param Wrapper.Airbase#AIRBASE SpawnAirbase The @{Airbase} where to spawn the group. +-- @param Wrapper.Airbase#AIRBASE SpawnAirbase The @{Wrapper.Airbase} where to spawn the group. -- @param #SPAWN.Takeoff Takeoff (optional) The location and takeoff method. Default is Hot. -- @param #number TakeoffAltitude (optional) The altitude above the ground. -- @return Wrapper.Group#GROUP that was spawned. @@ -1579,12 +1579,12 @@ function SPAWN:SpawnFromStatic( HostStatic, MinHeight, MaxHeight, SpawnIndex ) end --- Will spawn a Group within a given @{Zone}. --- The @{Zone} can be of any type derived from @{Zone#ZONE_BASE}. --- Once the @{Group} is spawned within the zone, the @{Group} will continue on its route. +-- The @{Zone} can be of any type derived from @{Core.Zone#ZONE_BASE}. +-- Once the @{Wrapper.Group} is spawned within the zone, the @{Wrapper.Group} will continue on its route. -- The **first waypoint** (where the group is spawned) is replaced with the zone location coordinates. -- @param #SPAWN self -- @param Core.Zone#ZONE Zone The zone where the group is to be spawned. --- @param #boolean RandomizeGroup (optional) Randomization of the @{Group} position in the zone. +-- @param #boolean RandomizeGroup (optional) Randomization of the @{Wrapper.Group} position in the zone. -- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone. -- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone. -- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. @@ -1680,12 +1680,12 @@ function SPAWN:SpawnGroupName( SpawnIndex ) end ---- Will find the first alive @{Group} it has spawned, and return the alive @{Group} object and the first Index where the first alive @{Group} object has been found. +--- Will find the first alive @{Wrapper.Group} it has spawned, and return the alive @{Wrapper.Group} object and the first Index where the first alive @{Wrapper.Group} object has been found. -- @param #SPAWN self --- @return Wrapper.Group#GROUP, #number The @{Group} object found, the new Index where the group was found. +-- @return Wrapper.Group#GROUP, #number The @{Wrapper.Group} object found, the new Index where the group was found. -- @return #nil, #nil When no group is found, #nil is returned. -- @usage --- -- Find the first alive @{Group} object of the SpawnPlanes SPAWN object @{Group} collection that it has spawned during the mission. +-- -- Find the first alive @{Wrapper.Group} object of the SpawnPlanes SPAWN object @{Wrapper.Group} collection that it has spawned during the mission. -- local GroupPlane, Index = SpawnPlanes:GetFirstAliveGroup() -- while GroupPlane ~= nil do -- -- Do actions with the GroupPlane object. @@ -1705,13 +1705,13 @@ function SPAWN:GetFirstAliveGroup() end ---- Will find the next alive @{Group} object from a given Index, and return a reference to the alive @{Group} object and the next Index where the alive @{Group} has been found. +--- Will find the next alive @{Wrapper.Group} object from a given Index, and return a reference to the alive @{Wrapper.Group} object and the next Index where the alive @{Wrapper.Group} has been found. -- @param #SPAWN self --- @param #number SpawnIndexStart A Index holding the start position to search from. This method can also be used to find the first alive @{Group} object from the given Index. --- @return Wrapper.Group#GROUP, #number The next alive @{Group} object found, the next Index where the next alive @{Group} object was found. --- @return #nil, #nil When no alive @{Group} object is found from the start Index position, #nil is returned. +-- @param #number SpawnIndexStart A Index holding the start position to search from. This method can also be used to find the first alive @{Wrapper.Group} object from the given Index. +-- @return Wrapper.Group#GROUP, #number The next alive @{Wrapper.Group} object found, the next Index where the next alive @{Wrapper.Group} object was found. +-- @return #nil, #nil When no alive @{Wrapper.Group} object is found from the start Index position, #nil is returned. -- @usage --- -- Find the first alive @{Group} object of the SpawnPlanes SPAWN object @{Group} collection that it has spawned during the mission. +-- -- Find the first alive @{Wrapper.Group} object of the SpawnPlanes SPAWN object @{Wrapper.Group} collection that it has spawned during the mission. -- local GroupPlane, Index = SpawnPlanes:GetFirstAliveGroup() -- while GroupPlane ~= nil do -- -- Do actions with the GroupPlane object. @@ -1731,12 +1731,12 @@ function SPAWN:GetNextAliveGroup( SpawnIndexStart ) return nil, nil end ---- Will find the last alive @{Group} object, and will return a reference to the last live @{Group} object and the last Index where the last alive @{Group} object has been found. +--- Will find the last alive @{Wrapper.Group} object, and will return a reference to the last live @{Wrapper.Group} object and the last Index where the last alive @{Wrapper.Group} object has been found. -- @param #SPAWN self --- @return Wrapper.Group#GROUP, #number The last alive @{Group} object found, the last Index where the last alive @{Group} object was found. --- @return #nil, #nil When no alive @{Group} object is found, #nil is returned. +-- @return Wrapper.Group#GROUP, #number The last alive @{Wrapper.Group} object found, the last Index where the last alive @{Wrapper.Group} object was found. +-- @return #nil, #nil When no alive @{Wrapper.Group} object is found, #nil is returned. -- @usage --- -- Find the last alive @{Group} object of the SpawnPlanes SPAWN object @{Group} collection that it has spawned during the mission. +-- -- Find the last alive @{Wrapper.Group} object of the SpawnPlanes SPAWN object @{Wrapper.Group} collection that it has spawned during the mission. -- local GroupPlane, Index = SpawnPlanes:GetLastAliveGroup() -- if GroupPlane then -- GroupPlane can be nil!!! -- -- Do actions with the GroupPlane object. diff --git a/Moose Development/Moose/Core/SpawnStatic.lua b/Moose Development/Moose/Core/SpawnStatic.lua index 22ae07cfc..f1715465a 100644 --- a/Moose Development/Moose/Core/SpawnStatic.lua +++ b/Moose Development/Moose/Core/SpawnStatic.lua @@ -37,7 +37,7 @@ -- @extends Core.Base#BASE ---- # SPAWNSTATIC class, extends @{Base#BASE} +--- # SPAWNSTATIC class, extends @{Core.Base#BASE} -- -- The SPAWNSTATIC class allows to spawn dynamically new @{Static}s. -- Through creating a copy of an existing static object template as defined in the Mission Editor (ME), diff --git a/Moose Development/Moose/Core/Spot.lua b/Moose Development/Moose/Core/Spot.lua index 19ca73263..22ba7eee8 100644 --- a/Moose Development/Moose/Core/Spot.lua +++ b/Moose Development/Moose/Core/Spot.lua @@ -8,7 +8,7 @@ -- -- * Spot for a defined duration. -- * wiggle the spot at the target. --- * Provide a @{Unit} as a target, instead of a point. +-- * Provide a @{Wrapper.Unit} as a target, instead of a point. -- * Implement a status machine, LaseOn, LaseOff. -- -- === @@ -53,7 +53,7 @@ do -- -- * Mark targets for a defined duration. -- * wiggle the spot at the target. - -- * Provide a @{Unit} as a target, instead of a point. + -- * Provide a @{Wrapper.Unit} as a target, instead of a point. -- * Implement a status machine, LaseOn, LaseOff. -- -- ## 1. SPOT constructor diff --git a/Moose Development/Moose/Core/UserFlag.lua b/Moose Development/Moose/Core/UserFlag.lua index f1b09589c..8cfff730f 100644 --- a/Moose Development/Moose/Core/UserFlag.lua +++ b/Moose Development/Moose/Core/UserFlag.lua @@ -18,7 +18,7 @@ do -- UserFlag -- @extends Core.Base#BASE - --- # USERFLAG class, extends @{Base#BASE} + --- # USERFLAG class, extends @{Core.Base#BASE} -- -- Management of DCS User Flags. -- diff --git a/Moose Development/Moose/Core/UserSound.lua b/Moose Development/Moose/Core/UserSound.lua index dcd6c58df..2a7614384 100644 --- a/Moose Development/Moose/Core/UserSound.lua +++ b/Moose Development/Moose/Core/UserSound.lua @@ -18,7 +18,7 @@ do -- UserSound -- @extends Core.Base#BASE - --- # USERSOUND class, extends @{Base#BASE} + --- # USERSOUND class, extends @{Core.Base#BASE} -- -- Management of DCS User Sound. -- @@ -110,9 +110,9 @@ do -- UserSound end - --- Play the usersound to the given @{Group}. + --- Play the usersound to the given @{Wrapper.Group}. -- @param #USERSOUND self - -- @param Wrapper.Group#GROUP Group The @{Group} to play the usersound to. + -- @param Wrapper.Group#GROUP Group The @{Wrapper.Group} to play the usersound to. -- @return #USERSOUND The usersound instance. -- @usage -- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" ) diff --git a/Moose Development/Moose/Core/Velocity.lua b/Moose Development/Moose/Core/Velocity.lua index ecc4b5ea6..0888aa210 100644 --- a/Moose Development/Moose/Core/Velocity.lua +++ b/Moose Development/Moose/Core/Velocity.lua @@ -15,7 +15,7 @@ do -- Velocity -- @extends Core.Base#BASE - --- # VELOCITY class, extends @{Base#BASE} + --- # VELOCITY class, extends @{Core.Base#BASE} -- -- VELOCITY models a speed, which can be expressed in various formats according the Settings. -- @@ -125,7 +125,7 @@ do -- VELOCITY_POSITIONABLE -- @extends Core.Base#BASE - --- # VELOCITY_POSITIONABLE class, extends @{Base#BASE} + --- # VELOCITY_POSITIONABLE class, extends @{Core.Base#BASE} -- -- VELOCITY_POSITIONABLE monitors the speed of an @{Positionable} in the simulation, which can be expressed in various formats according the Settings. -- diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index a8dc2f641..a6201d28a 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -12,7 +12,7 @@ -- The object classes are using the zone classes to test the zone boundaries, which can take various forms: -- -- * Test if completely within the zone. --- * Test if partly within the zone (for @{Group#GROUP} objects). +-- * Test if partly within the zone (for @{Wrapper.Group#GROUP} objects). -- * Test if not in the zone. -- * Distance to the nearest intersecting point of the zone. -- * Distance to the center of the zone. @@ -24,8 +24,8 @@ -- * @{#ZONE_RADIUS}: The ZONE_RADIUS class defined by a zone name, a location and a radius. -- * @{#ZONE}: The ZONE class, defined by the zone name as defined within the Mission Editor. -- * @{#ZONE_UNIT}: The ZONE_UNIT class defines by a zone around a @{Unit#UNIT} with a radius. --- * @{#ZONE_GROUP}: The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. --- * @{#ZONE_POLYGON}: The ZONE_POLYGON class defines by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. +-- * @{#ZONE_GROUP}: The ZONE_GROUP class defines by a zone around a @{Wrapper.Group#GROUP} with a radius. +-- * @{#ZONE_POLYGON}: The ZONE_POLYGON class defines by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon. -- -- === -- @@ -43,7 +43,7 @@ -- @extends Core.Base#BASE ---- # ZONE_BASE class, extends @{Base#BASE} +--- # ZONE_BASE class, extends @{Core.Base#BASE} -- -- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. -- @@ -53,7 +53,7 @@ -- * @{#ZONE_BASE.SetName}(): Sets the name of the zone. -- -- --- ## Each zone implements two polymorphic functions defined in @{Zone#ZONE_BASE}: +-- ## Each zone implements two polymorphic functions defined in @{Core.Zone#ZONE_BASE}: -- -- * @{#ZONE_BASE.IsVec2InZone}(): Returns if a 2D vector is within the zone. -- * @{#ZONE_BASE.IsVec3InZone}(): Returns if a 3D vector is within the zone. @@ -380,7 +380,7 @@ end -- @field Dcs.DCSTypes#Distance Radius The radius of the zone. -- @extends #ZONE_BASE ---- # ZONE_RADIUS class, extends @{Zone#ZONE_BASE} +--- # ZONE_RADIUS class, extends @{Core.Zone#ZONE_BASE} -- -- The ZONE_RADIUS class defined by a zone name, a location and a radius. -- This class implements the inherited functions from Core.Zone#ZONE_BASE taking into account the own zone format and properties. @@ -951,7 +951,7 @@ end -- @extends #ZONE_RADIUS ---- # ZONE class, extends @{Zone#ZONE_RADIUS} +--- # ZONE class, extends @{Core.Zone#ZONE_RADIUS} -- -- The ZONE class, defined by the zone name as defined within the Mission Editor. -- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. @@ -965,7 +965,7 @@ end -- You can declare a ZONE using the DCS mission editor by adding a trigger zone in the mission editor. -- -- Then during mission startup, when loading Moose.lua, this trigger zone will be detected as a ZONE declaration. --- Within the background, a ZONE object will be created within the @{Database}. +-- Within the background, a ZONE object will be created within the @{Core.Database}. -- The ZONE name will be the trigger zone name. -- -- So, you can search yourself for the ZONE object by using the @{#ZONE.FindByName}() method. @@ -1023,7 +1023,7 @@ end -- @field Wrapper.Unit#UNIT ZoneUNIT -- @extends Core.Zone#ZONE_RADIUS ---- # ZONE_UNIT class, extends @{Zone#ZONE_RADIUS} +--- # ZONE_UNIT class, extends @{Core.Zone#ZONE_RADIUS} -- -- The ZONE_UNIT class defined by a zone around a @{Unit#UNIT} with a radius. -- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. @@ -1116,20 +1116,20 @@ end -- @extends #ZONE_RADIUS ---- # ZONE_GROUP class, extends @{Zone#ZONE_RADIUS} +--- # ZONE_GROUP class, extends @{Core.Zone#ZONE_RADIUS} -- --- The ZONE_GROUP class defines by a zone around a @{Group#GROUP} with a radius. The current leader of the group defines the center of the zone. --- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. +-- The ZONE_GROUP class defines by a zone around a @{Wrapper.Group#GROUP} with a radius. The current leader of the group defines the center of the zone. +-- This class implements the inherited functions from @{Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties. -- -- @field #ZONE_GROUP ZONE_GROUP = { ClassName="ZONE_GROUP", } ---- Constructor to create a ZONE_GROUP instance, taking the zone name, a zone @{Group#GROUP} and a radius. +--- Constructor to create a ZONE_GROUP instance, taking the zone name, a zone @{Wrapper.Group#GROUP} and a radius. -- @param #ZONE_GROUP self -- @param #string ZoneName Name of the zone. --- @param Wrapper.Group#GROUP ZoneGROUP The @{Group} as the center of the zone. +-- @param Wrapper.Group#GROUP ZoneGROUP The @{Wrapper.Group} as the center of the zone. -- @param Dcs.DCSTypes#Distance Radius The radius of the zone. -- @return #ZONE_GROUP self function ZONE_GROUP:New( ZoneName, ZoneGROUP, Radius ) @@ -1145,9 +1145,9 @@ function ZONE_GROUP:New( ZoneName, ZoneGROUP, Radius ) end ---- Returns the current location of the @{Group}. +--- Returns the current location of the @{Wrapper.Group}. -- @param #ZONE_GROUP self --- @return Dcs.DCSTypes#Vec2 The location of the zone based on the @{Group} location. +-- @return Dcs.DCSTypes#Vec2 The location of the zone based on the @{Wrapper.Group} location. function ZONE_GROUP:GetVec2() self:F( self.ZoneName ) @@ -1158,9 +1158,9 @@ function ZONE_GROUP:GetVec2() return ZoneVec2 end ---- Returns a random location within the zone of the @{Group}. +--- Returns a random location within the zone of the @{Wrapper.Group}. -- @param #ZONE_GROUP self --- @return Dcs.DCSTypes#Vec2 The random location of the zone based on the @{Group} location. +-- @return Dcs.DCSTypes#Vec2 The random location of the zone based on the @{Wrapper.Group} location. function ZONE_GROUP:GetRandomVec2() self:F( self.ZoneName ) @@ -1197,10 +1197,10 @@ end -- @extends #ZONE_BASE ---- # ZONE_POLYGON_BASE class, extends @{Zone#ZONE_BASE} +--- # ZONE_POLYGON_BASE class, extends @{Core.Zone#ZONE_BASE} -- --- The ZONE_POLYGON_BASE class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. --- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. +-- The ZONE_POLYGON_BASE class defined by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon. +-- This class implements the inherited functions from @{Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties. -- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. -- -- ## Zone point randomization @@ -1221,7 +1221,7 @@ ZONE_POLYGON_BASE = { -- @list --- Constructor to create a ZONE_POLYGON_BASE instance, taking the zone name and an array of @{DCSTypes#Vec2}, forming a polygon. --- The @{Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected. +-- The @{Wrapper.Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected. -- @param #ZONE_POLYGON_BASE self -- @param #string ZoneName Name of the zone. -- @param #ZONE_POLYGON_BASE.ListVec2 PointsArray An array of @{DCSTypes#Vec2}, forming a polygon.. @@ -1245,7 +1245,7 @@ end --- Returns the center location of the polygon. -- @param #ZONE_GROUP self --- @return Dcs.DCSTypes#Vec2 The location of the zone based on the @{Group} location. +-- @return Dcs.DCSTypes#Vec2 The location of the zone based on the @{Wrapper.Group} location. function ZONE_POLYGON_BASE:GetVec2() self:F( self.ZoneName ) @@ -1474,10 +1474,10 @@ end -- @extends #ZONE_POLYGON_BASE ---- # ZONE_POLYGON class, extends @{Zone#ZONE_POLYGON_BASE} +--- # ZONE_POLYGON class, extends @{Core.Zone#ZONE_POLYGON_BASE} -- --- The ZONE_POLYGON class defined by a sequence of @{Group#GROUP} waypoints within the Mission Editor, forming a polygon. --- This class implements the inherited functions from @{Zone#ZONE_RADIUS} taking into account the own zone format and properties. +-- The ZONE_POLYGON class defined by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon. +-- This class implements the inherited functions from @{Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties. -- -- ## Declare a ZONE_POLYGON directly in the DCS mission editor! -- @@ -1485,7 +1485,7 @@ end -- -- So, imagine you have a group declared in the mission editor, with group name `DefenseZone~ZONE_POLYGON`. -- Then during mission startup, when loading Moose.lua, this group will be detected as a ZONE_POLYGON declaration. --- Within the background, a ZONE_POLYGON object will be created within the @{Database} using the properties of the group. +-- Within the background, a ZONE_POLYGON object will be created within the @{Core.Database} using the properties of the group. -- The ZONE_POLYGON name will be the group name without the ~ZONE_POLYGON tag. -- -- So, you can search yourself for the ZONE_POLYGON by using the @{#ZONE_POLYGON.FindByName}() method. @@ -1504,8 +1504,8 @@ ZONE_POLYGON = { ClassName="ZONE_POLYGON", } ---- Constructor to create a ZONE_POLYGON instance, taking the zone name and the @{Group#GROUP} defined within the Mission Editor. --- The @{Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected by ZONE_POLYGON. +--- Constructor to create a ZONE_POLYGON instance, taking the zone name and the @{Wrapper.Group#GROUP} defined within the Mission Editor. +-- The @{Wrapper.Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected by ZONE_POLYGON. -- @param #ZONE_POLYGON self -- @param #string ZoneName Name of the zone. -- @param Wrapper.Group#GROUP ZoneGroup The GROUP waypoints as defined within the Mission Editor define the polygon shape. @@ -1521,8 +1521,8 @@ function ZONE_POLYGON:New( ZoneName, ZoneGroup ) end ---- Constructor to create a ZONE_POLYGON instance, taking the zone name and the **name** of the @{Group#GROUP} defined within the Mission Editor. --- The @{Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected by ZONE_POLYGON. +--- Constructor to create a ZONE_POLYGON instance, taking the zone name and the **name** of the @{Wrapper.Group#GROUP} defined within the Mission Editor. +-- The @{Wrapper.Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected by ZONE_POLYGON. -- @param #ZONE_POLYGON self -- @param #string ZoneName Name of the zone. -- @param #string GroupName The group name of the GROUP defining the waypoints within the Mission Editor to define the polygon shape. diff --git a/Moose Development/Moose/Functional/ATC_Ground.lua b/Moose Development/Moose/Functional/ATC_Ground.lua index b27fa27f4..5709e7686 100644 --- a/Moose Development/Moose/Functional/ATC_Ground.lua +++ b/Moose Development/Moose/Functional/ATC_Ground.lua @@ -417,7 +417,7 @@ end -- # Airbases monitored -- -- The following airbases are monitored at the Caucasus region. --- Use the @{Airbase#AIRBASE.Caucasus} enumeration to select the airbases to be monitored. +-- Use the @{Wrapper.Airbase#AIRBASE.Caucasus} enumeration to select the airbases to be monitored. -- -- * `AIRBASE.Caucasus.Anapa_Vityazevo` -- * `AIRBASE.Caucasus.Batumi` @@ -1021,7 +1021,7 @@ end -- # Airbases monitored -- -- The following airbases are monitored at the Nevada region. --- Use the @{Airbase#AIRBASE.Nevada} enumeration to select the airbases to be monitored. +-- Use the @{Wrapper.Airbase#AIRBASE.Nevada} enumeration to select the airbases to be monitored. -- -- * `AIRBASE.Nevada.Beatty_Airport` -- * `AIRBASE.Nevada.Boulder_City_Airport` @@ -1561,7 +1561,7 @@ end -- # Airbases monitored -- -- The following airbases are monitored at the Normandy region. --- Use the @{Airbase#AIRBASE.Normandy} enumeration to select the airbases to be monitored. +-- Use the @{Wrapper.Airbase#AIRBASE.Normandy} enumeration to select the airbases to be monitored. -- -- * `AIRBASE.Normandy.Azeville` -- * `AIRBASE.Normandy.Bazenville` diff --git a/Moose Development/Moose/Functional/CleanUp.lua b/Moose Development/Moose/Functional/CleanUp.lua index 016bb10ef..84ea9c413 100644 --- a/Moose Development/Moose/Functional/CleanUp.lua +++ b/Moose Development/Moose/Functional/CleanUp.lua @@ -16,7 +16,7 @@ --- @type CLEANUP_AIRBASE -- @extends #CLEANUP_AIRBASE.__ ---- # CLEANUP_AIRBASE, extends @{Base#BASE} +--- # CLEANUP_AIRBASE, extends @{Core.Base#BASE} -- -- ![Banner Image](..\Presentations\CLEANUP_AIRBASE\Dia1.JPG) -- @@ -173,7 +173,7 @@ end ---- Destroys a @{Unit} from the simulator, but checks first if it is still existing! +--- Destroys a @{Wrapper.Unit} from the simulator, but checks first if it is still existing! -- @param #CLEANUP_AIRBASE self -- @param Wrapper.Unit#UNIT CleanUpUnit The object to be destroyed. function CLEANUP_AIRBASE.__:DestroyUnit( CleanUpUnit ) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 02122a437..2c5d61a8a 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -49,7 +49,7 @@ do -- DETECTION_BASE --- DETECTION_BASE class, extends @{Fsm#FSM} -- -- The DETECTION_BASE class defines the core functions to administer detected objects. - -- The DETECTION_BASE class will detect objects within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s). + -- The DETECTION_BASE class will detect objects within the battle zone for a list of @{Wrapper.Group}s detecting targets following (a) detection method(s). -- -- ## DETECTION_BASE constructor -- @@ -1255,7 +1255,7 @@ do -- DETECTION_BASE --- @param Dcs.DCSWrapper.Unit#Unit FoundDCSUnit -- @param Wrapper.Group#GROUP ReportGroup - -- @param Set#SET_GROUP ReportSetGroup + -- @param Core.Set#SET_GROUP ReportSetGroup local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) local DetectedItem = ReportGroupData.DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem @@ -1595,7 +1595,7 @@ do -- DETECTION_BASE return "" end - --- Get the @{Set#SET_UNIT} of a detecttion area using a given numeric index. + --- Get the @{Core.Set#SET_UNIT} of a detecttion area using a given numeric index. -- @param #DETECTION_BASE self -- @param #DETECTION_BASE.DetectedItem DetectedItem -- @return Core.Set#SET_UNIT DetectedSet @@ -1644,7 +1644,7 @@ do -- DETECTION_BASE do -- Zones - --- Get the @{Zone#ZONE_UNIT} of a detection area using a given numeric index. + --- Get the @{Core.Zone#ZONE_UNIT} of a detection area using a given numeric index. -- @param #DETECTION_BASE self -- @param #DETECTION_BASE.DetectedItem DetectedItem The DetectedItem. -- @return Core.Zone#ZONE_UNIT DetectedZone @@ -1810,7 +1810,7 @@ do -- DETECTION_UNITS --- # DETECTION_UNITS class, extends @{Detection#DETECTION_BASE} -- -- The DETECTION_UNITS class will detect units within the battle zone. - -- It will build a DetectedItems list filled with DetectedItems. Each DetectedItem will contain a field Set, which contains a @{Set#SET_UNIT} containing ONE @{UNIT} object reference. + -- It will build a DetectedItems list filled with DetectedItems. Each DetectedItem will contain a field Set, which contains a @{Core.Set#SET_UNIT} containing ONE @{UNIT} object reference. -- Beware that when the amount of units detected is large, the DetectedItems list will be large also. -- -- @type DETECTION_UNITS @@ -2061,7 +2061,7 @@ do -- DETECTION_TYPES -- -- The DETECTION_TYPES class will detect units within the battle zone. -- It will build a DetectedItems[] list filled with DetectedItems, grouped by the type of units detected. - -- Each DetectedItem will contain a field Set, which contains a @{Set#SET_UNIT} containing ONE @{UNIT} object reference. + -- Each DetectedItem will contain a field Set, which contains a @{Core.Set#SET_UNIT} containing ONE @{UNIT} object reference. -- Beware that when the amount of different types detected is large, the DetectedItems[] list will be large also. -- -- @type DETECTION_TYPES @@ -2268,8 +2268,8 @@ do -- DETECTION_AREAS --- # 4) DETECTION_AREAS class, extends @{Detection#DETECTION_BASE} -- - -- The DETECTION_AREAS class will detect units within the battle zone for a list of @{Group}s detecting targets following (a) detection method(s), - -- and will build a list (table) of @{Set#SET_UNIT}s containing the @{Unit#UNIT}s detected. + -- The DETECTION_AREAS class will detect units within the battle zone for a list of @{Wrapper.Group}s detecting targets following (a) detection method(s), + -- and will build a list (table) of @{Core.Set#SET_UNIT}s containing the @{Unit#UNIT}s detected. -- The class is group the detected units within zones given a DetectedZoneRange parameter. -- A set with multiple detected zones will be created as there are groups of units detected. -- @@ -2278,7 +2278,7 @@ do -- DETECTION_AREAS -- The methods to manage the DetectedItems[].Set(s) are implemented in @{Detection#DECTECTION_BASE} and -- the methods to manage the DetectedItems[].Zone(s) is implemented in @{Detection#DETECTION_AREAS}. -- - -- Retrieve the DetectedItems[].Set with the method @{Detection#DETECTION_BASE.GetDetectedSet}(). A @{Set#SET_UNIT} object will be returned. + -- Retrieve the DetectedItems[].Set with the method @{Detection#DETECTION_BASE.GetDetectedSet}(). A @{Core.Set#SET_UNIT} object will be returned. -- -- Retrieve the formed @{Zone@ZONE_UNIT}s as a result of the grouping the detected units within the DetectionZoneRange, use the method @{Detection#DETECTION_BASE.GetDetectionZones}(). -- To understand the amount of zones created, use the method @{Detection#DETECTION_BASE.GetDetectionZoneCount}(). @@ -2300,7 +2300,7 @@ do -- DETECTION_AREAS -- -- @type DETECTION_AREAS -- @field Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. - -- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Unit}s, @{Zone}s, the center @{Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. + -- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Wrapper.Unit}s, @{Zone}s, the center @{Wrapper.Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. -- @extends #DETECTION_BASE DETECTION_AREAS = { ClassName = "DETECTION_AREAS", diff --git a/Moose Development/Moose/Functional/Escort.lua b/Moose Development/Moose/Functional/Escort.lua index 6fcddbe7c..25ecced57 100644 --- a/Moose Development/Moose/Functional/Escort.lua +++ b/Moose Development/Moose/Functional/Escort.lua @@ -82,7 +82,7 @@ -- === -- Create a new SPAWN object with the @{#ESCORT.New} method: -- --- * @{#ESCORT.New}: Creates a new ESCORT object from a @{Group#GROUP} for a @{Client#CLIENT}, with an optional briefing text. +-- * @{#ESCORT.New}: Creates a new ESCORT object from a @{Wrapper.Group#GROUP} for a @{Wrapper.Client#CLIENT}, with an optional briefing text. -- -- ESCORT initialization methods. -- === diff --git a/Moose Development/Moose/Functional/MissileTrainer.lua b/Moose Development/Moose/Functional/MissileTrainer.lua index 7659b3c8a..abaa15d09 100644 --- a/Moose Development/Moose/Functional/MissileTrainer.lua +++ b/Moose Development/Moose/Functional/MissileTrainer.lua @@ -2,7 +2,7 @@ -- -- === -- --- 1) @{MissileTrainer#MISSILETRAINER} class, extends @{Base#BASE} +-- 1) @{MissileTrainer#MISSILETRAINER} class, extends @{Core.Base#BASE} -- === -- The @{#MISSILETRAINER} class uses the DCS world messaging system to be alerted of any missiles fired, and when a missile would hit your aircraft, -- the class will destroy the missile within a certain range, to avoid damage to your aircraft. diff --git a/Moose Development/Moose/Functional/Protect.lua b/Moose Development/Moose/Functional/Protect.lua index f8375b867..91c6009f3 100644 --- a/Moose Development/Moose/Functional/Protect.lua +++ b/Moose Development/Moose/Functional/Protect.lua @@ -15,7 +15,7 @@ --- @type PROTECT -- @extends #PROTECT.__ ---- # PROTECT, extends @{Base#BASE} +--- # PROTECT, extends @{Core.Base#BASE} -- -- @field #PROTECT PROTECT = { diff --git a/Moose Development/Moose/Functional/PseudoATC.lua b/Moose Development/Moose/Functional/PseudoATC.lua index ce7f4e737..78ed78f09 100644 --- a/Moose Development/Moose/Functional/PseudoATC.lua +++ b/Moose Development/Moose/Functional/PseudoATC.lua @@ -53,7 +53,7 @@ -- @field #boolean eventsmoose If true, events are handled by MOOSE. If false, events are handled directly by DCS eventhandler. -- @extends Core.Base#BASE ----# PSEUDOATC class, extends @{Base#BASE} +---# PSEUDOATC class, extends @{Core.Base#BASE} -- The PSEUDOATC class adds some rudimentary ATC functionality via the radio menu. -- -- Local weather reports can be requested for nearby airports and player's mission waypoints. diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 91f42eb48..644dbdac0 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -152,7 +152,7 @@ -- @field #boolean useparkingdb Parking spots are added to data base once an aircraft has used it. These spots can later be used by other aircraft. Default is true. -- @extends Core.Spawn#SPAWN ----# RAT class, extends @{Spawn#SPAWN} +---# RAT class, extends @{Core.Spawn#SPAWN} -- The RAT class implements an easy to use way to randomly fill your map with AI aircraft. -- -- @@ -5238,7 +5238,7 @@ end -- @field #number managerid Managing scheduler id. -- @extends Core.Base#BASE ----# RATMANAGER class, extends @{Base#BASE} +---# RATMANAGER class, extends @{Core.Base#BASE} -- The RATMANAGER class manages spawning of multiple RAT objects in a very simple way. It is created by the @{#RATMANAGER.New}() contructor. -- RAT objects with different "tasks" can be defined as usual. However, they **must not** be spawned via the @{#RAT.Spawn}() function. -- diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index bce0b8ef1..706fe8665 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -86,7 +86,7 @@ -- @field #boolean trackmissiles If true (default), all missile types are tracked and impact point to closest bombing target is evaluated. -- @extends Core.Base#BASE ----# RANGE class, extends @{Base#BASE} +---# RANGE class, extends @{Core.Base#BASE} -- The RANGE class enables a mission designer to easily set up practice ranges in DCS. A new RANGE object can be created with the @{#RANGE.New}(rangename) contructor. -- The parameter "rangename" defindes the name of the range. It has to be unique since this is also the name displayed in the radio menu. -- @@ -108,7 +108,7 @@ -- -- A strafe pit can be added to the range by the @{#RANGE.AddStrafepit}(*targetnames, boxlength, boxwidth, heading, inverseheading, goodpass, foulline*) function. -- --- * The first parameter *targetnames* defines the target or targets. This has to be given as a lua table which contains the names of @{Unit} or @{Static} objects defined in the mission editor. +-- * The first parameter *targetnames* defines the target or targets. This has to be given as a lua table which contains the names of @{Wrapper.Unit} or @{Static} objects defined in the mission editor. -- * In order to perform a valid pass on the strafe pit, the pilot has to begin his run from the correct direction. Therefore, an "approach box" is defined in front -- of the strafe targets. The parameters *boxlength* and *boxwidth* define the size of the box while the parameter *heading* defines its direction. -- If the parameter *heading* is passed as **nil**, the heading is automatically taken from the heading of the first target unit as defined in the ME. @@ -118,7 +118,7 @@ -- * The last parameter *foulline* sets the distance from the pit targets to the foul line. Hit from closer than this line are not counted! -- -- Another function to add a strafe pit is @{#RANGE.AddStrafePitGroup}(*group, boxlength, boxwidth, heading, inverseheading, goodpass, foulline*). Here, --- the first parameter *group* is a MOOSE @{Group} object and **all** units in this group define **one** strafe pit. +-- the first parameter *group* is a MOOSE @{Wrapper.Group} object and **all** units in this group define **one** strafe pit. -- -- Finally, a valid approach has to be performed below a certain maximum altitude. The default is 914 meters (3000 ft) AGL. This is a parameter valid for all -- strafing pits of the range and can be adjusted by the @{#RANGE.SetMaxStrafeAlt}(maxalt) function. @@ -126,13 +126,13 @@ -- ## Bombing targets -- One ore multiple bombing targets can be added to the range by the @{#RANGE.AddBombingTargets}(targetnames, goodhitrange, randommove) function. -- --- * The first parameter *targetnames* has to be a lua table, which contains the names of @{Unit} and/or @{Static} objects defined in the mission editor. --- Note that the @{Range} logic **automatically** determines, if a name belongs to a @{Unit} or @{Static} object now. +-- * The first parameter *targetnames* has to be a lua table, which contains the names of @{Wrapper.Unit} and/or @{Static} objects defined in the mission editor. +-- Note that the @{Range} logic **automatically** determines, if a name belongs to a @{Wrapper.Unit} or @{Static} object now. -- * The (optional) parameter *goodhitrange* specifies the radius around the target. If a bomb or rocket falls at a distance smaller than this number, the hit is considered to be "good". -- * If final (optional) parameter "*randommove*" can be enabled to create moving targets. If this parameter is set to true, the units of this bombing target will randomly move within the range zone. -- Note that there might be quirks since DCS units can get stuck in buildings etc. So it might be safer to manually define a route for the units in the mission editor if moving targets are desired. -- --- Another possibility to add bombing targets is the @{#RANGE.AddBombingTargetGroup}(*group, goodhitrange, randommove*) function. Here the parameter *group* is a MOOSE @{Group} object +-- Another possibility to add bombing targets is the @{#RANGE.AddBombingTargetGroup}(*group, goodhitrange, randommove*) function. Here the parameter *group* is a MOOSE @{Wrapper.Group} object -- and **all** units in this group are defined as bombing targets. -- -- ## Fine Tuning diff --git a/Moose Development/Moose/Functional/Scoring.lua b/Moose Development/Moose/Functional/Scoring.lua index 2ca3fd71a..092d7998f 100644 --- a/Moose Development/Moose/Functional/Scoring.lua +++ b/Moose Development/Moose/Functional/Scoring.lua @@ -10,7 +10,7 @@ -- and creates a CSV file logging the scoring events and results for use at team or squadron websites. -- -- SCORING automatically calculates the threat level of the objects hit and destroyed by players, --- which can be @{Unit}, @{Static) and @{Scenery} objects. +-- which can be @{Wrapper.Unit}, @{Static) and @{Scenery} objects. -- -- Positive score points are granted when enemy or neutral targets are destroyed. -- Negative score points or penalties are given when a friendly target is hit or destroyed. @@ -56,7 +56,7 @@ -- Use the radio menu F10 to consult the scores while running the mission. -- Scores can be reported for your user, or an overall score can be reported of all players currently active in the mission. -- --- # 1) @{Scoring#SCORING} class, extends @{Base#BASE} +-- # 1) @{Scoring#SCORING} class, extends @{Core.Base#BASE} -- -- ## 1.1) Set the destroy score or penalty scale -- @@ -74,9 +74,9 @@ -- ## 1.2) Define special targets that will give extra scores. -- -- Special targets can be set that will give extra scores to the players when these are destroyed. --- Use the methods @{#SCORING.AddUnitScore}() and @{#SCORING.RemoveUnitScore}() to specify a special additional score for a specific @{Unit}s. +-- Use the methods @{#SCORING.AddUnitScore}() and @{#SCORING.RemoveUnitScore}() to specify a special additional score for a specific @{Wrapper.Unit}s. -- Use the methods @{#SCORING.AddStaticScore}() and @{#SCORING.RemoveStaticScore}() to specify a special additional score for a specific @{Static}s. --- Use the method @{#SCORING.SetGroupGroup}() to specify a special additional score for a specific @{Group}s. +-- Use the method @{#SCORING.SetGroupGroup}() to specify a special additional score for a specific @{Wrapper.Group}s. -- -- local Scoring = SCORING:New( "Scoring File" ) -- Scoring:AddUnitScore( UNIT:FindByName( "Unit #001" ), 200 ) @@ -93,7 +93,7 @@ -- Define zones of destruction. Any object destroyed within the zone of the given category will give extra points. -- Use the method @{#SCORING.AddZoneScore}() to add a @{Zone} for additional scoring. -- Use the method @{#SCORING.RemoveZoneScore}() to remove a @{Zone} for additional scoring. --- There are interesting variations that can be achieved with this functionality. For example, if the @{Zone} is a @{Zone#ZONE_UNIT}, +-- There are interesting variations that can be achieved with this functionality. For example, if the @{Zone} is a @{Core.Zone#ZONE_UNIT}, -- then the zone is a moving zone, and anything destroyed within that @{Zone} will generate points. -- The other implementation could be to designate a scenery target (a building) in the mission editor surrounded by a @{Zone}, -- just large enough around that building. @@ -339,11 +339,11 @@ function SCORING:SetScaleDestroyPenalty( Scale ) return self end ---- Add a @{Unit} for additional scoring when the @{Unit} is destroyed. --- Note that if there was already a @{Unit} declared within the scoring with the same name, --- then the old @{Unit} will be replaced with the new @{Unit}. +--- Add a @{Wrapper.Unit} for additional scoring when the @{Wrapper.Unit} is destroyed. +-- Note that if there was already a @{Wrapper.Unit} declared within the scoring with the same name, +-- then the old @{Wrapper.Unit} will be replaced with the new @{Wrapper.Unit}. -- @param #SCORING self --- @param Wrapper.Unit#UNIT ScoreUnit The @{Unit} for which the Score needs to be given. +-- @param Wrapper.Unit#UNIT ScoreUnit The @{Wrapper.Unit} for which the Score needs to be given. -- @param #number Score The Score value. -- @return #SCORING function SCORING:AddUnitScore( ScoreUnit, Score ) @@ -355,9 +355,9 @@ function SCORING:AddUnitScore( ScoreUnit, Score ) return self end ---- Removes a @{Unit} for additional scoring when the @{Unit} is destroyed. +--- Removes a @{Wrapper.Unit} for additional scoring when the @{Wrapper.Unit} is destroyed. -- @param #SCORING self --- @param Wrapper.Unit#UNIT ScoreUnit The @{Unit} for which the Score needs to be given. +-- @param Wrapper.Unit#UNIT ScoreUnit The @{Wrapper.Unit} for which the Score needs to be given. -- @return #SCORING function SCORING:RemoveUnitScore( ScoreUnit ) @@ -398,9 +398,9 @@ function SCORING:RemoveStaticScore( ScoreStatic ) end ---- Specify a special additional score for a @{Group}. +--- Specify a special additional score for a @{Wrapper.Group}. -- @param #SCORING self --- @param Wrapper.Group#GROUP ScoreGroup The @{Group} for which each @{Unit} a Score is given. +-- @param Wrapper.Group#GROUP ScoreGroup The @{Wrapper.Group} for which each @{Wrapper.Unit} a Score is given. -- @param #number Score The Score value. -- @return #SCORING function SCORING:AddScoreGroup( ScoreGroup, Score ) @@ -714,7 +714,7 @@ end -- A free text can be given that is shown to the players. -- The Score can be both positive and negative. -- @param #SCORING self --- @param Wrapper.Unit#UNIT PlayerUnit The @{Unit} of the Player. Other Properties for the scoring are taken from this PlayerUnit, like coalition, type etc. +-- @param Wrapper.Unit#UNIT PlayerUnit The @{Wrapper.Unit} of the Player. Other Properties for the scoring are taken from this PlayerUnit, like coalition, type etc. -- @param #string GoalTag The string or identifier that is used in the CSV file to identify the goal (sort or group later in Excel). -- @param #string Text A free text that is shown to the players. -- @param #number Score The score can be both positive or negative ( Penalty ). diff --git a/Moose Development/Moose/Functional/ZoneGoal.lua b/Moose Development/Moose/Functional/ZoneGoal.lua index 5bca12a4e..9c3d22c36 100644 --- a/Moose Development/Moose/Functional/ZoneGoal.lua +++ b/Moose Development/Moose/Functional/ZoneGoal.lua @@ -36,7 +36,7 @@ do -- Zone -- -- ### 2.2 ZONE_GOAL Events -- - -- * DestroyedUnit: A @{Unit} is destroyed in the Zone. The event will only get triggered if the method @{#ZONE_GOAL.MonitorDestroyedUnits}() is used. + -- * DestroyedUnit: A @{Wrapper.Unit} is destroyed in the Zone. The event will only get triggered if the method @{#ZONE_GOAL.MonitorDestroyedUnits}() is used. -- -- @field #ZONE_GOAL ZONE_GOAL = { diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index 1e4452bec..4090980c6 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -23,7 +23,7 @@ -- @extends Core.Base#BASE ---- # COMMANDCENTER class, extends @{Base#BASE} +--- # COMMANDCENTER class, extends @{Core.Base#BASE} -- -- The COMMANDCENTER class governs multiple missions, the tasking and the reporting. -- diff --git a/Moose Development/Moose/Tasking/DetectionManager.lua b/Moose Development/Moose/Tasking/DetectionManager.lua index 5e95771dd..1c4f5492f 100644 --- a/Moose Development/Moose/Tasking/DetectionManager.lua +++ b/Moose Development/Moose/Tasking/DetectionManager.lua @@ -46,7 +46,7 @@ do -- DETECTION MANAGER --- DETECTION_MANAGER class. -- @type DETECTION_MANAGER - -- @field Set#SET_GROUP SetGroup The groups to which the FAC will report to. + -- @field Core.Set#SET_GROUP SetGroup The groups to which the FAC will report to. -- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects. -- @extends Core.Fsm#FSM DETECTION_MANAGER = { @@ -57,7 +57,7 @@ do -- DETECTION MANAGER --- FAC constructor. -- @param #DETECTION_MANAGER self - -- @param Set#SET_GROUP SetGroup + -- @param Core.Set#SET_GROUP SetGroup -- @param Functional.Detection#DETECTION_BASE Detection -- @return #DETECTION_MANAGER self function DETECTION_MANAGER:New( SetGroup, Detection ) @@ -217,7 +217,7 @@ do -- DETECTION MANAGER return self._ReportDisplayTime end - --- Reports the detected items to the @{Set#SET_GROUP}. + --- Reports the detected items to the @{Core.Set#SET_GROUP}. -- @param #DETECTION_MANAGER self -- @param Functional.Detection#DETECTION_BASE Detection -- @return #DETECTION_MANAGER self @@ -233,7 +233,7 @@ do -- DETECTION_REPORTING --- DETECTION_REPORTING class. -- @type DETECTION_REPORTING - -- @field Set#SET_GROUP SetGroup The groups to which the FAC will report to. + -- @field Core.Set#SET_GROUP SetGroup The groups to which the FAC will report to. -- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects. -- @extends #DETECTION_MANAGER DETECTION_REPORTING = { @@ -243,7 +243,7 @@ do -- DETECTION_REPORTING --- DETECTION_REPORTING constructor. -- @param #DETECTION_REPORTING self - -- @param Set#SET_GROUP SetGroup + -- @param Core.Set#SET_GROUP SetGroup -- @param Functional.Detection#DETECTION_AREAS Detection -- @return #DETECTION_REPORTING self function DETECTION_REPORTING:New( SetGroup, Detection ) @@ -257,7 +257,7 @@ do -- DETECTION_REPORTING --- Creates a string of the detected items in a @{Detection}. -- @param #DETECTION_MANAGER self - -- @param Set#SET_UNIT DetectedSet The detected Set created by the @{Detection#DETECTION_BASE} object. + -- @param Core.Set#SET_UNIT DetectedSet The detected Set created by the @{Detection#DETECTION_BASE} object. -- @return #DETECTION_MANAGER self function DETECTION_REPORTING:GetDetectedItemsText( DetectedSet ) self:F2() @@ -287,9 +287,9 @@ do -- DETECTION_REPORTING - --- Reports the detected items to the @{Set#SET_GROUP}. + --- Reports the detected items to the @{Core.Set#SET_GROUP}. -- @param #DETECTION_REPORTING self - -- @param Wrapper.Group#GROUP Group The @{Group} object to where the report needs to go. + -- @param Wrapper.Group#GROUP Group The @{Wrapper.Group} object to where the report needs to go. -- @param Functional.Detection#DETECTION_AREAS Detection The detection created by the @{Detection#DETECTION_BASE} object. -- @return #boolean Return true if you want the reporting to continue... false will cancel the reporting loop. function DETECTION_REPORTING:ProcessDetected( Group, Detection ) diff --git a/Moose Development/Moose/Tasking/Mission.lua b/Moose Development/Moose/Tasking/Mission.lua index 86ba1d410..1ac4383f8 100644 --- a/Moose Development/Moose/Tasking/Mission.lua +++ b/Moose Development/Moose/Tasking/Mission.lua @@ -458,7 +458,7 @@ do -- Group Assignment end - --- Set @{Group} assigned to the @{Mission}. + --- Set @{Wrapper.Group} assigned to the @{Mission}. -- @param #MISSION self -- @param Wrapper.Group#GROUP MissionGroup -- @return #MISSION @@ -473,7 +473,7 @@ do -- Group Assignment return self end - --- Clear the @{Group} assignment from the @{Mission}. + --- Clear the @{Wrapper.Group} assignment from the @{Mission}. -- @param #MISSION self -- @param Wrapper.Group#GROUP MissionGroup -- @return #MISSION diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index d854431a5..e68de90d3 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -21,7 +21,7 @@ -- @extends Core.Fsm#FSM_TASK --- --- # TASK class, extends @{Base#BASE} +-- # TASK class, extends @{Core.Base#BASE} -- -- ## The TASK class implements the methods for task orchestration within MOOSE. -- @@ -186,7 +186,7 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType, TaskBriefing ) -- @param #string From -- @param #string Event -- @param #string To - -- @param Wrapper.Unit#UNIT PlayerUnit The @{Unit} of the player. + -- @param Wrapper.Unit#UNIT PlayerUnit The @{Wrapper.Unit} of the player. -- @param #string PlayerName The name of the player. -- @return #boolean @@ -196,20 +196,20 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType, TaskBriefing ) -- @param #string From -- @param #string Event -- @param #string To - -- @param Wrapper.Unit#UNIT PlayerUnit The @{Unit} of the player. + -- @param Wrapper.Unit#UNIT PlayerUnit The @{Wrapper.Unit} of the player. -- @param #string PlayerName The name of the player. --- Goal Trigger for TASK -- @function [parent=#TASK] Goal -- @param #TASK self - -- @param Wrapper.Unit#UNIT PlayerUnit The @{Unit} of the player. + -- @param Wrapper.Unit#UNIT PlayerUnit The @{Wrapper.Unit} of the player. -- @param #string PlayerName The name of the player. --- Goal Asynchronous Trigger for TASK -- @function [parent=#TASK] __Goal -- @param #TASK self -- @param #number Delay - -- @param Wrapper.Unit#UNIT PlayerUnit The @{Unit} of the player. + -- @param Wrapper.Unit#UNIT PlayerUnit The @{Wrapper.Unit} of the player. -- @param #string PlayerName The name of the player. @@ -489,7 +489,7 @@ do -- Group Assignment end - --- Set @{Group} assigned to the @{Task}. + --- Set @{Wrapper.Group} assigned to the @{Task}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @return #TASK @@ -519,7 +519,7 @@ do -- Group Assignment return self end - --- Clear the @{Group} assignment from the @{Task}. + --- Clear the @{Wrapper.Group} assignment from the @{Task}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @return #TASK @@ -563,7 +563,7 @@ do -- Group Assignment end - --- Assign the @{Task} to a @{Group}. + --- Assign the @{Task} to a @{Wrapper.Group}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @return #TASK @@ -598,7 +598,7 @@ do -- Group Assignment return self end - --- UnAssign the @{Task} from a @{Group}. + --- UnAssign the @{Task} from a @{Wrapper.Group}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup function TASK:UnAssignFromGroup( TaskGroup ) @@ -633,7 +633,7 @@ function TASK:HasGroup( FindGroup ) end ---- Assign the @{Task} to an alive @{Unit}. +--- Assign the @{Task} to an alive @{Wrapper.Unit}. -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK self @@ -652,7 +652,7 @@ function TASK:AssignToUnit( TaskUnit ) return self end ---- UnAssign the @{Task} from an alive @{Unit}. +--- UnAssign the @{Task} from an alive @{Wrapper.Unit}. -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK self @@ -677,7 +677,7 @@ function TASK:SetTimeOut ( Timer ) return self end ---- Send a message of the @{Task} to the assigned @{Group}s. +--- Send a message of the @{Task} to the assigned @{Wrapper.Group}s. -- @param #TASK self function TASK:MessageToGroups( Message ) self:F( { Message = Message } ) @@ -694,7 +694,7 @@ function TASK:MessageToGroups( Message ) end ---- Send the briefng message of the @{Task} to the assigned @{Group}s. +--- Send the briefng message of the @{Task} to the assigned @{Wrapper.Group}s. -- @param #TASK self function TASK:SendBriefingToAssignedGroups() self:F2() @@ -709,7 +709,7 @@ function TASK:SendBriefingToAssignedGroups() end ---- UnAssign the @{Task} from the @{Group}s. +--- UnAssign the @{Task} from the @{Wrapper.Group}s. -- @param #TASK self function TASK:UnAssignFromGroups() self:F2() @@ -874,7 +874,7 @@ function TASK:RemoveMenu( MenuTime ) end ---- Remove the menu option of the @{Task} for a @{Group}. +--- Remove the menu option of the @{Task} for a @{Wrapper.Group}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @param #number MenuTime @@ -905,7 +905,7 @@ function TASK:RefreshMenus( TaskGroup, MenuTime ) end ---- Remove the assigned menu option of the @{Task} for a @{Group}. +--- Remove the assigned menu option of the @{Task} for a @{Wrapper.Group}. -- @param #TASK self -- @param Wrapper.Group#GROUP TaskGroup -- @param #number MenuTime @@ -1001,7 +1001,7 @@ end -- TODO: Obscolete? ---- Fail processes from @{Task} with key @{Unit} +--- Fail processes from @{Task} with key @{Wrapper.Unit} -- @param #TASK self -- @param #string TaskUnitName -- @return #TASK self @@ -1013,7 +1013,7 @@ function TASK:FailProcesses( TaskUnitName ) end end ---- Add a FiniteStateMachine to @{Task} with key Task@{Unit} +--- Add a FiniteStateMachine to @{Task} with key Task@{Wrapper.Unit} -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit -- @param Core.Fsm#FSM_PROCESS Fsm @@ -1026,7 +1026,7 @@ function TASK:SetStateMachine( TaskUnit, Fsm ) return Fsm end ---- Gets the FiniteStateMachine of @{Task} with key Task@{Unit} +--- Gets the FiniteStateMachine of @{Task} with key Task@{Wrapper.Unit} -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit -- @return Core.Fsm#FSM_PROCESS @@ -1036,7 +1036,7 @@ function TASK:GetStateMachine( TaskUnit ) return self.Fsm[TaskUnit] end ---- Remove FiniteStateMachines from @{Task} with key Task@{Unit} +--- Remove FiniteStateMachines from @{Task} with key Task@{Wrapper.Unit} -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK self @@ -1060,7 +1060,7 @@ function TASK:RemoveStateMachine( TaskUnit ) end ---- Checks if there is a FiniteStateMachine assigned to Task@{Unit} for @{Task} +--- Checks if there is a FiniteStateMachine assigned to Task@{Wrapper.Unit} for @{Task} -- @param #TASK self -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK self @@ -1675,7 +1675,7 @@ do -- Task Control Menu --- Init Task Control Menu -- @param #TASK self - -- @param Wrapper.Unit#UNIT TaskUnit The @{Unit} that contains a player. + -- @param Wrapper.Unit#UNIT TaskUnit The @{Wrapper.Unit} that contains a player. -- @return Task Control Menu Refresh ID function TASK:InitTaskControlMenu( TaskUnit ) @@ -1686,7 +1686,7 @@ do -- Task Control Menu --- Get Task Control Menu -- @param #TASK self - -- @param Wrapper.Unit#UNIT TaskUnit The @{Unit} that contains a player. + -- @param Wrapper.Unit#UNIT TaskUnit The @{Wrapper.Unit} that contains a player. -- @return Core.Menu#MENU_GROUP TaskControlMenu The Task Control Menu function TASK:GetTaskControlMenu( TaskUnit, TaskName ) @@ -1706,7 +1706,7 @@ do -- Task Control Menu --- Remove Task Control Menu -- @param #TASK self - -- @param Wrapper.Unit#UNIT TaskUnit The @{Unit} that contains a player. + -- @param Wrapper.Unit#UNIT TaskUnit The @{Wrapper.Unit} that contains a player. function TASK:RemoveTaskControlMenu( TaskUnit ) if self.TaskControlMenu then @@ -1717,7 +1717,7 @@ do -- Task Control Menu --- Refresh Task Control Menu -- @param #TASK self - -- @param Wrapper.Unit#UNIT TaskUnit The @{Unit} that contains a player. + -- @param Wrapper.Unit#UNIT TaskUnit The @{Wrapper.Unit} that contains a player. -- @param MenuTime The refresh time that was used to refresh the Task Control Menu items. -- @param MenuTag The tag. function TASK:RefreshTaskControlMenu( TaskUnit, MenuTime, MenuTag ) diff --git a/Moose Development/Moose/Tasking/TaskInfo.lua b/Moose Development/Moose/Tasking/TaskInfo.lua index 2a3c150c3..723975838 100644 --- a/Moose Development/Moose/Tasking/TaskInfo.lua +++ b/Moose Development/Moose/Tasking/TaskInfo.lua @@ -14,7 +14,7 @@ -- @extends Core.Base#BASE --- --- # TASKINFO class, extends @{Base#BASE} +-- # TASKINFO class, extends @{Core.Base#BASE} -- -- ## The TASKINFO class implements the methods to contain information and display information of a task. -- diff --git a/Moose Development/Moose/Tasking/TaskZoneCapture.lua b/Moose Development/Moose/Tasking/TaskZoneCapture.lua index fd5934dfd..7de3f3e04 100644 --- a/Moose Development/Moose/Tasking/TaskZoneCapture.lua +++ b/Moose Development/Moose/Tasking/TaskZoneCapture.lua @@ -24,7 +24,7 @@ do -- TASK_ZONE_GOAL -- -- * **None**: Start of the process -- * **Planned**: The A2G task is planned. - -- * **Assigned**: The A2G task is assigned to a @{Group#GROUP}. + -- * **Assigned**: The A2G task is assigned to a @{Wrapper.Group#GROUP}. -- * **Success**: The A2G task is successfully completed. -- * **Failed**: The A2G task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. -- @@ -44,7 +44,7 @@ do -- TASK_ZONE_GOAL --- Instantiates a new TASK_ZONE_GOAL. -- @param #TASK_ZONE_GOAL self -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task. -- @param Core.ZoneGoal#ZONE_GOAL ZoneGoal -- @return #TASK_ZONE_GOAL self diff --git a/Moose Development/Moose/Tasking/Task_A2A.lua b/Moose Development/Moose/Tasking/Task_A2A.lua index 0ebc3551a..fff13b776 100644 --- a/Moose Development/Moose/Tasking/Task_A2A.lua +++ b/Moose Development/Moose/Tasking/Task_A2A.lua @@ -14,7 +14,7 @@ do -- TASK_A2A --- The TASK_A2A class -- @type TASK_A2A - -- @field Set#SET_UNIT TargetSetUnit + -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK --- # TASK_A2A class, extends @{Task#TASK} @@ -25,7 +25,7 @@ do -- TASK_A2A -- -- * **None**: Start of the process -- * **Planned**: The A2A task is planned. - -- * **Assigned**: The A2A task is assigned to a @{Group#GROUP}. + -- * **Assigned**: The A2A task is assigned to a @{Wrapper.Group#GROUP}. -- * **Success**: The A2A task is successfully completed. -- * **Failed**: The A2A task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. -- @@ -45,9 +45,9 @@ do -- TASK_A2A --- Instantiates a new TASK_A2A. -- @param #TASK_A2A self -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP SetAttack The set of groups for which the Task can be assigned. + -- @param Core.Set#SET_GROUP SetAttack The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task. - -- @param Set#SET_UNIT UnitSetTargets + -- @param Core.Set#SET_UNIT UnitSetTargets -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. @@ -359,7 +359,7 @@ do -- TASK_A2A_INTERCEPT --- The TASK_A2A_INTERCEPT class -- @type TASK_A2A_INTERCEPT - -- @field Set#SET_UNIT TargetSetUnit + -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK --- # TASK_A2A_INTERCEPT class, extends @{Task_A2A#TASK_A2A} @@ -370,7 +370,7 @@ do -- TASK_A2A_INTERCEPT -- The TASK_A2A_INTERCEPT is used by the @{Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create intercept tasks -- based on detected airborne enemy targets intruding friendly airspace. -- - -- The task is defined for a @{Mission#MISSION}, where a friendly @{Set#SET_GROUP} consisting of GROUPs with one human players each, is intercepting the targets. + -- The task is defined for a @{Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is intercepting the targets. -- The task is given a name and a briefing, that is used in the menu structure and in the reporting. -- -- @field #TASK_A2A_INTERCEPT @@ -458,7 +458,7 @@ do -- TASK_A2A_SWEEP --- The TASK_A2A_SWEEP class -- @type TASK_A2A_SWEEP - -- @field Set#SET_UNIT TargetSetUnit + -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK --- # TASK_A2A_SWEEP class, extends @{Task_A2A#TASK_A2A} @@ -471,7 +471,7 @@ do -- TASK_A2A_SWEEP -- The TASK_A2A_SWEEP is used by the @{Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create sweep tasks -- based on detected airborne enemy targets intruding friendly airspace, for which the detection has been lost for more than 60 seconds. -- - -- The task is defined for a @{Mission#MISSION}, where a friendly @{Set#SET_GROUP} consisting of GROUPs with one human players each, is sweeping the targets. + -- The task is defined for a @{Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is sweeping the targets. -- The task is given a name and a briefing, that is used in the menu structure and in the reporting. -- -- @field #TASK_A2A_SWEEP @@ -569,7 +569,7 @@ do -- TASK_A2A_ENGAGE --- The TASK_A2A_ENGAGE class -- @type TASK_A2A_ENGAGE - -- @field Set#SET_UNIT TargetSetUnit + -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK --- # TASK_A2A_ENGAGE class, extends @{Task_A2A#TASK_A2A} @@ -580,7 +580,7 @@ do -- TASK_A2A_ENGAGE -- The TASK_A2A_ENGAGE is used by the @{Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create engage tasks -- based on detected airborne enemy targets intruding friendly airspace. -- - -- The task is defined for a @{Mission#MISSION}, where a friendly @{Set#SET_GROUP} consisting of GROUPs with one human players each, is engaging the targets. + -- The task is defined for a @{Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is engaging the targets. -- The task is given a name and a briefing, that is used in the menu structure and in the reporting. -- -- @field #TASK_A2A_ENGAGE diff --git a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua index e385fdd02..d3d0c8c39 100644 --- a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua @@ -182,7 +182,7 @@ do -- TASK_A2A_DISPATCHER --- TASK_A2A_DISPATCHER constructor. -- @param #TASK_A2A_DISPATCHER self -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. - -- @param Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission. + -- @param Core.Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission. -- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players. -- @return #TASK_A2A_DISPATCHER self function TASK_A2A_DISPATCHER:New( Mission, SetGroup, Detection ) @@ -251,7 +251,7 @@ do -- TASK_A2A_DISPATCHER --- Creates an INTERCEPT task when there are targets for it. -- @param #TASK_A2A_DISPATCHER self -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem - -- @return Set#SET_UNIT TargetSetUnit: The target set of units. + -- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units. -- @return #nil If there are no targets to be set. function TASK_A2A_DISPATCHER:EvaluateINTERCEPT( DetectedItem ) self:F( { DetectedItem.ItemID } ) @@ -278,7 +278,7 @@ do -- TASK_A2A_DISPATCHER --- Creates an SWEEP task when there are targets for it. -- @param #TASK_A2A_DISPATCHER self -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem - -- @return Set#SET_UNIT TargetSetUnit: The target set of units. + -- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units. -- @return #nil If there are no targets to be set. function TASK_A2A_DISPATCHER:EvaluateSWEEP( DetectedItem ) self:F( { DetectedItem.ItemID } ) @@ -304,7 +304,7 @@ do -- TASK_A2A_DISPATCHER --- Creates an ENGAGE task when there are human friendlies airborne near the targets. -- @param #TASK_A2A_DISPATCHER self -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem - -- @return Set#SET_UNIT TargetSetUnit: The target set of units. + -- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units. -- @return #nil If there are no targets to be set. function TASK_A2A_DISPATCHER:EvaluateENGAGE( DetectedItem ) self:F( { DetectedItem.ItemID } ) @@ -485,7 +485,7 @@ do -- TASK_A2A_DISPATCHER end - --- Assigns tasks in relation to the detected items to the @{Set#SET_GROUP}. + --- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}. -- @param #TASK_A2A_DISPATCHER self -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Detection#DETECTION_BASE} derived object. -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. diff --git a/Moose Development/Moose/Tasking/Task_A2G.lua b/Moose Development/Moose/Tasking/Task_A2G.lua index 6b3f28986..15c33a5f8 100644 --- a/Moose Development/Moose/Tasking/Task_A2G.lua +++ b/Moose Development/Moose/Tasking/Task_A2G.lua @@ -14,7 +14,7 @@ do -- TASK_A2G --- The TASK_A2G class -- @type TASK_A2G - -- @field Set#SET_UNIT TargetSetUnit + -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK --- # TASK_A2G class, extends @{Task#TASK} @@ -25,7 +25,7 @@ do -- TASK_A2G -- -- * **None**: Start of the process -- * **Planned**: The A2G task is planned. - -- * **Assigned**: The A2G task is assigned to a @{Group#GROUP}. + -- * **Assigned**: The A2G task is assigned to a @{Wrapper.Group#GROUP}. -- * **Success**: The A2G task is successfully completed. -- * **Failed**: The A2G task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. -- @@ -45,9 +45,9 @@ do -- TASK_A2G --- Instantiates a new TASK_A2G. -- @param #TASK_A2G self -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task. - -- @param Set#SET_UNIT UnitSetTargets + -- @param Core.Set#SET_UNIT UnitSetTargets -- @param #number TargetDistance The distance to Target when the Player is considered to have "arrived" at the engagement range. -- @param Core.Zone#ZONE_BASE TargetZone The target zone, if known. -- If the TargetZone parameter is specified, the player will be routed to the center of the zone where all the targets are assumed to be. @@ -364,7 +364,7 @@ do -- TASK_A2G_SEAD --- The TASK_A2G_SEAD class -- @type TASK_A2G_SEAD - -- @field Set#SET_UNIT TargetSetUnit + -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK --- # TASK_A2G_SEAD class, extends @{Task_A2G#TASK_A2G} @@ -457,7 +457,7 @@ do -- TASK_A2G_BAI --- The TASK_A2G_BAI class -- @type TASK_A2G_BAI - -- @field Set#SET_UNIT TargetSetUnit + -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK --- # TASK_A2G_BAI class, extends @{Task_A2G#TASK_A2G} @@ -553,7 +553,7 @@ do -- TASK_A2G_CAS --- The TASK_A2G_CAS class -- @type TASK_A2G_CAS - -- @field Set#SET_UNIT TargetSetUnit + -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK --- # TASK_A2G_CAS class, extends @{Task_A2G#TASK_A2G} diff --git a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua index dce26950b..a79825a6b 100644 --- a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua @@ -14,7 +14,7 @@ do -- TASK_A2G_DISPATCHER --- TASK\_A2G\_DISPATCHER class. -- @type TASK_A2G_DISPATCHER - -- @field Set#SET_GROUP SetGroup The groups to which the FAC will report to. + -- @field Core.Set#SET_GROUP SetGroup The groups to which the FAC will report to. -- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects. -- @field Tasking.Mission#MISSION Mission -- @extends Tasking.DetectionManager#DETECTION_MANAGER @@ -28,11 +28,11 @@ do -- TASK_A2G_DISPATCHER -- It provides a truly dynamic battle environment for pilots and ground commanders to engage upon, -- in a true co-operation environment wherein **Multiple Teams** will collaborate in Missions to **achieve a common Mission Goal**. -- - -- The A2G dispatcher will dispatch the A2G Tasks to a defined @{Set} of @{Group}s that will be manned by **Players**. - -- We call this the **AttackSet** of the A2G dispatcher. So, the Players are seated in the @{Client}s of the @{Group} @{Set}. + -- The A2G dispatcher will dispatch the A2G Tasks to a defined @{Set} of @{Wrapper.Group}s that will be manned by **Players**. + -- We call this the **AttackSet** of the A2G dispatcher. So, the Players are seated in the @{Client}s of the @{Wrapper.Group} @{Set}. -- -- Depending on the actions of the enemy, preventive tasks are dispatched to the players to orchestrate the engagement in a true co-operation. - -- The detection object will group the detected targets by its grouping method, and integrates a @{Set} of @{Group}s that are Recce vehicles or air units. + -- The detection object will group the detected targets by its grouping method, and integrates a @{Set} of @{Wrapper.Group}s that are Recce vehicles or air units. -- We call this the **RecceSet** of the A2G dispatcher. -- -- Depending on the current detected tactical situation, different task types will be dispatched to the Players seated in the AttackSet.. @@ -379,8 +379,8 @@ do -- TASK_A2G_DISPATCHER -- - A @{Mission} object. Each task belongs to a Mission. -- - A @{Detection} object. There are several detection grouping methods to choose from. -- - A @{Task_A2G_Dispatcher} object. The master A2G task dispatcher. - -- - A @{Set} of @{Group} objects that will detect the emeny, the RecceSet. This is attached to the @{Detection} object. - -- - A @{Set} ob @{Group} objects that will attack the enemy, the AttackSet. This is attached to the @{Task_A2G_Dispatcher} object. + -- - A @{Set} of @{Wrapper.Group} objects that will detect the emeny, the RecceSet. This is attached to the @{Detection} object. + -- - A @{Set} ob @{Wrapper.Group} objects that will attack the enemy, the AttackSet. This is attached to the @{Task_A2G_Dispatcher} object. -- -- Below an example mission declaration that is defines a Task A2G Dispatcher object. -- @@ -432,7 +432,7 @@ do -- TASK_A2G_DISPATCHER --- TASK_A2G_DISPATCHER constructor. -- @param #TASK_A2G_DISPATCHER self -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. - -- @param Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission. + -- @param Core.Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission. -- @param Functional.Detection#DETECTION_BASE Detection The detection results that are used to dynamically assign new tasks to human players. -- @return #TASK_A2G_DISPATCHER self function TASK_A2G_DISPATCHER:New( Mission, SetGroup, Detection ) @@ -582,7 +582,7 @@ do -- TASK_A2G_DISPATCHER end - --- Assigns tasks in relation to the detected items to the @{Set#SET_GROUP}. + --- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}. -- @param #TASK_A2G_DISPATCHER self -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Detection#DETECTION_BASE} derived object. -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 47ff9a6ee..7cf6bd9c1 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -133,7 +133,7 @@ do -- TASK_CARGO -- -- * **None**: Start of the process. -- * **Planned**: The cargo task is planned. - -- * **Assigned**: The cargo task is assigned to a @{Group#GROUP}. + -- * **Assigned**: The cargo task is assigned to a @{Wrapper.Group#GROUP}. -- * **Success**: The cargo task is successfully completed. -- * **Failed**: The cargo task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. -- @@ -148,7 +148,7 @@ do -- TASK_CARGO --- Instantiates a new TASK_CARGO. -- @param #TASK_CARGO self -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task. -- @param Core.Set#SET_CARGO SetCargo The scope of the cargo to be transported. -- @param #string TaskType The type of Cargo task. diff --git a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua index b2e2aebdb..b033ee17c 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua @@ -17,7 +17,7 @@ do -- TASK_CARGO_CSAR --- Instantiates a new TASK_CARGO_CSAR. -- @param #TASK_CARGO_CSAR self -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task. -- @param Core.Set#SET_CARGO SetCargo The scope of the cargo to be transported. -- @param #string TaskBriefing The Cargo Task briefing. diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index 1b8dd610f..9671798d6 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -187,7 +187,7 @@ do -- TASK_CARGO_DISPATCHER --- TASK_CARGO_DISPATCHER constructor. -- @param #TASK_CARGO_DISPATCHER self -- @param Tasking.Mission#MISSION Mission The mission for which the task dispatching is done. - -- @param Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission. + -- @param Core.Set#SET_GROUP SetGroup The set of groups that can join the tasks within the mission. -- @return #TASK_CARGO_DISPATCHER self function TASK_CARGO_DISPATCHER:New( Mission, SetGroup ) @@ -498,7 +498,7 @@ do -- TASK_CARGO_DISPATCHER --- Evaluates of a CSAR task needs to be started. -- @param #TASK_CARGO_DISPATCHER self - -- @return Set#SET_CARGO The SetCargo to be rescued. + -- @return Core.Set#SET_CARGO The SetCargo to be rescued. -- @return #nil If there is no CSAR task required. function TASK_CARGO_DISPATCHER:EvaluateCSAR( CSARUnit ) @@ -515,7 +515,7 @@ do -- TASK_CARGO_DISPATCHER - --- Assigns tasks to the @{Set#SET_GROUP}. + --- Assigns tasks to the @{Core.Set#SET_GROUP}. -- @param #TASK_CARGO_DISPATCHER self -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. function TASK_CARGO_DISPATCHER:ManageTasks() diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua index 21e296860..9dac77e5d 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua @@ -18,7 +18,7 @@ do -- TASK_CARGO_TRANSPORT --- Instantiates a new TASK_CARGO_TRANSPORT. -- @param #TASK_CARGO_TRANSPORT self -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. + -- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task. -- @param Core.Set#SET_CARGO SetCargo The scope of the cargo to be transported. -- @param #string TaskBriefing The Cargo Task briefing. diff --git a/Moose Development/Moose/Tasking/Task_Manager.lua b/Moose Development/Moose/Tasking/Task_Manager.lua index fe61dac24..7788fb9d1 100644 --- a/Moose Development/Moose/Tasking/Task_Manager.lua +++ b/Moose Development/Moose/Tasking/Task_Manager.lua @@ -35,7 +35,7 @@ do -- TASK_MANAGER --- TASK_MANAGER class. -- @type TASK_MANAGER - -- @field Set#SET_GROUP SetGroup The set of group objects containing players for which tasks are managed. + -- @field Core.Set#SET_GROUP SetGroup The set of group objects containing players for which tasks are managed. -- @extends Core.Fsm#FSM TASK_MANAGER = { ClassName = "TASK_MANAGER", @@ -44,7 +44,7 @@ do -- TASK_MANAGER --- TASK\_MANAGER constructor. -- @param #TASK_MANAGER self - -- @param Set#SET_GROUP SetGroup The set of group objects containing players for which tasks are managed. + -- @param Core.Set#SET_GROUP SetGroup The set of group objects containing players for which tasks are managed. -- @return #TASK_MANAGER self function TASK_MANAGER:New( SetGroup ) @@ -180,7 +180,7 @@ do -- TASK_MANAGER end - --- Manages the tasks for the @{Set#SET_GROUP}. + --- Manages the tasks for the @{Core.Set#SET_GROUP}. -- @param #TASK_MANAGER self -- @return #TASK_MANAGER self function TASK_MANAGER:ManageTasks() diff --git a/Moose Development/Moose/Tasking/Task_Pickup.lua b/Moose Development/Moose/Tasking/Task_Pickup.lua index 459f9f4a2..b33acedac 100644 --- a/Moose Development/Moose/Tasking/Task_Pickup.lua +++ b/Moose Development/Moose/Tasking/Task_Pickup.lua @@ -8,7 +8,7 @@ -- -- * **None**: Start of the process -- * **Planned**: The SEAD task is planned. Upon Planned, the sub-process @{Process_Fsm.Assign#ACT_ASSIGN_ACCEPT} is started to accept the task. --- * **Assigned**: The SEAD task is assigned to a @{Group#GROUP}. Upon Assigned, the sub-process @{Process_Fsm.Route#ACT_ROUTE} is started to route the active Units in the Group to the attack zone. +-- * **Assigned**: The SEAD task is assigned to a @{Wrapper.Group#GROUP}. Upon Assigned, the sub-process @{Process_Fsm.Route#ACT_ROUTE} is started to route the active Units in the Group to the attack zone. -- * **Success**: The SEAD task is successfully completed. Upon Success, the sub-process @{Process_SEAD#PROCESS_SEAD} is started to follow-up successful SEADing of the targets assigned in the task. -- * **Failed**: The SEAD task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. -- @@ -31,10 +31,10 @@ do -- TASK_PICKUP --- Instantiates a new TASK_PICKUP. -- @param #TASK_PICKUP self -- @param Tasking.Mission#MISSION Mission - -- @param Set#SET_GROUP AssignedSetGroup The set of groups for which the Task can be assigned. + -- @param Core.Set#SET_GROUP AssignedSetGroup The set of groups for which the Task can be assigned. -- @param #string TaskName The name of the Task. -- @param #string TaskType BAI or CAS - -- @param Set#SET_UNIT UnitSetTargets + -- @param Core.Set#SET_UNIT UnitSetTargets -- @param Core.Zone#ZONE_BASE TargetZone -- @return #TASK_PICKUP self function TASK_PICKUP:New( Mission, AssignedSetGroup, TaskName, TaskType ) @@ -55,7 +55,7 @@ do -- TASK_PICKUP end - --- Assign the @{Task} to a @{Unit}. + --- Assign the @{Task} to a @{Wrapper.Unit}. -- @param #TASK_PICKUP self -- @param Wrapper.Unit#UNIT TaskUnit -- @return #TASK_PICKUP self @@ -97,7 +97,7 @@ do -- TASK_PICKUP -- @param #string Event -- @param #string From -- @param #string To - -- @param Event#EVENTDATA Event + -- @param Core.Event#EVENTDATA Event function TASK_PICKUP:OnNext( Fsm, From, Event, To, Event ) self:SetState( self, "State", To ) diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index e6db9284c..1f7f98cbf 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -8,13 +8,13 @@ -- -- === -- --- @module Airbase +-- @module Wrapper.Airbase --- @type AIRBASE -- @extends Wrapper.Positionable#POSITIONABLE ---- # AIRBASE class, extends @{Positionable#POSITIONABLE} +--- # AIRBASE class, extends @{Wrapper.Positionable#POSITIONABLE} -- -- AIRBASE is a wrapper class to handle the DCS Airbase objects: -- diff --git a/Moose Development/Moose/Wrapper/Client.lua b/Moose Development/Moose/Wrapper/Client.lua index 4ccfe98d4..4f9b80f83 100644 --- a/Moose Development/Moose/Wrapper/Client.lua +++ b/Moose Development/Moose/Wrapper/Client.lua @@ -8,7 +8,7 @@ -- -- === -- --- @module Client +-- @module Wrapper.Client --- The CLIENT class @@ -16,7 +16,7 @@ -- @extends Wrapper.Unit#UNIT ---- # CLIENT class, extends @{Unit#UNIT} +--- # CLIENT class, extends @{Wrapper.Unit#UNIT} -- -- Clients are those **Units** defined within the Mission Editor that have the skillset defined as __Client__ or __Player__. -- Note that clients are NOT the same as Units, they are NOT necessarily alive. diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 04576498f..cdeac78e6 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -19,7 +19,7 @@ ---- # CONTROLLABLE class, extends @{Positionable#POSITIONABLE} +--- # CONTROLLABLE class, extends @{Wrapper.Positionable#POSITIONABLE} -- -- CONTROLLABLE is a wrapper class to handle the "DCS Controllable objects", which are Groups and Units: -- @@ -37,7 +37,7 @@ -- ## CONTROLLABLE Task methods -- -- Several controllable task methods are available that help you to prepare tasks. --- These methods return a string consisting of the task description, which can then be given to either a @{Controllable#CONTROLLABLE.PushTask} or @{Controllable#SetTask} method to assign the task to the CONTROLLABLE. +-- These methods return a string consisting of the task description, which can then be given to either a @{Wrapper.Controllable#CONTROLLABLE.PushTask} or @{Wrapper.Controllable#SetTask} method to assign the task to the CONTROLLABLE. -- Tasks are specific for the category of the CONTROLLABLE, more specific, for AIR, GROUND or AIR and GROUND. -- Each task description where applicable indicates for which controllable category the task is valid. -- There are 2 main subdivisions of tasks: Assigned tasks and EnRoute tasks. @@ -63,7 +63,7 @@ -- * @{#CONTROLLABLE.TaskHold}: (GROUND) Hold ground controllable from moving. -- * @{#CONTROLLABLE.TaskHoldPosition}: (AIR) Hold position at the current position of the first unit of the controllable. -- * @{#CONTROLLABLE.TaskLand}: (AIR HELICOPTER) Landing at the ground. For helicopters only. --- * @{#CONTROLLABLE.TaskLandAtZone}: (AIR) Land the controllable at a @{Zone#ZONE_RADIUS). +-- * @{#CONTROLLABLE.TaskLandAtZone}: (AIR) Land the controllable at a @{Core.Zone#ZONE_RADIUS). -- * @{#CONTROLLABLE.TaskOrbitCircle}: (AIR) Orbit at the current position of the first unit of the controllable at a specified alititude. -- * @{#CONTROLLABLE.TaskOrbitCircleAtVec2}: (AIR) Orbit at a specified position at a specified alititude during a specified duration with a specified speed. -- * @{#CONTROLLABLE.TaskRefueling}: (AIR) Refueling from the nearest tanker. No parameters. @@ -932,7 +932,7 @@ function CONTROLLABLE:TaskLandAtVec2( Point, Duration ) return DCSTask end ---- (AIR) Land the controllable at a @{Zone#ZONE_RADIUS). +--- (AIR) Land the controllable at a @{Core.Zone#ZONE_RADIUS). -- @param #CONTROLLABLE self -- @param Core.Zone#ZONE Zone The zone where to land. -- @param #number Duration The duration in seconds to stay on the ground. @@ -2195,7 +2195,7 @@ end ---- Return the route of a controllable by using the @{Database#DATABASE} class. +--- Return the route of a controllable by using the @{Core.Database#DATABASE} class. -- @param #CONTROLLABLE self -- @param #number Begin The route point from where the copy will start. The base route point is 0. -- @param #number End The route point where the copy will end. The End point is the last point - the End point. The last point has base 0. @@ -2742,7 +2742,7 @@ end --- Retrieve the controllable mission and allow to place function hooks within the mission waypoint plan. --- Use the method @{Controllable#CONTROLLABLE:WayPointFunction} to define the hook functions for specific waypoints. +-- Use the method @{Wrapper.Controllable#CONTROLLABLE:WayPointFunction} to define the hook functions for specific waypoints. -- Use the method @{Controllable@CONTROLLABLE:WayPointExecute) to start the execution of the new mission plan. -- Note that when WayPointInitialize is called, the Mission of the controllable is RESTARTED! -- @param #CONTROLLABLE self diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 6f45f873d..c33b674a2 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -32,7 +32,7 @@ --- --- # GROUP class, extends @{Controllable#CONTROLLABLE} +-- # GROUP class, extends @{Wrapper.Controllable#CONTROLLABLE} -- -- For each DCS Group object alive within a running mission, a GROUP wrapper object (instance) will be created within the _@{DATABASE} object. -- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Group objects are spawned (using the @{SPAWN} class). @@ -51,21 +51,21 @@ -- -- ## GROUP task methods -- --- A GROUP is a @{Controllable}. See the @{Controllable} task methods section for a description of the task methods. +-- A GROUP is a @{Wrapper.Controllable}. See the @{Wrapper.Controllable} task methods section for a description of the task methods. -- -- ### Obtain the mission from group templates -- -- Group templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a group and assign it to another: -- --- * @{Controllable#CONTROLLABLE.TaskMission}: (AIR + GROUND) Return a mission task from a mission template. +-- * @{Wrapper.Controllable#CONTROLLABLE.TaskMission}: (AIR + GROUND) Return a mission task from a mission template. -- -- ## GROUP Command methods -- --- A GROUP is a @{Controllable}. See the @{Controllable} command methods section for a description of the command methods. +-- A GROUP is a @{Wrapper.Controllable}. See the @{Wrapper.Controllable} command methods section for a description of the command methods. -- -- ## GROUP option methods -- --- A GROUP is a @{Controllable}. See the @{Controllable} option methods section for a description of the option methods. +-- A GROUP is a @{Wrapper.Controllable}. See the @{Wrapper.Controllable} option methods section for a description of the option methods. -- -- ## GROUP Zone validation methods -- @@ -76,7 +76,7 @@ -- * @{#GROUP.IsPartlyInZone}: Returns true if some units of the group are within a @{Zone}. -- * @{#GROUP.IsNotInZone}: Returns true if none of the group units of the group are within a @{Zone}. -- --- The zone can be of any @{Zone} class derived from @{Zone#ZONE_BASE}. So, these methods are polymorphic to the zones tested on. +-- The zone can be of any @{Zone} class derived from @{Core.Zone#ZONE_BASE}. So, these methods are polymorphic to the zones tested on. -- -- ## GROUP AI methods -- @@ -112,7 +112,7 @@ GROUPTEMPLATE.Takeoff = { --- Create a new GROUP from a given GroupTemplate as a parameter. -- Note that the GroupTemplate is NOT spawned into the mission. --- It is merely added to the @{Database}. +-- It is merely added to the @{Core.Database}. -- @param #GROUP self -- @param #table GroupTemplate The GroupTemplate Structure exactly as defined within the mission editor. -- @param Dcs.DCScoalition#coalition.side CoalitionSide The coalition.side of the group. @@ -209,8 +209,8 @@ end -- * Exist at run-time. -- * Has at least one unit. -- --- When the first @{Unit} of the Group is active, it will return true. --- If the first @{Unit} of the Group is inactive, it will return false. +-- When the first @{Wrapper.Unit} of the Group is active, it will return true. +-- If the first @{Wrapper.Unit} of the Group is inactive, it will return false. -- -- @param #GROUP self -- @return #boolean true if the Group is alive and active. @@ -350,9 +350,9 @@ function GROUP:GetCountry() return nil end ---- Returns a list of @{Unit} objects of the @{Group}. +--- Returns a list of @{Wrapper.Unit} objects of the @{Wrapper.Group}. -- @param #GROUP self --- @return #list The list of @{Unit} objects of the @{Group}. +-- @return #list The list of @{Wrapper.Unit} objects of the @{Wrapper.Group}. function GROUP:GetUnits() self:F2( { self.GroupName } ) local DCSGroup = self:GetDCSObject() @@ -371,9 +371,9 @@ function GROUP:GetUnits() end ---- Returns a list of @{Unit} objects of the @{Group} that are occupied by a player. +--- Returns a list of @{Wrapper.Unit} objects of the @{Wrapper.Group} that are occupied by a player. -- @param #GROUP self --- @return #list The list of player occupied @{Unit} objects of the @{Group}. +-- @return #list The list of player occupied @{Wrapper.Unit} objects of the @{Wrapper.Group}. function GROUP:GetPlayerUnits() self:F2( { self.GroupName } ) local DCSGroup = self:GetDCSObject() @@ -770,7 +770,7 @@ do -- Is Zone methods --- Returns true if all units of the group are within a @{Zone}. -- @param #GROUP self -- @param Core.Zone#ZONE_BASE Zone The zone to test. --- @return #boolean Returns true if the Group is completely within the @{Zone#ZONE_BASE} +-- @return #boolean Returns true if the Group is completely within the @{Core.Zone#ZONE_BASE} function GROUP:IsCompletelyInZone( Zone ) self:F2( { self.GroupName, Zone } ) @@ -790,7 +790,7 @@ end --- Returns true if some units of the group are within a @{Zone}. -- @param #GROUP self -- @param Core.Zone#ZONE_BASE Zone The zone to test. --- @return #boolean Returns true if the Group is partially within the @{Zone#ZONE_BASE} +-- @return #boolean Returns true if the Group is partially within the @{Core.Zone#ZONE_BASE} function GROUP:IsPartlyInZone( Zone ) self:F2( { self.GroupName, Zone } ) @@ -818,7 +818,7 @@ end --- Returns true if none of the group units of the group are within a @{Zone}. -- @param #GROUP self -- @param Core.Zone#ZONE_BASE Zone The zone to test. --- @return #boolean Returns true if the Group is not within the @{Zone#ZONE_BASE} +-- @return #boolean Returns true if the Group is not within the @{Core.Zone#ZONE_BASE} function GROUP:IsNotInZone( Zone ) self:F2( { self.GroupName, Zone } ) @@ -1164,7 +1164,7 @@ function GROUP:InitRandomizePositionRadius( OuterRadius, InnerRadius ) end ---- Respawn the @{Group} at a @{Point}. +--- Respawn the @{Wrapper.Group} at a @{Point}. -- The method will setup the new group template according the Init(Respawn) settings provided for the group. -- These settings can be provided by calling the relevant Init...() methods of the Group. -- @@ -1378,7 +1378,7 @@ function GROUP:GetTaskRoute() return routines.utils.deepCopy( _DATABASE.Templates.Groups[self.GroupName].Template.route.points ) end ---- Return the route of a group by using the @{Database#DATABASE} class. +--- Return the route of a group by using the @{Core.Database#DATABASE} class. -- @param #GROUP self -- @param #number Begin The route point from where the copy will start. The base route point is 0. -- @param #number End The route point where the copy will end. The End point is the last point - the End point. The last point has base 0. @@ -1469,17 +1469,17 @@ end do -- Route methods - --- (AIR) Return the Group to an @{Airbase#AIRBASE}. + --- (AIR) Return the Group to an @{Wrapper.Airbase#AIRBASE}. -- The following things are to be taken into account: -- -- * The group is respawned to achieve the RTB, there may be side artefacts as a result of this. (Like weapons suddenly come back). -- * A group consisting out of more than one unit, may rejoin formation when respawned. -- * A speed can be given in km/h. If no speed is specified, the maximum speed of the first unit will be taken to return to base. - -- * When there is no @{Airbase} object specified, the group will return to the home base if the route of the group is pinned at take-off or at landing to a base. - -- * When there is no @{Airbase} object specified and the group route is not pinned to any airbase, it will return to the nearest airbase. + -- * When there is no @{Wrapper.Airbase} object specified, the group will return to the home base if the route of the group is pinned at take-off or at landing to a base. + -- * When there is no @{Wrapper.Airbase} object specified and the group route is not pinned to any airbase, it will return to the nearest airbase. -- -- @param #GROUP self - -- @param Wrapper.Airbase#AIRBASE RTBAirbase (optional) The @{Airbase} to return to. If blank, the controllable will return to the nearest friendly airbase. + -- @param Wrapper.Airbase#AIRBASE RTBAirbase (optional) The @{Wrapper.Airbase} to return to. If blank, the controllable will return to the nearest friendly airbase. -- @param #number Speed (optional) The Speed, if no Speed is given, the maximum Speed of the first unit is selected. -- @return #GROUP function GROUP:RouteRTB( RTBAirbase, Speed ) diff --git a/Moose Development/Moose/Wrapper/Identifiable.lua b/Moose Development/Moose/Wrapper/Identifiable.lua index edd123665..bbc6e482b 100644 --- a/Moose Development/Moose/Wrapper/Identifiable.lua +++ b/Moose Development/Moose/Wrapper/Identifiable.lua @@ -8,13 +8,13 @@ -- -- === -- --- @module Identifiable +-- @module Wrapper.Identifiable --- @type IDENTIFIABLE -- @extends Wrapper.Object#OBJECT -- @field #string IdentifiableName The name of the identifiable. ---- # IDENTIFIABLE class, extends @{Object#OBJECT} +--- # IDENTIFIABLE class, extends @{Wrapper.Object#OBJECT} -- -- The IDENTIFIABLE class is a wrapper class to handle the DCS Identifiable objects: -- diff --git a/Moose Development/Moose/Wrapper/Object.lua b/Moose Development/Moose/Wrapper/Object.lua index 656031e78..6438cf807 100644 --- a/Moose Development/Moose/Wrapper/Object.lua +++ b/Moose Development/Moose/Wrapper/Object.lua @@ -8,7 +8,7 @@ -- -- === -- --- @module Object +-- @module Wrapper.Object --- @type OBJECT @@ -16,7 +16,7 @@ -- @field #string ObjectName The name of the Object. ---- # OBJECT class, extends @{Base#BASE} +--- # OBJECT class, extends @{Core.Base#BASE} -- -- OBJECT handles the DCS Object objects: -- diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 1ac85b6a4..d01a7c26b 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -8,7 +8,7 @@ -- -- === -- --- @module Positionable +-- @module Wrapper.Positionable --- @type POSITIONABLE.__ Methods which are not intended for mission designers, but which are used interally by the moose designer :-) -- @extends Wrapper.Identifiable#IDENTIFIABLE @@ -17,7 +17,7 @@ -- @extends Wrapper.Identifiable#IDENTIFIABLE ---- # POSITIONABLE class, extends @{Identifiable#IDENTIFIABLE} +--- # POSITIONABLE class, extends @{Wrapper.Identifiable#IDENTIFIABLE} -- -- The POSITIONABLE class is a wrapper class to handle the POSITIONABLE objects: -- @@ -626,7 +626,7 @@ function POSITIONABLE:MessageToClient( Message, Duration, Client, Name ) return nil end ---- Send a message to a @{Group}. +--- Send a message to a @{Wrapper.Group}. -- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. -- @param #POSITIONABLE self -- @param #string Message The message text @@ -653,7 +653,7 @@ function POSITIONABLE:MessageToGroup( Message, Duration, MessageGroup, Name ) return nil end ---- Send a message of a message type to a @{Group}. +--- Send a message of a message type to a @{Wrapper.Group}. -- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. -- @param #POSITIONABLE self -- @param #string Message The message text @@ -673,7 +673,7 @@ function POSITIONABLE:MessageTypeToGroup( Message, MessageType, MessageGroup, Na return nil end ---- Send a message to a @{Set#SET_GROUP}. +--- Send a message to a @{Core.Set#SET_GROUP}. -- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. -- @param #POSITIONABLE self -- @param #string Message The message text @@ -697,7 +697,7 @@ function POSITIONABLE:MessageToSetGroup( Message, Duration, MessageSetGroup, Nam return nil end ---- Send a message to the players in the @{Group}. +--- Send a message to the players in the @{Wrapper.Group}. -- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. -- @param #POSITIONABLE self -- @param #string Message The message text diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index e999917b1..8160aa955 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -8,7 +8,7 @@ -- -- === -- --- @module Scenery +-- @module Wrapper.Scenery @@ -16,7 +16,7 @@ -- @extends Wrapper.Positionable#POSITIONABLE ---- # SCENERY class, extends @{Positionable#POSITIONABLE} +--- # SCENERY class, extends @{Wrapper.Positionable#POSITIONABLE} -- -- Scenery objects are defined on the map. -- The @{Scenery#SCENERY} class is a wrapper class to handle the DCS Scenery objects: diff --git a/Moose Development/Moose/Wrapper/Static.lua b/Moose Development/Moose/Wrapper/Static.lua index ad53a16dd..bab5b8d70 100644 --- a/Moose Development/Moose/Wrapper/Static.lua +++ b/Moose Development/Moose/Wrapper/Static.lua @@ -8,13 +8,13 @@ -- -- === -- --- @module Static +-- @module Wrapper.Static --- @type STATIC -- @extends Wrapper.Positionable#POSITIONABLE ---- # STATIC class, extends @{Positionable#POSITIONABLE} +--- # STATIC class, extends @{Wrapper.Positionable#POSITIONABLE} -- -- Statics are **Static Units** defined within the Mission Editor. -- Note that Statics are almost the same as Units, but they don't have a controller. @@ -126,7 +126,7 @@ function STATIC:GetThreatLevel() return 1, "Static" end ---- Respawn the @{Unit} using a (tweaked) template of the parent Group. +--- Respawn the @{Wrapper.Unit} using a (tweaked) template of the parent Group. -- @param #UNIT self -- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static. -- @param #number Heading The heading of the unit respawn. @@ -138,7 +138,7 @@ function STATIC:SpawnAt( Coordinate, Heading ) end ---- Respawn the @{Unit} at the same location with the same properties. +--- Respawn the @{Wrapper.Unit} at the same location with the same properties. -- This is useful to respawn a cargo after it has been destroyed. -- @param #UNIT self function STATIC:ReSpawn() @@ -149,7 +149,7 @@ function STATIC:ReSpawn() end ---- Respawn the @{Unit} at a defined Coordinate with an optional heading. +--- Respawn the @{Wrapper.Unit} at a defined Coordinate with an optional heading. -- @param #UNIT self -- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static. -- @param #number Heading The heading of the unit respawn. diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 1c4236dc3..8d11d8723 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -17,14 +17,14 @@ -- -- === -- --- @module Unit +-- @module Wrapper.Unit --- @type UNIT -- @extends Wrapper.Controllable#CONTROLLABLE --- --- # UNIT class, extends @{Controllable#CONTROLLABLE} +-- # UNIT class, extends @{Wrapper.Controllable#CONTROLLABLE} -- -- For each DCS Unit object alive within a running mission, a UNIT wrapper object (instance) will be created within the _@{DATABASE} object. -- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Unit objects are spawned (using the @{SPAWN} class). @@ -75,7 +75,7 @@ -- -- ### Zones range -- --- To test whether the Unit is within a **zone**, use the @{#UNIT.IsInZone}() or the @{#UNIT.IsNotInZone}() methods. Any zone can be tested on, but the zone must be derived from @{Zone#ZONE_BASE}. +-- To test whether the Unit is within a **zone**, use the @{#UNIT.IsInZone}() or the @{#UNIT.IsNotInZone}() methods. Any zone can be tested on, but the zone must be derived from @{Core.Zone#ZONE_BASE}. -- -- ### Unit range -- @@ -190,7 +190,7 @@ function UNIT:Destroy( GenerateEvent ) end ---- Respawn the @{Unit} using a (tweaked) template of the parent Group. +--- Respawn the @{Wrapper.Unit} using a (tweaked) template of the parent Group. -- -- This function will: -- @@ -556,9 +556,9 @@ function UNIT:GetFuel() return nil end ---- Returns a list of one @{Unit}. +--- Returns a list of one @{Wrapper.Unit}. -- @param #UNIT self --- @return #list A list of one @{Unit}. +-- @return #list A list of one @{Wrapper.Unit}. function UNIT:GetUnits() self:F2( { self.UnitName } ) local DCSUnit = self:GetDCSObject() @@ -778,7 +778,7 @@ end --- Returns true if the unit is within a @{Zone}. -- @param #UNIT self -- @param Core.Zone#ZONE_BASE Zone The zone to test. --- @return #boolean Returns true if the unit is within the @{Zone#ZONE_BASE} +-- @return #boolean Returns true if the unit is within the @{Core.Zone#ZONE_BASE} function UNIT:IsInZone( Zone ) self:F2( { self.UnitName, Zone } ) @@ -793,7 +793,7 @@ end --- Returns true if the unit is not within a @{Zone}. -- @param #UNIT self -- @param Core.Zone#ZONE_BASE Zone The zone to test. --- @return #boolean Returns true if the unit is not within the @{Zone#ZONE_BASE} +-- @return #boolean Returns true if the unit is not within the @{Core.Zone#ZONE_BASE} function UNIT:IsNotInZone( Zone ) self:F2( { self.UnitName, Zone } ) From 5e2a5cf5e8d51c75d10206ee887e7d138f6f8913 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Wed, 23 May 2018 13:51:47 +0200 Subject: [PATCH 128/420] Tasking and Functional --- .../Moose/Functional/ATC_Ground.lua | 2 +- .../Moose/Functional/Artillery.lua | 2 +- .../Moose/Functional/CleanUp.lua | 2 +- .../Moose/Functional/Designate.lua | 2 +- .../Moose/Functional/Detection.lua | 2 +- Moose Development/Moose/Functional/Escort.lua | 3 +- .../Moose/Functional/MissileTrainer.lua | 3 +- .../Moose/Functional/Movement.lua | 2 +- .../Moose/Functional/Protect.lua | 2 +- .../Moose/Functional/PseudoATC.lua | 2 +- Moose Development/Moose/Functional/RAT.lua | 2 +- Moose Development/Moose/Functional/Range.lua | 2 +- .../Moose/Functional/Scoring.lua | 2 +- Moose Development/Moose/Functional/Sead.lua | 2 +- .../Moose/Functional/Suppression.lua | 2 +- .../Moose/Functional/ZoneCaptureCoalition.lua | 2 +- .../Moose/Functional/ZoneGoal.lua | 2 +- .../Moose/Functional/ZoneGoalCargo.lua | 2 +- .../Moose/Functional/ZoneGoalCoalition.lua | 2 +- .../Moose/Tasking/CommandCenter.lua | 2 +- .../Moose/Tasking/DetectionManager.lua | 2 +- Moose Development/Moose/Tasking/Mission.lua | 2 +- Moose Development/Moose/Tasking/Task.lua | 2 +- Moose Development/Moose/Tasking/TaskInfo.lua | 2 +- .../Moose/Tasking/TaskZoneCapture.lua | 2 +- Moose Development/Moose/Tasking/Task_A2A.lua | 2 +- .../Moose/Tasking/Task_A2A_Dispatcher.lua | 2 +- Moose Development/Moose/Tasking/Task_A2G.lua | 2 +- .../Moose/Tasking/Task_A2G_Dispatcher.lua | 2 +- .../Moose/Tasking/Task_Cargo_CSAR.lua | 2 + .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 2 +- .../Moose/Tasking/Task_Cargo_Transport.lua | 2 +- .../Moose/Tasking/Task_Manager.lua | 2 +- .../Moose/Tasking/Task_Pickup.lua | 132 ------------------ 34 files changed, 34 insertions(+), 166 deletions(-) delete mode 100644 Moose Development/Moose/Tasking/Task_Pickup.lua diff --git a/Moose Development/Moose/Functional/ATC_Ground.lua b/Moose Development/Moose/Functional/ATC_Ground.lua index 5709e7686..707a8ff24 100644 --- a/Moose Development/Moose/Functional/ATC_Ground.lua +++ b/Moose Development/Moose/Functional/ATC_Ground.lua @@ -11,7 +11,7 @@ -- -- === -- --- @module ATC_Ground +-- @module Functional.ATC_Ground --- @type ATC_GROUND diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index c38ee1fad..59806a42c 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -38,7 +38,7 @@ -- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536) -- -- ==== --- @module Arty +-- @module Functional.Arty ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- ARTY class diff --git a/Moose Development/Moose/Functional/CleanUp.lua b/Moose Development/Moose/Functional/CleanUp.lua index 84ea9c413..f7414e190 100644 --- a/Moose Development/Moose/Functional/CleanUp.lua +++ b/Moose Development/Moose/Functional/CleanUp.lua @@ -7,7 +7,7 @@ -- -- === -- --- @module CleanUp +-- @module Functional.CleanUp --- @type CLEANUP_AIRBASE.__ Methods which are not intended for mission designers, but which are used interally by the moose designer :-) -- @field #map<#string,Wrapper.Airbase#AIRBASE> Airbases Map of Airbases. diff --git a/Moose Development/Moose/Functional/Designate.lua b/Moose Development/Moose/Functional/Designate.lua index a633a8801..dce33f924 100644 --- a/Moose Development/Moose/Functional/Designate.lua +++ b/Moose Development/Moose/Functional/Designate.lua @@ -28,7 +28,7 @@ -- -- * **FlightControl**: Design & Programming -- --- @module Designate +-- @module Functional.Designate do -- DESIGNATE diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 2c5d61a8a..93755e614 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -29,7 +29,7 @@ -- -- * FlightControl : Analysis, Design, Programming, Testing -- --- @module Detection +-- @module Functional.Detection ----BASE:TraceClass("DETECTION_BASE") ----BASE:TraceClass("DETECTION_AREAS") diff --git a/Moose Development/Moose/Functional/Escort.lua b/Moose Development/Moose/Functional/Escort.lua index 25ecced57..755770c84 100644 --- a/Moose Development/Moose/Functional/Escort.lua +++ b/Moose Development/Moose/Functional/Escort.lua @@ -114,8 +114,7 @@ -- -- -- --- @module Escort --- @author FlightControl +-- @module Functional.Escort --- ESCORT class -- @type ESCORT diff --git a/Moose Development/Moose/Functional/MissileTrainer.lua b/Moose Development/Moose/Functional/MissileTrainer.lua index abaa15d09..151964515 100644 --- a/Moose Development/Moose/Functional/MissileTrainer.lua +++ b/Moose Development/Moose/Functional/MissileTrainer.lua @@ -77,8 +77,7 @@ -- Danny has shared his ideas and together we made a design. -- Together with the **476 virtual team**, we tested the MISSILETRAINER class, and got much positive feedback! -- --- @module MissileTrainer --- @author FlightControl +-- @module Functional.MissileTrainer --- The MISSILETRAINER class diff --git a/Moose Development/Moose/Functional/Movement.lua b/Moose Development/Moose/Functional/Movement.lua index a5d4d7058..8aabf0130 100644 --- a/Moose Development/Moose/Functional/Movement.lua +++ b/Moose Development/Moose/Functional/Movement.lua @@ -7,7 +7,7 @@ -- Performance: If in a DCSRTE there are a lot of moving GROUND units, then in a multi player mission, this WILL create lag if -- the main DCS execution core of your CPU is fully utilized. So, this class will limit the amount of simultaneous moving GROUND units -- on defined intervals (currently every minute). --- @module Movement +-- @module Functional.Movement --- the MOVEMENT class -- @type MOVEMENT diff --git a/Moose Development/Moose/Functional/Protect.lua b/Moose Development/Moose/Functional/Protect.lua index 91c6009f3..00645edbe 100644 --- a/Moose Development/Moose/Functional/Protect.lua +++ b/Moose Development/Moose/Functional/Protect.lua @@ -7,7 +7,7 @@ -- -- === -- --- @module Protect +-- @module Functional.Protect --- @type PROTECT.__ Methods which are not intended for mission designers, but which are used interally by the moose designer :-) -- @extends Core.Fsm#FSM diff --git a/Moose Development/Moose/Functional/PseudoATC.lua b/Moose Development/Moose/Functional/PseudoATC.lua index 78ed78f09..c1a639029 100644 --- a/Moose Development/Moose/Functional/PseudoATC.lua +++ b/Moose Development/Moose/Functional/PseudoATC.lua @@ -38,7 +38,7 @@ -- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536) -- -- ==== --- @module PseudoATC +-- @module Functional.PseudoATC ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- PSEUDOATC class diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 644dbdac0..31b3fc369 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -59,7 +59,7 @@ -- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536) -- -- === --- @module Rat +-- @module Functional.Rat ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- RAT class diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 706fe8665..175574bcf 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -48,7 +48,7 @@ -- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536), [Ciribob](https://forums.eagle.ru/member.php?u=112175) -- -- === --- @module Range +-- @module Functional.Range ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- RANGE class diff --git a/Moose Development/Moose/Functional/Scoring.lua b/Moose Development/Moose/Functional/Scoring.lua index 092d7998f..c6468a1e5 100644 --- a/Moose Development/Moose/Functional/Scoring.lua +++ b/Moose Development/Moose/Functional/Scoring.lua @@ -206,7 +206,7 @@ -- -- * **FlightControl**: Concept, Design & Programming. -- --- @module Scoring +-- @module Functional.Scoring --- The Scoring class diff --git a/Moose Development/Moose/Functional/Sead.lua b/Moose Development/Moose/Functional/Sead.lua index f1a18b8df..d9d9bfffb 100644 --- a/Moose Development/Moose/Functional/Sead.lua +++ b/Moose Development/Moose/Functional/Sead.lua @@ -2,7 +2,7 @@ -- -- === -- --- @module Sead +-- @module Functional.Sead --- The SEAD class -- @type SEAD diff --git a/Moose Development/Moose/Functional/Suppression.lua b/Moose Development/Moose/Functional/Suppression.lua index 41fb3dc09..929e8b718 100644 --- a/Moose Development/Moose/Functional/Suppression.lua +++ b/Moose Development/Moose/Functional/Suppression.lua @@ -31,7 +31,7 @@ -- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536) -- -- ==== --- @module Suppression +-- @module Functional.Suppression ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua b/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua index e093eb3ef..7c1610687 100644 --- a/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua +++ b/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua @@ -21,7 +21,7 @@ -- -- === -- --- @module ZoneCaptureCoalition +-- @module Functional.ZoneCaptureCoalition do -- ZONE_CAPTURE_COALITION diff --git a/Moose Development/Moose/Functional/ZoneGoal.lua b/Moose Development/Moose/Functional/ZoneGoal.lua index 9c3d22c36..60c2f6d25 100644 --- a/Moose Development/Moose/Functional/ZoneGoal.lua +++ b/Moose Development/Moose/Functional/ZoneGoal.lua @@ -11,7 +11,7 @@ -- -- === -- --- @module ZoneGoal +-- @module Functional.ZoneGoal do -- Zone diff --git a/Moose Development/Moose/Functional/ZoneGoalCargo.lua b/Moose Development/Moose/Functional/ZoneGoalCargo.lua index e5ea44310..5fc27acc6 100644 --- a/Moose Development/Moose/Functional/ZoneGoalCargo.lua +++ b/Moose Development/Moose/Functional/ZoneGoalCargo.lua @@ -11,7 +11,7 @@ -- -- === -- --- @module ZoneGoalCargo +-- @module Functional.ZoneGoalCargo do -- ZoneGoal diff --git a/Moose Development/Moose/Functional/ZoneGoalCoalition.lua b/Moose Development/Moose/Functional/ZoneGoalCoalition.lua index 91c7df0dc..b0b737518 100644 --- a/Moose Development/Moose/Functional/ZoneGoalCoalition.lua +++ b/Moose Development/Moose/Functional/ZoneGoalCoalition.lua @@ -11,7 +11,7 @@ -- -- === -- --- @module ZoneGoalCoalition +-- @module Functional.ZoneGoalCoalition do -- ZoneGoal diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index 4090980c6..725a634b3 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -9,7 +9,7 @@ -- -- === -- --- @module CommandCenter +-- @module Tasking.CommandCenter diff --git a/Moose Development/Moose/Tasking/DetectionManager.lua b/Moose Development/Moose/Tasking/DetectionManager.lua index 1c4f5492f..e6a6aaf19 100644 --- a/Moose Development/Moose/Tasking/DetectionManager.lua +++ b/Moose Development/Moose/Tasking/DetectionManager.lua @@ -40,7 +40,7 @@ -- ### Contributions: Mechanist, Prof_Hilactic, FlightControl - Concept & Testing -- ### Author: FlightControl - Framework Design & Programming -- --- @module DetectionManager +-- @module Tasking.DetectionManager do -- DETECTION MANAGER diff --git a/Moose Development/Moose/Tasking/Mission.lua b/Moose Development/Moose/Tasking/Mission.lua index 1ac4383f8..e541b7bbb 100644 --- a/Moose Development/Moose/Tasking/Mission.lua +++ b/Moose Development/Moose/Tasking/Mission.lua @@ -8,7 +8,7 @@ -- -- === -- --- @module Mission +-- @module Tasking.Mission --- The MISSION class -- @type MISSION diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index e68de90d3..9dd3da5bb 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -8,7 +8,7 @@ -- -- === -- --- @module Task +-- @module Tasking.Task --- @type TASK -- @field Core.Scheduler#SCHEDULER TaskScheduler diff --git a/Moose Development/Moose/Tasking/TaskInfo.lua b/Moose Development/Moose/Tasking/TaskInfo.lua index 723975838..349d090c1 100644 --- a/Moose Development/Moose/Tasking/TaskInfo.lua +++ b/Moose Development/Moose/Tasking/TaskInfo.lua @@ -8,7 +8,7 @@ -- -- === -- --- @module TaskInfo +-- @module Tasking.TaskInfo --- @type TASKINFO -- @extends Core.Base#BASE diff --git a/Moose Development/Moose/Tasking/TaskZoneCapture.lua b/Moose Development/Moose/Tasking/TaskZoneCapture.lua index 7de3f3e04..ad563c1d5 100644 --- a/Moose Development/Moose/Tasking/TaskZoneCapture.lua +++ b/Moose Development/Moose/Tasking/TaskZoneCapture.lua @@ -8,7 +8,7 @@ -- -- === -- --- @module TaskZoneCapture +-- @module Tasking.TaskZoneCapture do -- TASK_ZONE_GOAL diff --git a/Moose Development/Moose/Tasking/Task_A2A.lua b/Moose Development/Moose/Tasking/Task_A2A.lua index fff13b776..b3c268100 100644 --- a/Moose Development/Moose/Tasking/Task_A2A.lua +++ b/Moose Development/Moose/Tasking/Task_A2A.lua @@ -8,7 +8,7 @@ -- -- === -- --- @module Task_A2A +-- @module Tasking.Tasking.Task_A2A do -- TASK_A2A diff --git a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua index d3d0c8c39..3ca89bf8e 100644 --- a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua @@ -10,7 +10,7 @@ -- -- === -- --- @module Task_A2A_Dispatcher +-- @module Tasking.Task_A2A_Dispatcher do -- TASK_A2A_DISPATCHER diff --git a/Moose Development/Moose/Tasking/Task_A2G.lua b/Moose Development/Moose/Tasking/Task_A2G.lua index 15c33a5f8..59cef82ea 100644 --- a/Moose Development/Moose/Tasking/Task_A2G.lua +++ b/Moose Development/Moose/Tasking/Task_A2G.lua @@ -8,7 +8,7 @@ -- -- === -- --- @module Task_A2G +-- @module Tasking.Task_A2G do -- TASK_A2G diff --git a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua index a79825a6b..65d1796b7 100644 --- a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua @@ -8,7 +8,7 @@ -- -- === -- --- @module Task_A2G_Dispatcher +-- @module Tasking.Task_A2G_Dispatcher do -- TASK_A2G_DISPATCHER diff --git a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua index b033ee17c..1326cec30 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua @@ -3,6 +3,8 @@ -- ![Banner Image](..\Presentations\TASK_CARGO\Dia1.JPG) -- -- === +-- +-- @module Tasking.Task_Cargo_CSAR do -- TASK_CARGO_CSAR diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index 9671798d6..9603f5f4f 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -8,7 +8,7 @@ -- -- === -- --- @module Task_Cargo_Dispatcher +-- @module Tasking.Task_Cargo_Dispatcher do -- TASK_CARGO_DISPATCHER diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua index 9dac77e5d..f906b87f8 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua @@ -3,7 +3,7 @@ -- ![Banner Image](..\Presentations\TASK_CARGO\Dia1.JPG) -- -- === --- @module +-- @module Tasking.Task_Cargo_Transport do -- TASK_CARGO_TRANSPORT diff --git a/Moose Development/Moose/Tasking/Task_Manager.lua b/Moose Development/Moose/Tasking/Task_Manager.lua index 7788fb9d1..c82fa505e 100644 --- a/Moose Development/Moose/Tasking/Task_Manager.lua +++ b/Moose Development/Moose/Tasking/Task_Manager.lua @@ -29,7 +29,7 @@ -- ### Contributions: Mechanist, Prof_Hilactic, FlightControl - Concept & Testing -- ### Author: FlightControl - Framework Design & Programming -- --- @module Task_Manager +-- @module Tasking.Task_Manager do -- TASK_MANAGER diff --git a/Moose Development/Moose/Tasking/Task_Pickup.lua b/Moose Development/Moose/Tasking/Task_Pickup.lua deleted file mode 100644 index b33acedac..000000000 --- a/Moose Development/Moose/Tasking/Task_Pickup.lua +++ /dev/null @@ -1,132 +0,0 @@ ---- This module contains the TASK_PICKUP classes. --- --- 1) @{#TASK_PICKUP} class, extends @{Task#TASK} --- === --- The @{#TASK_PICKUP} class defines a pickup task of a @{Set} of @{CARGO} objects defined within the mission. --- based on the tasking capabilities defined in @{Task#TASK}. --- The TASK_PICKUP is implemented using a @{Statemachine#FSM_TASK}, and has the following statuses: --- --- * **None**: Start of the process --- * **Planned**: The SEAD task is planned. Upon Planned, the sub-process @{Process_Fsm.Assign#ACT_ASSIGN_ACCEPT} is started to accept the task. --- * **Assigned**: The SEAD task is assigned to a @{Wrapper.Group#GROUP}. Upon Assigned, the sub-process @{Process_Fsm.Route#ACT_ROUTE} is started to route the active Units in the Group to the attack zone. --- * **Success**: The SEAD task is successfully completed. Upon Success, the sub-process @{Process_SEAD#PROCESS_SEAD} is started to follow-up successful SEADing of the targets assigned in the task. --- * **Failed**: The SEAD task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. --- --- === --- --- ### Authors: FlightControl - Design and Programming --- --- @module Task_PICKUP - - -do -- TASK_PICKUP - - --- The TASK_PICKUP class - -- @type TASK_PICKUP - -- @extends Tasking.Task#TASK - TASK_PICKUP = { - ClassName = "TASK_PICKUP", - } - - --- Instantiates a new TASK_PICKUP. - -- @param #TASK_PICKUP self - -- @param Tasking.Mission#MISSION Mission - -- @param Core.Set#SET_GROUP AssignedSetGroup The set of groups for which the Task can be assigned. - -- @param #string TaskName The name of the Task. - -- @param #string TaskType BAI or CAS - -- @param Core.Set#SET_UNIT UnitSetTargets - -- @param Core.Zone#ZONE_BASE TargetZone - -- @return #TASK_PICKUP self - function TASK_PICKUP:New( Mission, AssignedSetGroup, TaskName, TaskType ) - local self = BASE:Inherit( self, TASK:New( Mission, AssignedSetGroup, TaskName, TaskType, "PICKUP" ) ) - self:F() - - return self - end - - --- Removes a TASK_PICKUP. - -- @param #TASK_PICKUP self - -- @return #nil - function TASK_PICKUP:CleanUp() - - self:GetParent( self ):CleanUp() - - return nil - end - - - --- Assign the @{Task} to a @{Wrapper.Unit}. - -- @param #TASK_PICKUP self - -- @param Wrapper.Unit#UNIT TaskUnit - -- @return #TASK_PICKUP self - function TASK_PICKUP:AssignToUnit( TaskUnit ) - self:F( TaskUnit:GetName() ) - - local ProcessAssign = self:AddProcess( TaskUnit, ACT_ASSIGN_ACCEPT:New( self, TaskUnit, self.TaskBriefing ) ) - local ProcessPickup = self:AddProcess( TaskUnit, PROCESS_PICKUP:New( self, self.TaskType, TaskUnit ) ) - - local Process = self:AddStateMachine( TaskUnit, FSM_TASK:New( self, TaskUnit, { - initial = 'None', - events = { - { name = 'Next', from = 'None', to = 'Planned' }, - { name = 'Next', from = 'Planned', to = 'Assigned' }, - { name = 'Next', from = 'Assigned', to = 'Success' }, - { name = 'Fail', from = 'Assigned', to = 'Failed' }, - }, - callbacks = { - onNext = self.OnNext, - }, - subs = { - Assign = { onstateparent = 'Planned', oneventparent = 'Next', fsm = ProcessAssign.Fsm, event = 'Start', returnevents = { 'Next', 'Reject' } }, - Pickup = { onstateparent = 'Assigned', oneventparent = 'Next', fsm = ProcessDestroy.Fsm, event = 'Start', returnevents = { 'Next' } }, - } - } ) ) - - ProcessRoute:AddScore( "Failed", "failed to destroy a ground unit", -100 ) - ProcessDestroy:AddScore( "Pickup", "Picked-Up a Cargo", 25 ) - ProcessDestroy:AddScore( "Failed", "failed to destroy a ground unit", -100 ) - - Process:Next() - - return self - end - - --- StateMachine callback function for a TASK - -- @param #TASK_PICKUP self - -- @param Core.Fsm#FSM_TASK Fsm - -- @param #string Event - -- @param #string From - -- @param #string To - -- @param Core.Event#EVENTDATA Event - function TASK_PICKUP:OnNext( Fsm, From, Event, To, Event ) - - self:SetState( self, "State", To ) - - end - - --- @param #TASK_PICKUP self - function TASK_PICKUP:GetPlannedMenuText() - return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.TargetSetUnit:GetUnitTypesText() .. " )" - end - - - --- @param #TASK_PICKUP self - function TASK_PICKUP:_Schedule() - self:F2() - - self.TaskScheduler = SCHEDULER:New( self, _Scheduler, {}, 15, 15 ) - return self - end - - - --- @param #TASK_PICKUP self - function TASK_PICKUP._Scheduler() - self:F2() - - return true - end - -end - - - From 369ea08fd10935a942c38ac7dbb532fef79463ee Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 24 May 2018 19:36:53 +0200 Subject: [PATCH 129/420] Minor changes ARTY corrected MISSILE category. --- Moose Development/Moose/Core/Zone.lua | 5 +- .../Moose/Functional/Artillery.lua | 48 ++++++++++++++++--- Moose Development/Moose/Functional/Range.lua | 2 +- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index a8dc2f641..fafde4e43 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -431,8 +431,9 @@ end --- Bounds the zone with tires. -- @param #ZONE_RADIUS self --- @param #number Points (optional) The amount of points in the circle. --- @param #boolean UnBound If true the tyres will be destroyed. +-- @param #number Points (optional) The amount of points in the circle. Default 360. +-- @param Dcs.DCScountry#country.id CountryID The country id of the tire objects, e.g. country.id.USA for blue or country.id.RUSSIA for red. +-- @param #boolean UnBound (Optional) If true the tyres will be destroyed. -- @return #ZONE_RADIUS self function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound ) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index c38ee1fad..b14699798 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -449,6 +449,8 @@ function ARTY:New(group) local DCSunit=DCSgroup:getUnit(1) self.DCSdesc=DCSunit:getDesc() + --self.DCSdesc=group:GetDesc() + -- DCS descriptors. self:T3(ARTY.id.."DCS descriptors for group "..group:GetName()) for id,desc in pairs(self.DCSdesc) do @@ -456,7 +458,8 @@ function ARTY:New(group) end -- Maximum speed in km/h. - self.SpeedMax=self.DCSdesc.speedMax*3.6 + self.SpeedMax=group:GetSpeedMax() + --self.SpeedMax=self.DCSdesc.speedMax*3.6 -- Set speed to 0.7 of maximum. self.Speed=self.SpeedMax * 0.7 @@ -517,7 +520,7 @@ end --- Assign target coordinates to the ARTY group. Only the first parameter, i.e. the coordinate of the target is mandatory. The remaining parameters are optional and can be used to fine tune the engagement. -- @param #ARTY self --- @param Wrapper.Point#COORDINATE coord Coordinates of the target. +-- @param Core.Point#COORDINATE coord Coordinates of the target. -- @param #number prio (Optional) Priority of target. Number between 1 (high) and 100 (low). Default 50. -- @param #number radius (Optional) Radius. Default is 100 m. -- @param #number nshells (Optional) How many shells (or rockets) are fired on target per engagement. Default 5. @@ -544,6 +547,29 @@ function ARTY:AssignTargetCoord(coord, prio, radius, nshells, maxengage, time, w unique=false end weapontype=weapontype or ARTY.WeaponType.Auto + + -- Check if we have a coordinate object. + local text=nil + if coord:IsInstanceOf("GROUP") then + text="WARNING: ARTY:AssignTargetCoordinate(coord, ...) needs a COORDINATE object as first parameter - you gave a GROUP. Converting to COORDINATE..." + coord=coord:GetCoordinate() + elseif coord:IsInstanceOf("UNIT") then + text="WARNING: ARTY:AssignTargetCoordinate(coord, ...) needs a COORDINATE object as first parameter - you gave a UNIT. Converting to COORDINATE..." + coord=coord:GetCoordinate() + elseif coord:IsInstanceOf("POSITIONABLE") then + text="WARNING: ARTY:AssignTargetCoordinate(coord, ...) needs a COORDINATE object as first parameter - you gave a POSITIONABLE. Converting to COORDINATE..." + coord=coord:GetCoordinate() + elseif coord:IsInstanceOf("COORDINATE") then + -- Nothing to do here. + else + text="ERROR: ARTY:AssignTargetCoordinate(coord, ...) needs a COORDINATE object as first parameter!" + MESSAGE:New(text, 30):ToAll() + self:E(ARTY.id..text) + return nil + end + if text~=nil then + self:E(ARTY.id..text) + end -- Name of the target. local _name=name or coord:ToStringLLDMS() @@ -575,7 +601,7 @@ end --- Assign coordinate to where the ARTY group should move. -- @param #ARTY self --- @param Wrapper.Point#COORDINATE coord Coordinates of the target. +-- @param Core.Point#COORDINATE coord Coordinates of the target. -- @param #string time (Optional) Day time at which the group should start moving. Passed as a string in format "08:13:45". -- @param #number speed (Optinal) Speed in km/h the group should move at. Default 50 km/h. -- @param #boolean onroad (Optional) If true, group will mainly use roads. Default off, i.e. go directly towards the specified coordinate. @@ -698,7 +724,7 @@ end --- Defines the rearming place of the ARTY group. If the place is too far away from the ARTY group it will be routed to the place. -- @param #ARTY self --- @param Wrapper.Point#COORDINATE coord Coordinates of the rearming place. +-- @param Core.Point#COORDINATE coord Coordinates of the rearming place. function ARTY:SetRearmingPlace(coord) self:F({coord=coord}) self.RearmingPlaceCoord=coord @@ -848,6 +874,10 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("Targets:\n") for _, target in pairs(self.targets) do text=text..string.format("- %s\n", self:_TargetInfo(target)) + if self.Debug then + local zone=ZONE_RADIUS:New(target.name, target.coord:GetVec2(), target.radius) + zone:BoundZone(180, coalition.side.NEUTRAL) + end end text=text..string.format("Moves:\n") for i=1,#self.moves do @@ -867,7 +897,11 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("- %s\n", _type) end text=text..string.format("******************************************************") - self:T(ARTY.id..text) + if self.Debug then + self:E(ARTY.id..text) + else + self:T(ARTY.id..text) + end -- Add event handler. self:HandleEvent(EVENTS.Shot, self._OnEventShot) @@ -1510,7 +1544,7 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param Wrapper.Point#COORDINATE ToCoord Coordinate to which the ARTY group should move. +-- @param Core.Point#COORDINATE ToCoord Coordinate to which the ARTY group should move. -- @param #boolean OnRoad If true group should move on road mainly. -- @return #boolean If true, proceed to onafterMove. function ARTY:onbeforeMove(Controllable, From, Event, To, ToCoord, OnRoad) @@ -1961,7 +1995,7 @@ function ARTY:GetAmmo(display) end end else - if Category==Weapon.Category.ROCKET then + if Category==Weapon.Category.MISSILE then _gotmissile=true end end diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index bce0b8ef1..f815a6520 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -314,7 +314,7 @@ function RANGE:New(rangename) self.rangename=rangename or "Practice Range" -- Debug info. - local text=string.format("RANGE script version %s. Creating new RANGE object. Range name: %s.", RANGE.version, self.rangename) + local text=string.format("RANGE script version %s - creating new RANGE object of name: %s.", RANGE.version, self.rangename) self:E(RANGE.id..text) MESSAGE:New(text, 10):ToAllIf(self.Debug) From 11516228faf4f7eead6486f3c2884dfb10ff5bce Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sat, 26 May 2018 10:56:22 +0200 Subject: [PATCH 130/420] First images --- Moose Development/Moose/AI/AI_A2A.lua | 7 ++----- Moose Development/Moose/AI/AI_A2A_Cap.lua | 9 ++------- .../Moose/AI/AI_A2A_Dispatcher.lua | 19 +++---------------- Moose Development/Moose/AI/AI_A2A_Patrol.lua | 3 +-- Moose Development/Moose/AI/AI_BAI.lua | 9 ++------- Moose Development/Moose/AI/AI_Balancer.lua | 5 +---- Moose Development/Moose/AI/AI_CAP.lua | 5 +---- .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 9 ++------- 8 files changed, 14 insertions(+), 52 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A.lua b/Moose Development/Moose/AI/AI_A2A.lua index 22507ffb3..10f7507cd 100644 --- a/Moose Development/Moose/AI/AI_A2A.lua +++ b/Moose Development/Moose/AI/AI_A2A.lua @@ -1,7 +1,5 @@ --- **AI** -- (R2.2) - Models the process of air operations for airplanes. -- --- This is a class used in the @{AI_A2A_Dispatcher}. --- -- === -- -- ### Author: **FlightControl** @@ -9,6 +7,7 @@ -- === -- -- @module AI.AI_A2A +-- @image Air_To_Air_Dispatching.JPG --BASE:TraceClass("AI_A2A") @@ -16,9 +15,7 @@ --- @type AI_A2A -- @extends Core.Fsm#FSM_CONTROLLABLE ---- # AI_A2A class, extends @{Fsm#FSM_CONTROLLABLE} --- --- The AI_A2A class implements the core functions to operate an AI @{Wrapper.Group} A2A tasking. +--- The AI_A2A class implements the core functions to operate an AI @{Wrapper.Group} A2A tasking. -- -- -- ## AI_A2A constructor diff --git a/Moose Development/Moose/AI/AI_A2A_Cap.lua b/Moose Development/Moose/AI/AI_A2A_Cap.lua index 0451051a2..50cdf48bd 100644 --- a/Moose Development/Moose/AI/AI_A2A_Cap.lua +++ b/Moose Development/Moose/AI/AI_A2A_Cap.lua @@ -1,7 +1,5 @@ --- **AI** -- (R2.2) - Models the process of Combat Air Patrol (CAP) for airplanes. -- --- This is a class used in the @{AI.AI_A2A_Dispatcher}. --- -- === -- -- ### Author: **FlightControl** @@ -9,16 +7,13 @@ -- === -- -- @module AI.AI_A2A_Cap - ---BASE:TraceClass("AI_A2A_CAP") +-- @image Combat_Air_Patrol.JPG --- @type AI_A2A_CAP -- @extends AI.AI_A2A_Patrol#AI_A2A_PATROL ---- # AI_A2A_CAP class, extends @{AI.AI_A2A_Patrol#AI_A2A_PATROL} --- --- The AI_A2A_CAP class implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group} +--- The AI_A2A_CAP class implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group} -- and automatically engage any airborne enemies that are within a certain range or within a certain zone. -- -- ![Process](..\Presentations\AI_CAP\Dia3.JPG) diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index 047922014..0edff73b5 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -1,11 +1,5 @@ --- **AI** - (R2.2) - Manages the process of an automatic A2A defense system based on an EWR network targets and coordinating CAP and GCI. -- --- === --- --- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia1.JPG) --- --- === --- -- # QUICK START GUIDE -- -- There are basically two classes available to model an A2A defense system. @@ -156,6 +150,7 @@ -- ### Authors: **Stonehouse**, **SNAFU** in terms of the advice, documentation, and the original GCICAP script. -- -- @module AI.AI_A2A_Dispatcher +-- @image Air_To_Air_Dispatching.JPG @@ -165,11 +160,7 @@ do -- AI_A2A_DISPATCHER -- @type AI_A2A_DISPATCHER -- @extends Tasking.DetectionManager#DETECTION_MANAGER - --- # AI\_A2A\_DISPATCHER class, extends @{Tasking.DetectionManage#DETECTION_MANAGER} - -- - -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia1.JPG) - -- - -- The @{#AI_A2A_DISPATCHER} class is designed to create an automatic air defence system for a coalition. + --- Create an automatic air defence system for a coalition. -- -- === -- @@ -3173,11 +3164,7 @@ do --- @type AI_A2A_GCICAP -- @extends #AI_A2A_DISPATCHER - --- # AI\_A2A\_GCICAP class, extends @{AI_A2A_Dispatcher#AI_A2A_DISPATCHER} - -- - -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia1.JPG) - -- - -- The AI_A2A_GCICAP class is designed to create an automatic air defence system for a coalition setting up GCI and CAP air defenses. + --- Create an automatic air defence system for a coalition setting up GCI and CAP air defenses. -- The class derives from @{AI#AI_A2A_DISPATCHER} and thus, all the methods that are defined in the @{AI#AI_A2A_DISPATCHER} class, can be used also in AI\_A2A\_GCICAP. -- -- === diff --git a/Moose Development/Moose/AI/AI_A2A_Patrol.lua b/Moose Development/Moose/AI/AI_A2A_Patrol.lua index e741e9fba..ab97e5942 100644 --- a/Moose Development/Moose/AI/AI_A2A_Patrol.lua +++ b/Moose Development/Moose/AI/AI_A2A_Patrol.lua @@ -1,7 +1,5 @@ --- **AI** -- (R2.2) - Models the process of air patrol of airplanes. -- --- This is a class used in the @{AI_A2A_Dispatcher}. --- -- === -- -- ### Author: **FlightControl** @@ -9,6 +7,7 @@ -- === -- -- @module AI.AI_A2A_Patrol +-- @image Air_Patrolling.JPG --- @type AI_A2A_PATROL diff --git a/Moose Development/Moose/AI/AI_BAI.lua b/Moose Development/Moose/AI/AI_BAI.lua index 5d8ed3df6..160516b8b 100644 --- a/Moose Development/Moose/AI/AI_BAI.lua +++ b/Moose Development/Moose/AI/AI_BAI.lua @@ -1,10 +1,6 @@ --- **AI** -- (R2.1) - Manages the independent process of Battlefield Air Interdiction (bombing) for airplanes. -- -- === --- --- ![Banner Image](..\Presentations\AI_BAI\Dia1.JPG) --- --- === -- -- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/BAI%20-%20Battlefield%20Air%20Interdiction) -- @@ -22,6 +18,7 @@ -- === -- -- @module AI.AI_Bai +-- @image Battlefield_Air_Interdiction.JPG --- AI_BAI_ZONE class @@ -30,9 +27,7 @@ -- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. -- @extends AI.AI_Patrol#AI_PATROL_ZONE ---- # AI_BAI_ZONE class, extends @{AI.AI_Patrol#AI_PATROL_ZONE} --- --- AI_BAI_ZONE derives from the @{AI.AI_Patrol#AI_PATROL_ZONE}, inheriting its methods and behaviour. +--- AI_BAI_ZONE derives from the @{AI.AI_Patrol#AI_PATROL_ZONE}, inheriting its methods and behaviour. -- -- The AI_BAI_ZONE class implements the core functions to provide BattleGround Air Interdiction in an Engage @{Zone} by an AIR @{Wrapper.Controllable} or @{Wrapper.Group}. -- The AI_BAI_ZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone. diff --git a/Moose Development/Moose/AI/AI_Balancer.lua b/Moose Development/Moose/AI/AI_Balancer.lua index e14ec568f..2648b4760 100644 --- a/Moose Development/Moose/AI/AI_Balancer.lua +++ b/Moose Development/Moose/AI/AI_Balancer.lua @@ -2,10 +2,6 @@ -- -- === -- --- ![Banner Image](..\Presentations\AI_Balancer\Dia1.JPG) --- --- === --- -- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/AIB%20-%20AI%20Balancing) -- -- === @@ -22,6 +18,7 @@ -- === -- -- @module AI.AI_Balancer +-- @image AI_Balancing.JPG --- @type AI_BALANCER -- @field Core.Set#SET_CLIENT SetClient diff --git a/Moose Development/Moose/AI/AI_CAP.lua b/Moose Development/Moose/AI/AI_CAP.lua index 9df9ddf8e..033baa443 100644 --- a/Moose Development/Moose/AI/AI_CAP.lua +++ b/Moose Development/Moose/AI/AI_CAP.lua @@ -1,10 +1,6 @@ --- **AI** -- (R2.1) - Manages the independent process of Combat Air Patrol (CAP) for airplanes. -- -- === --- --- ![Banner Image](..\Presentations\AI_CAP\Dia1.JPG) --- --- === -- -- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAP%20-%20Combat%20Air%20Patrol) -- @@ -26,6 +22,7 @@ -- === -- -- @module AI.AI_Cap +-- @image Combat_Air_Patrol.JPG --- @type AI_CAP_ZONE diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index b2808924a..a78ad4ff4 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -7,18 +7,13 @@ -- === -- -- @module AI.AI_Cargo_Dispatcher_APC +-- @image Cargo_Dispatching_For_APC.JPG --- @type AI_CARGO_DISPATCHER_APC -- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER ---- # AI\_CARGO\_DISPATCHER\_APC class, extends @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} --- --- ![Banner Image](..\Presentations\AI_CARGO_DISPATCHER_APC\Dia1.JPG) --- --- === --- --- AI\_CARGO\_DISPATCHER\_APC brings a dynamic cargo handling capability for AI groups. +--- A dynamic cargo transportation capability for AI groups. -- -- Armoured Personnel APCs (APC), Trucks, Jeeps and other carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. -- The AI\_CARGO\_DISPATCHER\_APC module uses the @{Cargo} capabilities within the MOOSE framework. From ad75a7ddb5ebb15fc4667f5243df92a39bbea466 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sat, 26 May 2018 14:46:23 +0200 Subject: [PATCH 131/420] RANGE v1.2 and other changes RANGE: -Optimized performance. Bombs are now only tracked if the player is within a certain distance of the range. CONTROLLABLE: - Added speed unit to @param description. Sometimes it was unclear if speed needs to be given in m/s or km/h. - Fixed some default speed and conversions. COORDINATE: - Cleaned up some speed unit stuff. - Reintroduced PathOnRoad function to contain the full path. Useful as interface to DCS API function. - Fixed some default speed and conversions. AI_CARGO_APC: Speed is not fixed any more but set to 50% of the max speed a given unit can move at. --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 11 +- Moose Development/Moose/Core/Point.lua | 30 ++- .../Moose/Functional/Artillery.lua | 16 +- Moose Development/Moose/Functional/Range.lua | 224 ++++++++++-------- .../Moose/Wrapper/Controllable.lua | 121 +++++----- 5 files changed, 212 insertions(+), 190 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 51f94e4c9..9fbf1566a 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -600,8 +600,6 @@ end -- @param Event -- @param To -- @param Core.Point#COORDINATE Coordinate --- @param #number Speed --- @param #string EndPointFormation The formation at the end point of the action. function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate ) if APC and APC:IsAlive() then @@ -609,7 +607,7 @@ function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate ) if Coordinate then self.RoutePickup = true - local Waypoints = APC:TaskGroundOnRoad( Coordinate, APC:GetSpeedMax()*0.8, "Line abreast" ) + local Waypoints = APC:TaskGroundOnRoad( Coordinate, APC:GetSpeedMax()*0.5, "Line abreast" ) local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Pickup", self ) @@ -634,15 +632,13 @@ end -- @param Event -- @param To -- @param Core.Point#COORDINATE Coordinate --- @param #number Speed --- @param #string EndPointFormation The formation at the end point of the action. function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate ) if APC and APC:IsAlive() then self.RouteDeploy = true - local Waypoints = APC:TaskGroundOnRoad( Coordinate, 150, "Line abreast" ) + local Waypoints = APC:TaskGroundOnRoad( Coordinate, APC:GetSpeedMax()*0.5, "Line abreast" ) local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Deploy", self ) @@ -662,14 +658,13 @@ end -- @param Event -- @param To -- @param Core.Point#COORDINATE Coordinate --- @param #number Speed function AI_CARGO_APC:onafterHome( APC, From, Event, To, Coordinate ) if APC and APC:IsAlive() ~= nil then self.RouteHome = true - local Waypoints = APC:TaskGroundOnRoad( Coordinate, 120, "Line abreast" ) + local Waypoints = APC:TaskGroundOnRoad( Coordinate, APC:GetSpeedMax()*0.5, "Line abreast" ) self:F({Waypoints = Waypoints}) local Waypoint = Waypoints[#Waypoints] diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index cd8390f7b..286e4f79f 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -777,7 +777,7 @@ do -- COORDINATE -- @param #COORDINATE.WaypointAltType AltType The altitude type. -- @param #COORDINATE.WaypointType Type The route point type. -- @param #COORDINATE.WaypointAction Action The route point action. - -- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h. + -- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h. Default is 500 km/h. -- @param #boolean SpeedLocked true means the speed is locked. -- @return #table The route point. function COORDINATE:WaypointAir( AltType, Type, Action, Speed, SpeedLocked ) @@ -887,7 +887,7 @@ do -- COORDINATE --- Build an ground type route point. -- @param #COORDINATE self - -- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h. + -- @param #number Speed (optional) Speed in km/h. The default speed is 20 km/h. -- @param #string Formation (optional) The route point Formation, which is a text string that specifies exactly the Text in the Type of the route point, like "Vee", "Echelon Right". -- @return #table The route point. function COORDINATE:WaypointGround( Speed, Formation ) @@ -935,22 +935,30 @@ do -- COORDINATE return COORDINATE:NewFromVec2(vec2) end - --- Returns a table of coordinates to a destination. + --- Returns a table of coordinates to a destination using only roads. + -- The first point is the closest point on road of the given coordinate. The last point is the closest point on road of the ToCoord. Hence, the coordinate itself and the final ToCoord are not necessarily included in the path. -- @param #COORDINATE self -- @param #COORDINATE ToCoord Coordinate of destination. -- @return #table Table of coordinates on road. function COORDINATE:GetPathOnRoad(ToCoord) - local Path={} + + -- DCS API function returning a table of vec2. local path = land.findPathOnRoads("roads", self.x, self.z, ToCoord.x, ToCoord.z) - Path[#Path+1]=COORDINATE:NewFromVec2(path[1]) - Path[#Path+1]=COORDINATE:NewFromVec2(path[#path]) + + --Path[#Path+1]=COORDINATE:NewFromVec2(path[1]) + --Path[#Path+1]=COORDINATE:NewFromVec2(path[#path]) + --Path[#Path+1]=self:GetClosestPointToRoad() + --Path[#Path+1]=ToCoord:GetClosestPointToRoad() -- I've removed this stuff because it severely slows down DCS in case of paths with a lot of segments. -- Just the beginning and the end point is sufficient. --- for i, v in ipairs(path) do --- self:I(v) --- local coord=COORDINATE:NewFromVec2(v) --- Path[#Path+1]=COORDINATE:NewFromVec2(v) --- end + + local Path={} + --Path[#Path+1]=self + for i, v in ipairs(path) do + Path[#Path+1]=COORDINATE:NewFromVec2(v) + end + --Path[#Path+1]=ToCoord + return Path end diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 1921787ae..f8a210b8e 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -2434,16 +2434,18 @@ function ARTY:_Move(group, ToCoord, Speed, OnRoad) -- Route group on road if requested. if OnRoad then + -- Path on road (only first and last points) local _first=cpini:GetClosestPointToRoad() local _last=ToCoord:GetClosestPointToRoad() - local _onroad=_first:GetPathOnRoad(_last) - -- Points on road. - for i=1,#_onroad do - path[#path+1]=_onroad[i]:WaypointGround(Speed, "On road") - task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, false) - end - + -- First point on road. + path[#path+1]=_first:WaypointGround(Speed, "On road") + task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, false) + + -- Last point on road. + path[#path+1]=_last:WaypointGround(Speed, "On road") + task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, false) + end -- Last waypoint at ToCoord. diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 65614ebf5..c21f6656f 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -70,6 +70,7 @@ -- @field #table bombPlayerResults Table containing the bombing results of each player. -- @field #table PlayerSettings Indiviual player settings. -- @field #number dtBombtrack Time step [sec] used for tracking released bomb/rocket positions. Default 0.005 seconds. +-- @field #number BombtrackThreshold Bombs/rockets/missiles are only tracked if player-range distance is smaller than this threashold [m]. Default 25000 m. -- @field #number Tmsg Time [sec] messages to players are displayed. Default 30 sec. -- @field #number strafemaxalt Maximum altitude above ground for registering for a strafe run. Default is 914 m = 3000 ft. -- @field #number ndisplayresult Number of (player) results that a displayed. Default is 10. @@ -236,6 +237,7 @@ RANGE={ bombPlayerResults = {}, PlayerSettings = {}, dtBombtrack=0.005, + BombtrackThreshold=25000, Tmsg=30, strafemaxalt=914, ndisplayresult=10, @@ -283,7 +285,7 @@ RANGE.id="RANGE | " --- Range script version. -- @field #number version -RANGE.version="1.1.1" +RANGE.version="1.2.0" --TODO list: --TODO: Add custom weapons, which can be specified by the user. @@ -451,6 +453,13 @@ function RANGE:SetRangeRadius(radius) self.rangeradius=radius*1000 or RANGE.Defaults.rangeradius end +--- Set bomb track threshold distance. Bombs/rockets/missiles are only tracked if player-range distance is less than this distance. Default 25 km. +-- @param #RANGE self +-- @param #number distance Threshold distance in km. Default 25 km. +function RANGE:SetBombtrackThreshold(distance) + self.BombtrackThreshold=distance*1000 or 25*1000 +end + --- Set range location. If this is not done, one (random) unit position of the range is used to determine the center of the range. -- @param #RANGE self -- @param Core.Point#COORDINATE coordinate Coordinate of the center of the range. @@ -1081,6 +1090,7 @@ function RANGE:OnEventShot(EventData) local _weaponName = _weaponStrArray[#_weaponStrArray] -- Debug info. + self:T(RANGE.id.."EVENT SHOT: Range "..self.rangename) self:T(RANGE.id.."EVENT SHOT: Ini unit = "..EventData.IniUnitName) self:T(RANGE.id.."EVENT SHOT: Ini group = "..EventData.IniGroupName) self:T(RANGE.id.."EVENT SHOT: Weapon type = ".._weapon) @@ -1097,129 +1107,141 @@ function RANGE:OnEventShot(EventData) -- Check if any condition applies here. local _track = (_bombs and self.trackbombs) or (_rockets and self.trackrockets) or (_missiles and self.trackmissiles) - if _track then + -- Get unit name. + local _unitName = EventData.IniUnitName + + -- Get player unit and name. + local _unit, _playername = self:_GetPlayerUnitAndName(_unitName) - -- Weapon - local _ordnance = EventData.weapon + -- Set this to larger value than the threshold. + local dPR=self.BombtrackThreshold*2 + + -- Distance player to range. + if _unit and _playername then + dPR=_unit:GetCoordinate():Get2DDistance(self.location) + self:T(RANGE.id..string.format("Range %s, player %s, player-range distance = %d km.", self.rangename, _playername, dPR/1000)) + end + + -- Only track if distance player to range is < 25 km. + if _track and dPR<=self.BombtrackThreshold then -- Tracking info and init of last bomb position. - self:T(RANGE.id..string.format("Tracking %s - %s.", _weapon, _ordnance:getName())) + self:T(RANGE.id..string.format("RANGE %s: Tracking %s - %s.", self.rangename, _weapon, EventData.weapon:getName())) -- Init bomb position. local _lastBombPos = {x=0,y=0,z=0} - - -- Get unit name. - local _unitName = EventData.IniUnitName -- Function monitoring the position of a bomb until impact. - local function trackBomb(_previousPos) + local function trackBomb(_ordnance) + + -- When the pcall returns a failure the weapon has hit. + local _status,_bombPos = pcall( + function() + return _ordnance:getPoint() + end) + + self:T3(RANGE.id..string.format("Range %s: Bomb still in air: %s", self.rangename, tostring(_status))) + if _status then - -- Get player unit and name. - local _unit, _playername = self:_GetPlayerUnitAndName(_unitName) - local _callsign=self:_myname(_unitName) + -- Still in the air. Remember this position. + _lastBombPos = {x = _bombPos.x, y = _bombPos.y, z= _bombPos.z } - if _unit and _playername then - - -- When the pcall returns a failure the weapon has hit. - local _status,_bombPos = pcall( - function() - return _ordnance:getPoint() - end) - - if _status then + -- Check again in 0.005 seconds. + return timer.getTime() + self.dtBombtrack - -- Still in the air. Remember this position. - _lastBombPos = {x = _bombPos.x, y = _bombPos.y, z= _bombPos.z } - - -- Check again in 0.005 seconds. - return timer.getTime() + self.dtBombtrack - - else + else + + -- Bomb did hit the ground. + -- Get closet target to last position. + local _closetTarget = nil + local _distance = nil + local _hitquality = "POOR" - -- Bomb did hit the ground. - -- Get closet target to last position. - local _closetTarget = nil - local _distance = nil - local _hitquality = "POOR" - - -- Coordinate of impact point. - local impactcoord=COORDINATE:NewFromVec3(_lastBombPos) - - -- Distance from range. We dont want to smoke targets outside of the range. - local impactdist=impactcoord:Get2DDistance(self.location) - - -- Smoke impact point of bomb. - if self.PlayerSettings[_playername].smokebombimpact and impactdist10 then - table.insert(route, ToCoordinate:WaypointGround(Speed, "Off Road")) - end + -- Defaults. + Speed=Speed or 20 + DelaySeconds=DelaySeconds or 1 + OffRoadFormation=OffRoadFormation or "Off Road" + + -- Get the route task. + local route=self:TaskGroundOnRoad(ToCoordinate, Speed, OffRoadFormation) -- Route controllable to destination. self:Route( route, DelaySeconds ) @@ -1978,39 +1969,43 @@ do -- Route methods end - --- Make a task for a GROUND Controllable to drive towards a specific point using (only) roads. + --- Make a task for a GROUND Controllable to drive towards a specific point using (mostly) roads. -- @param #CONTROLLABLE self -- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to. - -- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h. - -- @param #string EndPointFormation The formation to achieve at the end point. + -- @param #number Speed (Optional) Speed in km/h. The default speed is 20 km/h. + -- @param #string OffRoadFormation (Optional) The formation at initial and final waypoint. Default is "Off Road". -- @return Task - function CONTROLLABLE:TaskGroundOnRoad( ToCoordinate, Speed, EndPointFormation ) + function CONTROLLABLE:TaskGroundOnRoad( ToCoordinate, Speed, OffRoadFormation ) + self:F2({ToCoordinate=ToCoordinate, Speed=Speed, OffRoadFormation=OffRoadFormation}) + + -- Defaults. + Speed=Speed or 20 + OffRoadFormation=OffRoadFormation or "Off Road" -- Current coordinate. local FromCoordinate = self:GetCoordinate() - -- Formation is set to on road. - local Formation="On Road" - - -- Path on road from current position to destination coordinate. - local path=FromCoordinate:GetPathOnRoad( ToCoordinate ) + -- First point on road. + local FromOnRoad = FromCoordinate:GetClosestPointToRoad() - -- Route, ground waypoints along roads. - local Route = {} - table.insert( Route, FromCoordinate:WaypointGround( Speed, Formation ) ) + -- Last Point on road. + local ToOnRoad = ToCoordinate:GetClosestPointToRoad() + + -- Route, ground waypoints along road. + local route={} - -- Convert coordinates to ground waypoints and insert into table. - for _, coord in ipairs(path) do - table.insert( Route, coord:WaypointGround( Speed, Formation ) ) - end - - -- Add the final coordinate because the final coordinate in path is last point on road. - local dist=ToCoordinate:Get2DDistance(path[#path]) + -- Create waypoints. + table.insert(route, FromCoordinate:WaypointGround(Speed, OffRoadFormation)) + table.insert(route, FromOnRoad:WaypointGround(Speed, "On Road")) + table.insert(route, ToOnRoad:WaypointGround(Speed, "On Road")) + + -- Add the final coordinate because the final might not be on the road. + local dist=ToCoordinate:Get2DDistance(ToOnRoad) if dist>10 then - table.insert( Route, ToCoordinate:WaypointGround( Speed, EndPointFormation ) ) - end + table.insert(route, ToCoordinate:WaypointGround(Speed, OffRoadFormation)) + end - return Route + return route end @@ -2020,7 +2015,7 @@ do -- Route methods -- @param Core.Point#COORDINATE.RoutePointAltType AltType The altitude type. -- @param Core.Point#COORDINATE.RoutePointType Type The route point type. -- @param Core.Point#COORDINATE.RoutePointAction Action The route point action. - -- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h. + -- @param #number Speed (optional) Speed in km/h. The default speed is 500 km/h. -- @param #number DelaySeconds Wait for the specified seconds before executing the Route. -- @return #CONTROLLABLE The CONTROLLABLE. function CONTROLLABLE:RouteAirTo( ToCoordinate, AltType, Type, Action, Speed, DelaySeconds ) @@ -2043,7 +2038,7 @@ do -- Route methods -- @param #CONTROLLABLE self -- @param Core.Zone#ZONE Zone The zone where to route to. -- @param #boolean Randomize Defines whether to target point gets randomized within the Zone. - -- @param #number Speed The speed. + -- @param #number Speed The speed in m/s. Default is 5.555 m/s = 20 km/h. -- @param Base#FORMATION Formation The formation string. function CONTROLLABLE:TaskRouteToZone( Zone, Randomize, Speed, Formation ) self:F2( Zone ) @@ -2104,7 +2099,7 @@ do -- Route methods -- A given formation can be given. -- @param #CONTROLLABLE self -- @param #Vec2 Vec2 The Vec2 where to route to. - -- @param #number Speed The speed. + -- @param #number Speed The speed in m/s. Default is 5.555 m/s = 20 km/h. -- @param Base#FORMATION Formation The formation string. function CONTROLLABLE:TaskRouteToVec2( Vec2, Speed, Formation ) @@ -2119,7 +2114,7 @@ do -- Route methods PointFrom.y = ControllablePoint.y PointFrom.type = "Turning Point" PointFrom.action = Formation or "Cone" - PointFrom.speed = 20 / 1.6 + PointFrom.speed = 20 / 3.6 local PointTo = {} @@ -2137,7 +2132,7 @@ do -- Route methods if Speed then PointTo.speed = Speed else - PointTo.speed = 60 / 3.6 + PointTo.speed = 20 / 3.6 end local Points = { PointFrom, PointTo } From 073bfbf9c97a7382e55f2c91b9a24f07ec75efdc Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sun, 27 May 2018 08:57:44 +0200 Subject: [PATCH 132/420] Documentation Improvements --- Moose Development/Moose/AI/AI_A2A.lua | 2 +- Moose Development/Moose/AI/AI_A2A_Cap.lua | 2 +- .../Moose/AI/AI_A2A_Dispatcher.lua | 2 +- Moose Development/Moose/AI/AI_A2A_Gci.lua | 1 + Moose Development/Moose/AI/AI_A2A_Patrol.lua | 2 +- Moose Development/Moose/AI/AI_BAI.lua | 2 +- Moose Development/Moose/AI/AI_CAP.lua | 2 +- Moose Development/Moose/AI/AI_CAS.lua | 6 +- Moose Development/Moose/AI/AI_Cargo_APC.lua | 1 + .../Moose/AI/AI_Cargo_Airplane.lua | 1 + .../Moose/AI/AI_Cargo_Dispatcher.lua | 1 + .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 2 +- .../Moose/AI/AI_Cargo_Dispatcher_Airplane.lua | 3 +- .../AI/AI_Cargo_Dispatcher_Helicopter.lua | 1 + .../Moose/AI/AI_Cargo_Helicopter.lua | 1 + Moose Development/Moose/AI/AI_Formation.lua | 5 +- Moose Development/Moose/AI/AI_Patrol.lua | 6 +- Moose Development/Moose/Cargo/Cargo.lua | 10 +- Moose Development/Moose/Cargo/CargoCrate.lua | 5 +- Moose Development/Moose/Cargo/CargoGroup.lua | 1 + .../Moose/Cargo/CargoSlingload.lua | 5 +- Moose Development/Moose/Cargo/CargoUnit.lua | 5 +- Moose Development/Moose/Core/Base.lua | 9 +- Moose Development/Moose/Core/Database.lua | 5 +- Moose Development/Moose/Core/Event.lua | 3 +- Moose Development/Moose/Core/Fsm.lua | 23 +--- Moose Development/Moose/Core/Goal.lua | 5 +- Moose Development/Moose/Core/Menu.lua | 40 ++----- Moose Development/Moose/Core/Message.lua | 7 +- Moose Development/Moose/Core/Point.lua | 16 +-- Moose Development/Moose/Core/Radio.lua | 3 +- Moose Development/Moose/Core/Report.lua | 1 + .../Moose/Core/ScheduleDispatcher.lua | 1 + Moose Development/Moose/Core/Scheduler.lua | 8 +- Moose Development/Moose/Core/Set.lua | 108 ++++++++---------- Moose Development/Moose/Core/Settings.lua | 56 ++++----- Moose Development/Moose/Core/Spawn.lua | 11 +- Moose Development/Moose/Core/SpawnStatic.lua | 7 +- Moose Development/Moose/Core/Spot.lua | 7 +- Moose Development/Moose/Core/UserFlag.lua | 8 +- Moose Development/Moose/Core/UserSound.lua | 7 +- Moose Development/Moose/Core/Velocity.lua | 5 +- Moose Development/Moose/Core/Zone.lua | 31 ++--- 43 files changed, 151 insertions(+), 276 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A.lua b/Moose Development/Moose/AI/AI_A2A.lua index 10f7507cd..faaeb3745 100644 --- a/Moose Development/Moose/AI/AI_A2A.lua +++ b/Moose Development/Moose/AI/AI_A2A.lua @@ -7,7 +7,7 @@ -- === -- -- @module AI.AI_A2A --- @image Air_To_Air_Dispatching.JPG +-- @image AI_Air_To_Air_Dispatching.JPG --BASE:TraceClass("AI_A2A") diff --git a/Moose Development/Moose/AI/AI_A2A_Cap.lua b/Moose Development/Moose/AI/AI_A2A_Cap.lua index 50cdf48bd..5917ed848 100644 --- a/Moose Development/Moose/AI/AI_A2A_Cap.lua +++ b/Moose Development/Moose/AI/AI_A2A_Cap.lua @@ -7,7 +7,7 @@ -- === -- -- @module AI.AI_A2A_Cap --- @image Combat_Air_Patrol.JPG +-- @image AI_Combat_Air_Patrol.JPG --- @type AI_A2A_CAP -- @extends AI.AI_A2A_Patrol#AI_A2A_PATROL diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index 0edff73b5..146f35d4d 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -150,7 +150,7 @@ -- ### Authors: **Stonehouse**, **SNAFU** in terms of the advice, documentation, and the original GCICAP script. -- -- @module AI.AI_A2A_Dispatcher --- @image Air_To_Air_Dispatching.JPG +-- @image AI_Air_To_Air_Dispatching.JPG diff --git a/Moose Development/Moose/AI/AI_A2A_Gci.lua b/Moose Development/Moose/AI/AI_A2A_Gci.lua index 98670a381..5d2e2567f 100644 --- a/Moose Development/Moose/AI/AI_A2A_Gci.lua +++ b/Moose Development/Moose/AI/AI_A2A_Gci.lua @@ -9,6 +9,7 @@ -- === -- -- @module AI.AI_A2A_GCI +-- @image AI_AI_Ground_Control_Intercept.JPG diff --git a/Moose Development/Moose/AI/AI_A2A_Patrol.lua b/Moose Development/Moose/AI/AI_A2A_Patrol.lua index ab97e5942..73d814ef6 100644 --- a/Moose Development/Moose/AI/AI_A2A_Patrol.lua +++ b/Moose Development/Moose/AI/AI_A2A_Patrol.lua @@ -7,7 +7,7 @@ -- === -- -- @module AI.AI_A2A_Patrol --- @image Air_Patrolling.JPG +-- @image AI_Air_Patrolling.JPG --- @type AI_A2A_PATROL diff --git a/Moose Development/Moose/AI/AI_BAI.lua b/Moose Development/Moose/AI/AI_BAI.lua index 160516b8b..84ef87f92 100644 --- a/Moose Development/Moose/AI/AI_BAI.lua +++ b/Moose Development/Moose/AI/AI_BAI.lua @@ -18,7 +18,7 @@ -- === -- -- @module AI.AI_Bai --- @image Battlefield_Air_Interdiction.JPG +-- @image AI_Battlefield_Air_Interdiction.JPG --- AI_BAI_ZONE class diff --git a/Moose Development/Moose/AI/AI_CAP.lua b/Moose Development/Moose/AI/AI_CAP.lua index 033baa443..af783bc37 100644 --- a/Moose Development/Moose/AI/AI_CAP.lua +++ b/Moose Development/Moose/AI/AI_CAP.lua @@ -22,7 +22,7 @@ -- === -- -- @module AI.AI_Cap --- @image Combat_Air_Patrol.JPG +-- @image AI_Combat_Air_Patrol.JPG --- @type AI_CAP_ZONE diff --git a/Moose Development/Moose/AI/AI_CAS.lua b/Moose Development/Moose/AI/AI_CAS.lua index 74e18c3b5..a80e348ac 100644 --- a/Moose Development/Moose/AI/AI_CAS.lua +++ b/Moose Development/Moose/AI/AI_CAS.lua @@ -1,10 +1,6 @@ --- **AI** -- (R2.1) - Manages the independent process of Close Air Support for airplanes. -- -- === --- --- ![Banner Image](..\Presentations\AI_CAS\Dia1.JPG) --- --- === -- -- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release/CAS%20-%20Close%20Air%20Support) -- @@ -24,7 +20,7 @@ -- === -- -- @module AI.AI_Cas - +-- @image AI_Close_Air_Support.JPG --- AI_CAS_ZONE class -- @type AI_CAS_ZONE diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index db1a05e55..e0de55ecc 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -7,6 +7,7 @@ -- === -- -- @module AI.AI_Cargo_APC +-- @image AI_Cargo_Dispatching_For_APC.JPG --- @type AI_CARGO_APC -- @extends Core.Fsm#FSM_CONTROLLABLE diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index e216d0aff..ed10d2e5d 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -7,6 +7,7 @@ -- === -- -- @module AI.AI_Cargo_Airplane +-- @image AI_Cargo_Dispatching_For_Airplanes.JPG --- @type AI_CARGO_AIRPLANE -- @extends Core.Fsm#FSM_CONTROLLABLE diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index a3858a7c7..378c881fe 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -7,6 +7,7 @@ -- === -- -- @module AI.AI_Cargo_Dispatcher +-- @image AI_Cargo_Dispatching_For_Helicopters.JPG --- @type AI_CARGO_DISPATCHER -- @extends Core.Fsm#FSM diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index a78ad4ff4..cb400cecc 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -7,7 +7,7 @@ -- === -- -- @module AI.AI_Cargo_Dispatcher_APC --- @image Cargo_Dispatching_For_APC.JPG +-- @image AI_Cargo_Dispatching_For_APC.JPG --- @type AI_CARGO_DISPATCHER_APC -- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua index 6007e9973..409699733 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua @@ -7,7 +7,8 @@ -- === -- -- @module AI.AI_Cargo_Dispatcher_Airplane - +-- @image AI_Cargo_Dispatching_For_Airplanes.JPG +-- --- @type AI_CARGO_DISPATCHER_AIRPLANE -- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index aa3b264fc..bfa86e06e 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -9,6 +9,7 @@ -- === -- -- @module AI.AI_Cargo_Dispatcher_Helicopter +-- @image AI_Cargo_Dispatching_For_Helicopters.JPG --- @type AI_CARGO_DISPATCHER_HELICOPTER -- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 555cb858b..b0725cc0a 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -7,6 +7,7 @@ -- === -- -- @module AI.AI_Cargo_Helicopter +-- @image AI_Cargo_Dispatching_For_Helicopters.JPG --- @type AI_CARGO_HELICOPTER -- @extends Core.Fsm#FSM_CONTROLLABLE diff --git a/Moose Development/Moose/AI/AI_Formation.lua b/Moose Development/Moose/AI/AI_Formation.lua index db5e6edad..ebc5d2b6f 100644 --- a/Moose Development/Moose/AI/AI_Formation.lua +++ b/Moose Development/Moose/AI/AI_Formation.lua @@ -2,10 +2,6 @@ -- -- === -- --- ![Banner Image](..\Presentations\AI_FORMATION\Dia1.JPG) --- --- === --- -- AI_FORMATION makes AI @{Wrapper.Group}s fly in formation of various compositions. -- The AI_FORMATION class models formations in a different manner than the internal DCS formation logic!!! -- The purpose of the class is to: @@ -46,6 +42,7 @@ -- === -- -- @module AI.AI_Formation +-- @image AI_Large_Formations.JPG --- AI_FORMATION class -- @type AI_FORMATION diff --git a/Moose Development/Moose/AI/AI_Patrol.lua b/Moose Development/Moose/AI/AI_Patrol.lua index 77026ce02..74e06492b 100644 --- a/Moose Development/Moose/AI/AI_Patrol.lua +++ b/Moose Development/Moose/AI/AI_Patrol.lua @@ -2,10 +2,6 @@ -- -- === -- --- ![Banner Image](..\Presentations\AI_PATROL\Dia1.JPG) --- --- === --- -- AI PATROL classes makes AI Controllables execute an Patrol. -- -- There are the following types of PATROL classes defined: @@ -31,7 +27,7 @@ -- === -- -- @module AI.AI_Patrol - +-- @image AI_Air_Patrolling.JPG --- AI_PATROL_ZONE class -- @type AI_PATROL_ZONE diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 566ba7beb..31fa1644e 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -1,15 +1,6 @@ --- **Core** -- Management of CARGO logistics, that can be transported from and to transportation carriers. -- -- === --- --- ![Banner Image](..\Presentations\CARGO\Dia1.JPG) --- --- === --- --- --- This module is still under construction, but is described above works already, and will keep working ... --- --- === -- -- ### Author: **FlightControl** -- ### Contributions: @@ -17,6 +8,7 @@ -- === -- -- @module Cargo.Cargo +-- @image Cargo.JPG -- Events diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua index 0256721da..c3e12ddea 100644 --- a/Moose Development/Moose/Cargo/CargoCrate.lua +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -1,10 +1,6 @@ --- **Cargo** -- Management of single cargo crates, which are based on a @{Static} object. -- -- === --- --- ![Banner Image](..\Presentations\CARGO\Dia1.JPG) --- --- === -- -- ### [Demo Missions]() -- @@ -18,6 +14,7 @@ -- === -- -- @module Cargo.CargoCrate +-- @image Cargo_Crates.JPG do -- CARGO_CRATE diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index b2fab505c..98435b25c 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -18,6 +18,7 @@ -- === -- -- @module Cargo.CargoGroup +-- @image Cargo_Groups.JPG do -- CARGO_GROUP diff --git a/Moose Development/Moose/Cargo/CargoSlingload.lua b/Moose Development/Moose/Cargo/CargoSlingload.lua index b16e1ffe6..617cc4770 100644 --- a/Moose Development/Moose/Cargo/CargoSlingload.lua +++ b/Moose Development/Moose/Cargo/CargoSlingload.lua @@ -1,10 +1,6 @@ --- **Cargo** -- Management of single cargo crates, which are based on a @{Static} object. The cargo can only be slingloaded. -- -- === --- --- ![Banner Image](..\Presentations\CARGO\Dia1.JPG) --- --- === -- -- ### [Demo Missions]() -- @@ -18,6 +14,7 @@ -- === -- -- @module Cargo.CargoSlingload +-- @image Cargo_Slingload.JPG do -- CARGO_SLINGLOAD diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index 43d5a7350..09fd9f0ce 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -1,10 +1,6 @@ --- **Cargo** -- Management of single cargo logistics, which are based on a @{Wrapper.Unit} object. -- -- === --- --- ![Banner Image](..\Presentations\CARGO\Dia1.JPG) --- --- === -- -- ### [Demo Missions]() -- @@ -18,6 +14,7 @@ -- === -- -- @module Cargo.CargoUnit +-- @image Cargo_Units.JPG do -- CARGO_UNIT diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index f9577a7d1..12a8a5efe 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -1,15 +1,12 @@ --- **Core** -- BASE forms **the basis of the MOOSE framework**. Each class within the MOOSE framework derives from BASE. -- --- ![Banner Image](..\Presentations\BASE\Dia1.JPG) --- --- === --- -- ### Author: **FlightControl** -- ### Contributions: -- -- === -- -- @module Core.Base +-- @image Core_Base.JPG @@ -26,9 +23,7 @@ local _ClassID = 0 -- @field ClassID The ID number of the class. -- @field ClassNameAndID The name of the class concatenated with the ID number of the class. ---- # 1) #BASE class --- --- All classes within the MOOSE framework are derived from the BASE class. +--- All classes within the MOOSE framework are derived from the BASE class. -- -- BASE provides facilities for : -- diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index ec7c4c9e5..eea110521 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -8,14 +8,13 @@ -- === -- -- @module Core.Database +-- @image Core_Database.JPG --- @type DATABASE -- @extends Core.Base#BASE ---- # DATABASE class, extends @{Core.Base#BASE} --- --- Mission designers can use the DATABASE class to refer to: +--- Mission designers can use the DATABASE class to refer to: -- -- * STATICS -- * UNITS diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index d786101fa..dd739a0d9 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -1,7 +1,5 @@ --- **Core** -- EVENT models DCS **event dispatching** using a **publish-subscribe** model. -- --- ![Banner Image](..\Presentations\EVENT\Dia1.JPG) --- -- === -- -- # 1) Event Handling Overview @@ -165,6 +163,7 @@ -- === -- -- @module Core.Event +-- @image Core_Event.JPG --- The EVENT structure diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index e81bb9519..b92beb1c1 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -1,8 +1,6 @@ --- **Core** -- The **FSM** (**F**inite **S**tate **M**achine) class and derived **FSM\_** classes -- are design patterns allowing efficient (long-lasting) processes and workflows. -- --- ![Banner Image](..\Presentations\FSM\Dia1.JPG) --- -- === -- -- A Finite State Machine (FSM) models a process flow that transitions between various **States** through triggered **Events**. @@ -65,6 +63,7 @@ -- === -- -- @module Core.Fsm +-- @image Core_Finite_State_Machine.JPG do -- FSM @@ -72,9 +71,7 @@ do -- FSM -- @extends Core.Base#BASE - --- # FSM class, extends @{Core.Base#BASE} - -- - -- A Finite State Machine (FSM) models a process flow that transitions between various **States** through triggered **Events**. + --- A Finite State Machine (FSM) models a process flow that transitions between various **States** through triggered **Events**. -- -- A FSM can only be in one of a finite number of states. -- The machine is in only one state at a time; the state it is in at any given time is called the **current state**. @@ -804,9 +801,7 @@ do -- FSM_CONTROLLABLE -- @field Wrapper.Controllable#CONTROLLABLE Controllable -- @extends Core.Fsm#FSM - --- # FSM_CONTROLLABLE, extends @{#FSM} - -- - -- FSM_CONTROLLABLE class models Finite State Machines for @{Wrapper.Controllable}s, which are @{Wrapper.Group}s, @{Wrapper.Unit}s, @{Client}s. + --- FSM_CONTROLLABLE class models Finite State Machines for @{Wrapper.Controllable}s, which are @{Wrapper.Group}s, @{Wrapper.Unit}s, @{Client}s. -- -- === -- @@ -939,9 +934,7 @@ do -- FSM_PROCESS -- @extends Core.Fsm#FSM_CONTROLLABLE - --- # FSM_PROCESS, extends @{#FSM} - -- - -- FSM_PROCESS class models Finite State Machines for @{Task} actions, which control @{Client}s. + --- FSM_PROCESS class models Finite State Machines for @{Task} actions, which control @{Client}s. -- -- === -- @@ -1184,9 +1177,7 @@ do -- FSM_TASK -- @field Tasking.Task#TASK Task -- @extends #FSM - --- # FSM_TASK, extends @{#FSM} - -- - -- FSM_TASK class models Finite State Machines for @{Task}s. + --- FSM_TASK class models Finite State Machines for @{Task}s. -- -- === -- @@ -1230,9 +1221,7 @@ do -- FSM_SET -- @extends Core.Fsm#FSM - --- # FSM_SET, extends @{#FSM} - -- - -- FSM_SET class models Finite State Machines for @{Set}s. Note that these FSMs control multiple objects!!! So State concerns here + --- FSM_SET class models Finite State Machines for @{Set}s. Note that these FSMs control multiple objects!!! So State concerns here -- for multiple objects or the position of the state machine in the process. -- -- === diff --git a/Moose Development/Moose/Core/Goal.lua b/Moose Development/Moose/Core/Goal.lua index ba8e9ea1d..608c4ecc0 100644 --- a/Moose Development/Moose/Core/Goal.lua +++ b/Moose Development/Moose/Core/Goal.lua @@ -11,6 +11,7 @@ -- === -- -- @module Core.Goal +-- @image Core_Goal.JPG do -- Goal @@ -18,9 +19,7 @@ do -- Goal -- @extends Core.Fsm#FSM - --- # GOAL class, extends @{Core.Fsm#FSM} - -- - -- GOAL models processes that have an objective with a defined achievement. Derived classes implement the ways how the achievements can be realized. + --- GOAL models processes that have an objective with a defined achievement. Derived classes implement the ways how the achievements can be realized. -- -- ## 1. GOAL constructor -- diff --git a/Moose Development/Moose/Core/Menu.lua b/Moose Development/Moose/Core/Menu.lua index 6850b9213..b93cb59c9 100644 --- a/Moose Development/Moose/Core/Menu.lua +++ b/Moose Development/Moose/Core/Menu.lua @@ -31,6 +31,7 @@ -- === -- -- @module Core.Menu +-- @image Core_Menu.JPG MENU_INDEX = {} @@ -182,8 +183,7 @@ do -- MENU_BASE --- @type MENU_BASE -- @extends Base#BASE - --- # MENU_BASE class, extends @{Core.Base#BASE} - -- The MENU_BASE class defines the main MENU class where other MENU classes are derived from. + --- The MENU_BASE class defines the main MENU class where other MENU classes are derived from. -- This is an abstract class, so don't use it. -- @field #MENU_BASE MENU_BASE = { @@ -286,9 +286,7 @@ do -- MENU_COMMAND_BASE -- @field #function MenuCallHandler -- @extends Core.Menu#MENU_BASE - --- # MENU_COMMAND_BASE class, extends @{Core.Base#BASE} - -- ---------------------------------------------------------- - -- The MENU_COMMAND_BASE class defines the main MENU class where other MENU COMMAND_ + --- The MENU_COMMAND_BASE class defines the main MENU class where other MENU COMMAND_ -- classes are derived from, in order to set commands. -- -- @field #MENU_COMMAND_BASE @@ -358,9 +356,7 @@ do -- MENU_MISSION --- @type MENU_MISSION -- @extends Core.Menu#MENU_BASE - --- # MENU_MISSION class, extends @{Menu#MENU_BASE} - -- - -- The MENU_MISSION class manages the main menus for a complete mission. + --- The MENU_MISSION class manages the main menus for a complete mission. -- You can add menus with the @{#MENU_MISSION.New} method, which constructs a MENU_MISSION object and returns you the object reference. -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION.Remove}. -- @field #MENU_MISSION @@ -455,9 +451,7 @@ do -- MENU_MISSION_COMMAND --- @type MENU_MISSION_COMMAND -- @extends Core.Menu#MENU_COMMAND_BASE - --- # MENU_MISSION_COMMAND class, extends @{Menu#MENU_COMMAND_BASE} - -- - -- The MENU_MISSION_COMMAND class manages the command menus for a complete mission, which allow players to execute functions during mission execution. + --- The MENU_MISSION_COMMAND class manages the command menus for a complete mission, which allow players to execute functions during mission execution. -- You can add menus with the @{#MENU_MISSION_COMMAND.New} method, which constructs a MENU_MISSION_COMMAND object and returns you the object reference. -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION_COMMAND.Remove}. -- @@ -542,9 +536,7 @@ do -- MENU_COALITION --- @type MENU_COALITION -- @extends Core.Menu#MENU_BASE - --- # MENU_COALITION class, extends @{Menu#MENU_BASE} - -- - -- The @{Menu#MENU_COALITION} class manages the main menus for coalitions. + --- The @{Menu#MENU_COALITION} class manages the main menus for coalitions. -- You can add menus with the @{#MENU_COALITION.New} method, which constructs a MENU_COALITION object and returns you the object reference. -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_COALITION.Remove}. -- @@ -680,9 +672,7 @@ do -- MENU_COALITION_COMMAND --- @type MENU_COALITION_COMMAND -- @extends Core.Menu#MENU_COMMAND_BASE - --- # MENU_COALITION_COMMAND class, extends @{Menu#MENU_COMMAND_BASE} - -- - -- The MENU_COALITION_COMMAND class manages the command menus for coalitions, which allow players to execute functions during mission execution. + --- The MENU_COALITION_COMMAND class manages the command menus for coalitions, which allow players to execute functions during mission execution. -- You can add menus with the @{#MENU_COALITION_COMMAND.New} method, which constructs a MENU_COALITION_COMMAND object and returns you the object reference. -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_COALITION_COMMAND.Remove}. -- @@ -780,9 +770,7 @@ do -- @extends Core.Menu#MENU_BASE - --- #MENU_GROUP class, extends @{Menu#MENU_BASE} - -- - -- The MENU_GROUP class manages the main menus for coalitions. + --- The MENU_GROUP class manages the main menus for coalitions. -- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference. -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP.Remove}. -- @@ -938,9 +926,7 @@ do --- @type MENU_GROUP_COMMAND -- @extends Core.Menu#MENU_COMMAND_BASE - --- # MENU_GROUP_COMMAND class, extends @{Menu#MENU_COMMAND_BASE} - -- - -- The @{Menu#MENU_GROUP_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution. + --- The @{Menu#MENU_GROUP_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution. -- You can add menus with the @{#MENU_GROUP_COMMAND.New} method, which constructs a MENU_GROUP_COMMAND object and returns you the object reference. -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP_COMMAND.Remove}. -- @@ -1035,9 +1021,7 @@ do -- @extends Core.Menu#MENU_BASE - --- #MENU_GROUP_DELAYED class, extends @{Menu#MENU_BASE} - -- - -- The MENU_GROUP_DELAYED class manages the main menus for groups. + --- The MENU_GROUP_DELAYED class manages the main menus for groups. -- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference. -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP.Remove}. -- The creation of the menu item is delayed however, and must be created using the @{#MENU_GROUP.Set} method. @@ -1171,9 +1155,7 @@ do --- @type MENU_GROUP_COMMAND_DELAYED -- @extends Core.Menu#MENU_COMMAND_BASE - --- # MENU_GROUP_COMMAND_DELAYED class, extends @{Menu#MENU_COMMAND_BASE} - -- - -- The @{Menu#MENU_GROUP_COMMAND_DELAYED} class manages the command menus for coalitions, which allow players to execute functions during mission execution. + --- The @{Menu#MENU_GROUP_COMMAND_DELAYED} class manages the command menus for coalitions, which allow players to execute functions during mission execution. -- You can add menus with the @{#MENU_GROUP_COMMAND_DELAYED.New} method, which constructs a MENU_GROUP_COMMAND_DELAYED object and returns you the object reference. -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP_COMMAND_DELAYED.Remove}. -- diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index d2256e436..926284332 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -1,18 +1,15 @@ --- **Core** -- MESSAGE class takes are of the **real-time notifications** and **messages to players** during a simulation. -- --- ![Banner Image](..\Presentations\MESSAGE\Dia1.JPG) --- -- === -- -- @module Core.Message +-- @image Core_Message.JPG --- The MESSAGE class -- @type MESSAGE -- @extends Core.Base#BASE ---- # MESSAGE class, extends @{Core.Base#BASE} --- --- Message System to display Messages to Clients, Coalitions or All. +--- Message System to display Messages to Clients, Coalitions or All. -- Messages are shown on the display panel for an amount of seconds, and will then disappear. -- Messages can contain a category which is indicating the category of the message. -- diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index cd8390f7b..6f1f25468 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1,7 +1,5 @@ --- **Core** -- **POINT\_VEC** classes define an **extensive API** to **manage 3D points** in the simulation space. -- --- ![Banner Image](..\Presentations\POINT\Dia1.JPG) --- -- === -- -- # Demo Missions @@ -27,7 +25,7 @@ -- ### Contributions: -- -- @module Core.Point - +-- @image Core_Coordinate.JPG @@ -38,9 +36,7 @@ do -- COORDINATE -- @extends Core.Base#BASE - --- # COORDINATE class, extends @{Core.Base#BASE} - -- - -- COORDINATE defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space. + --- COORDINATE defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space. -- -- ## COORDINATE constructor -- @@ -1584,9 +1580,7 @@ do -- POINT_VEC3 -- @extends Core.Point#COORDINATE - --- # POINT_VEC3 class, extends @{Point#COORDINATE} - -- - -- POINT_VEC3 defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space. + --- POINT_VEC3 defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space. -- -- **Important Note:** Most of the functions in this section were taken from MIST, and reworked to OO concepts. -- In order to keep the credibility of the the author, @@ -1797,9 +1791,7 @@ do -- POINT_VEC2 -- @field Dcs.DCSTypes#Distance y the y coordinate in meters. -- @extends Core.Point#COORDINATE - --- # POINT_VEC2 class, extends @{Point#COORDINATE} - -- - -- The @{Point#POINT_VEC2} class defines a 2D point in the simulator. The height coordinate (if needed) will be the land height + an optional added height specified. + --- The @{Point#POINT_VEC2} class defines a 2D point in the simulator. The height coordinate (if needed) will be the land height + an optional added height specified. -- -- ## POINT_VEC2 constructor -- diff --git a/Moose Development/Moose/Core/Radio.lua b/Moose Development/Moose/Core/Radio.lua index 4bc0c24a1..1e1d96aaf 100644 --- a/Moose Development/Moose/Core/Radio.lua +++ b/Moose Development/Moose/Core/Radio.lua @@ -1,7 +1,5 @@ --- **Core** -- The RADIO Module is responsible for everything that is related to radio transmission and you can hear in DCS, be it TACAN beacons, Radio transmissions... -- --- ![Banner Image](..\Presentations\RADIO\Dia1.JPG) --- -- === -- -- The Radio contains 2 classes : RADIO and BEACON @@ -33,6 +31,7 @@ -- ### Author: Hugues "Grey_Echo" Bousquet -- -- @module Core.Radio +-- @image Core_Radio.JPG --- # RADIO class, extends @{Core.Base#BASE} diff --git a/Moose Development/Moose/Core/Report.lua b/Moose Development/Moose/Core/Report.lua index c6a66fca8..c570bb39b 100644 --- a/Moose Development/Moose/Core/Report.lua +++ b/Moose Development/Moose/Core/Report.lua @@ -9,6 +9,7 @@ -- ### Contributions: -- -- @module Core.Report +-- @image Core_Report.JPG --- The REPORT class diff --git a/Moose Development/Moose/Core/ScheduleDispatcher.lua b/Moose Development/Moose/Core/ScheduleDispatcher.lua index 20f3b5185..a9d27adbc 100644 --- a/Moose Development/Moose/Core/ScheduleDispatcher.lua +++ b/Moose Development/Moose/Core/ScheduleDispatcher.lua @@ -31,6 +31,7 @@ -- ### Authors: FlightControl : Design & Programming -- -- @module Core.ScheduleDispatcher +-- @image Core_Schedule_Dispatcher.JPG --- The SCHEDULEDISPATCHER structure -- @type SCHEDULEDISPATCHER diff --git a/Moose Development/Moose/Core/Scheduler.lua b/Moose Development/Moose/Core/Scheduler.lua index 98b824205..734c31c01 100644 --- a/Moose Development/Moose/Core/Scheduler.lua +++ b/Moose Development/Moose/Core/Scheduler.lua @@ -1,7 +1,5 @@ --- **Core** -- SCHEDULER prepares and handles the **execution of functions over scheduled time (intervals)**. -- --- ![Banner Image](..\Presentations\SCHEDULER\Dia1.JPG) --- -- === -- -- SCHEDULER manages the **scheduling of functions**: @@ -40,7 +38,7 @@ -- === -- -- @module Core.Scheduler - +-- @image Core_Scheduler.JPG --- The SCHEDULER class -- @type SCHEDULER @@ -48,9 +46,7 @@ -- @extends Core.Base#BASE ---- # SCHEDULER class, extends @{Core.Base#BASE} --- --- The SCHEDULER class creates schedule. +--- The SCHEDULER class creates schedule. -- -- A SCHEDULER can manage **multiple** (repeating) schedules. Each planned or executing schedule has a unique **ScheduleID**. -- The ScheduleID is returned when the method @{#SCHEDULER.Schedule}() is called. diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 800511352..fbaed636e 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -1,7 +1,5 @@ --- **Core** -- SET_ classes define **collections** of objects to perform **bulk actions** and logically **group** objects. -- --- ![Banner Image](..\Presentations\SET\Dia1.JPG) --- -- === -- -- SET_ classes group objects of the same type into a collection, which is either: @@ -31,6 +29,7 @@ -- === -- -- @module Core.Set +-- @image Core_Sets.JPG --- @type SET_BASE @@ -41,18 +40,17 @@ -- @extends Core.Base#BASE ---- # 1) SET_BASE class, extends @{Core.Base#BASE} --- The @{Core.Set#SET_BASE} class defines the core functions that define a collection of objects. +--- The @{Core.Set#SET_BASE} class defines the core functions that define a collection of objects. -- A SET provides iterators to iterate the SET, but will **temporarily** yield the ForEach interator loop at defined **"intervals"** to the mail simulator loop. -- In this way, large loops can be done while not blocking the simulator main processing loop. -- The default **"yield interval"** is after 10 objects processed. -- The default **"time interval"** is after 0.001 seconds. -- --- ## 1.1) Add or remove objects from the SET +-- ## Add or remove objects from the SET -- -- Some key core functions are @{Core.Set#SET_BASE.Add} and @{Core.Set#SET_BASE.Remove} to add or remove objects from the SET in your logic. -- --- ## 1.2) Define the SET iterator **"yield interval"** and the **"time interval"** +-- ## Define the SET iterator **"yield interval"** and the **"time interval"** -- -- Modify the iterator intervals with the @{Core.Set#SET_BASE.SetInteratorIntervals} method. -- You can set the **"yield interval"**, and the **"time interval"**. (See above). @@ -647,27 +645,25 @@ end --- @type SET_GROUP -- @extends Core.Set#SET_BASE ---- # SET_GROUP class, extends @{Core.Set#SET_BASE} --- --- Mission designers can use the @{Core.Set#SET_GROUP} class to build sets of groups belonging to certain: +--- Mission designers can use the @{Core.Set#SET_GROUP} class to build sets of groups belonging to certain: -- -- * Coalitions -- * Categories -- * Countries -- * Starting with certain prefix strings. -- --- ## 1. SET_GROUP constructor +-- ## SET_GROUP constructor -- -- Create a new SET_GROUP object with the @{#SET_GROUP.New} method: -- -- * @{#SET_GROUP.New}: Creates a new SET_GROUP object. -- --- ## 2. Add or Remove GROUP(s) from SET_GROUP +-- ## Add or Remove GROUP(s) from SET_GROUP -- -- GROUPS can be added and removed using the @{Core.Set#SET_GROUP.AddGroupsByName} and @{Core.Set#SET_GROUP.RemoveGroupsByName} respectively. -- These methods take a single GROUP name or an array of GROUP names to be added or removed from SET_GROUP. -- --- ## 3. SET_GROUP filter criteria +-- ## SET_GROUP filter criteria -- -- You can set filter criteria to define the set of groups within the SET_GROUP. -- Filter criteria are defined by: @@ -694,7 +690,7 @@ end -- -- * @{#SET_GROUP.FilterZones}: Builds the SET_GROUP with the groups within a @{Core.Zone#ZONE}. -- --- ## 4. SET_GROUP iterators +-- ## SET_GROUP iterators -- -- Once the filters have been defined and the SET_GROUP has been built, you can iterate the SET_GROUP with the available iterator methods. -- The iterator methods will walk the SET_GROUP set, and call for each element within the set a function that you provide. @@ -706,11 +702,11 @@ end -- * @{#SET_GROUP.ForEachGroupNotInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. -- -- --- ## 5. SET_GROUP trigger events on the GROUP objects. +-- ## SET_GROUP trigger events on the GROUP objects. -- -- The SET is derived from the FSM class, which provides extra capabilities to track the contents of the GROUP objects in the SET_GROUP. -- --- ### 5.1. When a GROUP object crashes or is dead, the SET_GROUP will trigger a **Dead** event. +-- ### When a GROUP object crashes or is dead, the SET_GROUP will trigger a **Dead** event. -- -- You can handle the event using the OnBefore and OnAfter event handlers. -- The event handlers need to have the paramters From, Event, To, GroupObject. @@ -1423,9 +1419,7 @@ do -- SET_UNIT --- @type SET_UNIT -- @extends Core.Set#SET_BASE - --- # 3) SET_UNIT class, extends @{Core.Set#SET_BASE} - -- - -- Mission designers can use the SET_UNIT class to build sets of units belonging to certain: + --- Mission designers can use the SET_UNIT class to build sets of units belonging to certain: -- -- * Coalitions -- * Categories @@ -1433,18 +1427,18 @@ do -- SET_UNIT -- * Unit types -- * Starting with certain prefix strings. -- - -- ## 3.1) SET_UNIT constructor + -- ## SET_UNIT constructor -- -- Create a new SET_UNIT object with the @{#SET_UNIT.New} method: -- -- * @{#SET_UNIT.New}: Creates a new SET_UNIT object. -- - -- ## 3.2) Add or Remove UNIT(s) from SET_UNIT + -- ## Add or Remove UNIT(s) from SET_UNIT -- -- UNITs can be added and removed using the @{Core.Set#SET_UNIT.AddUnitsByName} and @{Core.Set#SET_UNIT.RemoveUnitsByName} respectively. -- These methods take a single UNIT name or an array of UNIT names to be added or removed from SET_UNIT. -- - -- ## 3.3) SET_UNIT filter criteria + -- ## SET_UNIT filter criteria -- -- You can set filter criteria to define the set of units within the SET_UNIT. -- Filter criteria are defined by: @@ -1463,7 +1457,7 @@ do -- SET_UNIT -- -- * @{#SET_UNIT.FilterZones}: Builds the SET_UNIT with the units within a @{Core.Zone#ZONE}. -- - -- ## 3.4) SET_UNIT iterators + -- ## SET_UNIT iterators -- -- Once the filters have been defined and the SET_UNIT has been built, you can iterate the SET_UNIT with the available iterator methods. -- The iterator methods will walk the SET_UNIT set, and call for each element within the set a function that you provide. @@ -1479,13 +1473,13 @@ do -- SET_UNIT -- * @{#SET_UNIT.ForEachUnitCompletelyInZone}: Iterate and call an iterator function for each **alive** UNIT presence completely in a @{Zone}, providing the UNIT and optional parameters to the called function. -- * @{#SET_UNIT.ForEachUnitNotInZone}: Iterate and call an iterator function for each **alive** UNIT presence not in a @{Zone}, providing the UNIT and optional parameters to the called function. -- - -- ## 3.5 ) SET_UNIT atomic methods + -- ## SET_UNIT atomic methods -- -- Various methods exist for a SET_UNIT to perform actions or calculations and retrieve results from the SET_UNIT: -- -- * @{#SET_UNIT.GetTypeNames}(): Retrieve the type names of the @{Wrapper.Unit}s in the SET, delimited by a comma. -- - -- ## 4. SET_UNIT iterators + -- ## SET_UNIT iterators -- -- Once the filters have been defined and the SET_UNIT has been built, you can iterate the SET_UNIT with the available iterator methods. -- The iterator methods will walk the SET_UNIT set, and call for each element within the set a function that you provide. @@ -1495,11 +1489,11 @@ do -- SET_UNIT -- * @{#SET_UNIT.ForEachUnitInZone}: Iterate the SET_UNIT and call an iterator function for each **alive** UNIT object presence completely in a @{Zone}, providing the UNIT object and optional parameters to the called function. -- * @{#SET_UNIT.ForEachUnitNotInZone}: Iterate the SET_UNIT and call an iterator function for each **alive** UNIT object presence not in a @{Zone}, providing the UNIT object and optional parameters to the called function. -- - -- ## 5. SET_UNIT trigger events on the UNIT objects. + -- ## SET_UNIT trigger events on the UNIT objects. -- -- The SET is derived from the FSM class, which provides extra capabilities to track the contents of the UNIT objects in the SET_UNIT. -- - -- ### 5.1. When a UNIT object crashes or is dead, the SET_UNIT will trigger a **Dead** event. + -- ### When a UNIT object crashes or is dead, the SET_UNIT will trigger a **Dead** event. -- -- You can handle the event using the OnBefore and OnAfter event handlers. -- The event handlers need to have the paramters From, Event, To, GroupObject. @@ -2423,9 +2417,7 @@ do -- SET_STATIC --- @type SET_STATIC -- @extends Core.Set#SET_BASE - --- # 3) SET_STATIC class, extends @{Core.Set#SET_BASE} - -- - -- Mission designers can use the SET_STATIC class to build sets of Statics belonging to certain: + --- Mission designers can use the SET_STATIC class to build sets of Statics belonging to certain: -- -- * Coalitions -- * Categories @@ -2433,18 +2425,18 @@ do -- SET_STATIC -- * Static types -- * Starting with certain prefix strings. -- - -- ## 3.1) SET_STATIC constructor + -- ## SET_STATIC constructor -- -- Create a new SET_STATIC object with the @{#SET_STATIC.New} method: -- -- * @{#SET_STATIC.New}: Creates a new SET_STATIC object. -- - -- ## 3.2) Add or Remove STATIC(s) from SET_STATIC + -- ## Add or Remove STATIC(s) from SET_STATIC -- -- STATICs can be added and removed using the @{Core.Set#SET_STATIC.AddStaticsByName} and @{Core.Set#SET_STATIC.RemoveStaticsByName} respectively. -- These methods take a single STATIC name or an array of STATIC names to be added or removed from SET_STATIC. -- - -- ## 3.3) SET_STATIC filter criteria + -- ## SET_STATIC filter criteria -- -- You can set filter criteria to define the set of units within the SET_STATIC. -- Filter criteria are defined by: @@ -2463,7 +2455,7 @@ do -- SET_STATIC -- -- * @{#SET_STATIC.FilterZones}: Builds the SET_STATIC with the units within a @{Core.Zone#ZONE}. -- - -- ## 3.4) SET_STATIC iterators + -- ## SET_STATIC iterators -- -- Once the filters have been defined and the SET_STATIC has been built, you can iterate the SET_STATIC with the available iterator methods. -- The iterator methods will walk the SET_STATIC set, and call for each element within the set a function that you provide. @@ -2479,7 +2471,7 @@ do -- SET_STATIC -- * @{#SET_STATIC.ForEachStaticCompletelyInZone}: Iterate and call an iterator function for each **alive** STATIC presence completely in a @{Zone}, providing the STATIC and optional parameters to the called function. -- * @{#SET_STATIC.ForEachStaticNotInZone}: Iterate and call an iterator function for each **alive** STATIC presence not in a @{Zone}, providing the STATIC and optional parameters to the called function. -- - -- ## 3.5 ) SET_STATIC atomic methods + -- ## SET_STATIC atomic methods -- -- Various methods exist for a SET_STATIC to perform actions or calculations and retrieve results from the SET_STATIC: -- @@ -3099,9 +3091,7 @@ end ---- # 4) SET_CLIENT class, extends @{Core.Set#SET_BASE} --- --- Mission designers can use the @{Core.Set#SET_CLIENT} class to build sets of units belonging to certain: +--- Mission designers can use the @{Core.Set#SET_CLIENT} class to build sets of units belonging to certain: -- -- * Coalitions -- * Categories @@ -3109,18 +3099,18 @@ end -- * Client types -- * Starting with certain prefix strings. -- --- ## 4.1) SET_CLIENT constructor +-- ## SET_CLIENT constructor -- -- Create a new SET_CLIENT object with the @{#SET_CLIENT.New} method: -- -- * @{#SET_CLIENT.New}: Creates a new SET_CLIENT object. -- --- ## 4.2) Add or Remove CLIENT(s) from SET_CLIENT +-- ## Add or Remove CLIENT(s) from SET_CLIENT -- -- CLIENTs can be added and removed using the @{Core.Set#SET_CLIENT.AddClientsByName} and @{Core.Set#SET_CLIENT.RemoveClientsByName} respectively. -- These methods take a single CLIENT name or an array of CLIENT names to be added or removed from SET_CLIENT. -- --- ## 4.3) SET_CLIENT filter criteria +-- ## SET_CLIENT filter criteria -- -- You can set filter criteria to define the set of clients within the SET_CLIENT. -- Filter criteria are defined by: @@ -3139,7 +3129,7 @@ end -- -- * @{#SET_CLIENT.FilterZones}: Builds the SET_CLIENT with the clients within a @{Core.Zone#ZONE}. -- --- ## 4.4) SET_CLIENT iterators +-- ## SET_CLIENT iterators -- -- Once the filters have been defined and the SET_CLIENT has been built, you can iterate the SET_CLIENT with the available iterator methods. -- The iterator methods will walk the SET_CLIENT set, and call for each element within the set a function that you provide. @@ -3512,17 +3502,15 @@ end ---- # 4) SET_PLAYER class, extends @{Core.Set#SET_BASE} +--- Mission designers can use the @{Core.Set#SET_PLAYER} class to build sets of units belonging to alive players: -- --- Mission designers can use the @{Core.Set#SET_PLAYER} class to build sets of units belonging to alive players: --- --- ## 4.1) SET_PLAYER constructor +-- ## 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 +-- ## SET_PLAYER filter criteria -- -- You can set filter criteria to define the set of clients within the SET_PLAYER. -- Filter criteria are defined by: @@ -3541,7 +3529,7 @@ end -- -- * @{#SET_PLAYER.FilterZones}: Builds the SET_PLAYER with the clients within a @{Core.Zone#ZONE}. -- --- ## 4.4) SET_PLAYER iterators +-- ## 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. @@ -3909,24 +3897,22 @@ end --- @type SET_AIRBASE -- @extends Core.Set#SET_BASE ---- # 5) SET_AIRBASE class, extends @{Core.Set#SET_BASE} --- --- Mission designers can use the @{Core.Set#SET_AIRBASE} class to build sets of airbases optionally belonging to certain: +--- Mission designers can use the @{Core.Set#SET_AIRBASE} class to build sets of airbases optionally belonging to certain: -- -- * Coalitions -- --- ## 5.1) SET_AIRBASE constructor +-- ## SET_AIRBASE constructor -- -- Create a new SET_AIRBASE object with the @{#SET_AIRBASE.New} method: -- -- * @{#SET_AIRBASE.New}: Creates a new SET_AIRBASE object. -- --- ## 5.2) Add or Remove AIRBASEs from SET_AIRBASE +-- ## Add or Remove AIRBASEs from SET_AIRBASE -- -- AIRBASEs can be added and removed using the @{Core.Set#SET_AIRBASE.AddAirbasesByName} and @{Core.Set#SET_AIRBASE.RemoveAirbasesByName} respectively. -- These methods take a single AIRBASE name or an array of AIRBASE names to be added or removed from SET_AIRBASE. -- --- ## 5.3) SET_AIRBASE filter criteria +-- ## SET_AIRBASE filter criteria -- -- You can set filter criteria to define the set of clients within the SET_AIRBASE. -- Filter criteria are defined by: @@ -3937,7 +3923,7 @@ end -- -- * @{#SET_AIRBASE.FilterStart}: Starts the filtering of the airbases within the SET_AIRBASE. -- --- ## 5.4) SET_AIRBASE iterators +-- ## SET_AIRBASE iterators -- -- Once the filters have been defined and the SET_AIRBASE has been built, you can iterate the SET_AIRBASE with the available iterator methods. -- The iterator methods will walk the SET_AIRBASE set, and call for each airbase within the set a function that you provide. @@ -4198,9 +4184,7 @@ end --- @type SET_CARGO -- @extends Core.Set#SET_BASE ---- # (R2.1) SET_CARGO class, extends @{Core.Set#SET_BASE} --- --- Mission designers can use the @{Core.Set#SET_CARGO} class to build sets of cargos optionally belonging to certain: +--- Mission designers can use the @{Core.Set#SET_CARGO} class to build sets of cargos optionally belonging to certain: -- -- * Coalitions -- * Types @@ -4260,7 +4244,7 @@ SET_CARGO = { } ---- (R2.1) Creates a new SET_CARGO object, building a set of cargos belonging to a coalitions and categories. +--- Creates a new SET_CARGO object, building a set of cargos belonging to a coalitions and categories. -- @param #SET_CARGO self -- @return #SET_CARGO -- @usage @@ -4632,9 +4616,7 @@ end --- @type SET_ZONE -- @extends Core.Set#SET_BASE ---- # SET_ZONE class, extends @{Core.Set#SET_BASE} --- --- Mission designers can use the @{Core.Set#SET_ZONE} class to build sets of zones of various types. +--- Mission designers can use the @{Core.Set#SET_ZONE} class to build sets of zones of various types. -- -- ## SET_ZONE constructor -- @@ -4647,7 +4629,7 @@ end -- ZONEs can be added and removed using the @{Core.Set#SET_ZONE.AddZonesByName} and @{Core.Set#SET_ZONE.RemoveZonesByName} respectively. -- These methods take a single ZONE name or an array of ZONE names to be added or removed from SET_ZONE. -- --- ## 5.3) SET_ZONE filter criteria +-- ## SET_ZONE filter criteria -- -- You can set filter criteria to build the collection of zones in SET_ZONE. -- Filter criteria are defined by: @@ -4658,7 +4640,7 @@ end -- -- * @{#SET_ZONE.FilterStart}: Starts the filtering of the zones within the SET_ZONE. -- --- ## 5.4) SET_ZONE iterators +-- ## SET_ZONE iterators -- -- Once the filters have been defined and the SET_ZONE has been built, you can iterate the SET_ZONE with the available iterator methods. -- The iterator methods will walk the SET_ZONE set, and call for each airbase within the set a function that you provide. diff --git a/Moose Development/Moose/Core/Settings.lua b/Moose Development/Moose/Core/Settings.lua index 86a607fc1..8bd51a63b 100644 --- a/Moose Development/Moose/Core/Settings.lua +++ b/Moose Development/Moose/Core/Settings.lua @@ -1,7 +1,5 @@ --- **Core** -- Manages various settings for MOOSE classes. -- --- ![Banner Image](..\Presentations\SETTINGS\Dia1.JPG) --- -- === -- -- The documentation of the SETTINGS class can be found further in this document. @@ -17,20 +15,16 @@ -- * **FlightControl**: Design & Programming -- -- @module Core.Settings +-- @image Core_Settings.JPG --- @type SETTINGS -- @extends Core.Base#BASE ---- # SETTINGS class, extends @{Core.Base#BASE} --- The SETTINGS class takes care of various settings that influence the behaviour of certain functionalities and classes within the MOOSE framework. +--- The SETTINGS class takes care of various settings that influence the behaviour of certain functionalities and classes within the MOOSE framework. -- -- === -- --- ![Banner Image](..\Presentations\SETTINGS\Dia1.JPG) --- --- === --- -- The SETTINGS class takes care of various settings that influence the behaviour of certain functionalities and classes within the MOOSE framework. -- SETTINGS can work on 2 levels: -- @@ -39,29 +33,29 @@ -- -- So, when there isn't any **Player setting** defined for a player for a specific setting, or, the player cannot be identified, the **Default setting** will be used instead. -- --- ## 1. \_SETTINGS object +-- ## \_SETTINGS object -- -- MOOSE defines by default a singleton object called **\_SETTINGS**. Use this object to modify all the **Default settings** for a running mission. -- For each player, MOOSE will automatically allocate also a **player settings** object, and will expose a radio menu to allow the player to adapt the settings to his own preferences. -- --- ## 2. SETTINGS Menu +-- ## SETTINGS Menu -- -- Settings can be adapted by the Players and by the Mission Administrator through **radio menus, which are automatically available in the mission**. -- These menus can be found **on level F10 under "Settings"**. There are two kinds of menus generated by the system. -- --- ### 2.1. Default settings menu +-- ### Default settings menu -- -- A menu is created automatically per Command Center that allows to modify the **Default** settings. -- So, when joining a CC unit, a menu will be available that allows to change the settings parameters **FOR ALL THE PLAYERS**! -- Note that the **Default settings** will only be used when a player has not choosen its own settings. -- --- ### 2.2. Player settings menu +-- ### Player settings menu -- -- A menu is created automatically per Player Slot (group) that allows to modify the **Player** settings. -- So, when joining a slot, a menu wil be available that allows to change the settings parameters **FOR THE PLAYER ONLY**! -- Note that when a player has not chosen a specific setting, the **Default settings** will be used. -- --- ### 2.3. Show or Hide the Player Setting menus +-- ### Show or Hide the Player Setting menus -- -- Of course, it may be requried not to show any setting menus. In this case, a method is available on the **\_SETTINGS object**. -- Use @{#SETTINGS.SetPlayerMenuOff}() to hide the player menus, and use @{#SETTINGS.SetPlayerMenuOn}() show the player menus. @@ -75,14 +69,14 @@ -- -- But only when a player exits and reenters the slot these settings will have effect! -- -- --- ## 3. Settings +-- ## Settings -- -- There are different settings that are managed and applied within the MOOSE framework. -- See below a comprehensive description of each. -- --- ### 3.1. **A2G coordinates** display formatting +-- ### **A2G coordinates** display formatting -- --- #### 3.1.1. A2G coordinates setting **types** +-- #### A2G coordinates setting **types** -- -- Will customize which display format is used to indicate A2G coordinates in text as part of the Command Center communications. -- @@ -91,11 +85,11 @@ -- - A2G LL DMS: Lattitude Longitude [Degrees Minutes Seconds](https://en.wikipedia.org/wiki/Geographic_coordinate_conversion). The accuracy can also be adapted. -- - A2G LL DDM: Lattitude Longitude [Decimal Degrees Minutes](https://en.wikipedia.org/wiki/Decimal_degrees). The accuracy can also be adapted. -- --- #### 3.1.2. A2G coordinates setting **menu** +-- #### A2G coordinates setting **menu** -- -- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot. -- --- #### 3.1.3. A2G coordinates setting **methods** +-- #### A2G coordinates setting **methods** -- -- There are different methods that can be used to change the **System settings** using the \_SETTINGS object. -- @@ -104,14 +98,14 @@ -- - @{#SETTINGS.SetA2G_LL_DMS}(): Enable the LL DMS display formatting by default. Use @{SETTINGS.SetLL_Accuracy}() to adapt the accuracy of the Seconds formatting. -- - @{#SETTINGS.SetA2G_LL_DDM}(): Enable the LL DDM display formatting by default. Use @{SETTINGS.SetLL_Accuracy}() to adapt the accuracy of the Seconds formatting. -- --- #### 3.1.4. A2G coordinates setting - additional notes +-- #### A2G coordinates setting - additional notes -- -- One additional note on BR. In a situation when a BR coordinate should be given, -- but there isn't any player context (no player unit to reference from), the MGRS formatting will be applied! -- --- ### 3.2. **A2A coordinates** formatting +-- ### **A2A coordinates** formatting -- --- #### 3.2.1. A2A coordinates setting **types** +-- #### A2A coordinates setting **types** -- -- Will customize which display format is used to indicate A2A coordinates in text as part of the Command Center communications. -- @@ -121,11 +115,11 @@ -- - A2A LL DDM: Lattitude Longitude [Decimal Degrees and Minutes](https://en.wikipedia.org/wiki/Decimal_degrees). The accuracy can also be adapted. -- - A2A BULLS: [Bullseye](http://falcon4.wikidot.com/concepts:bullseye). -- --- #### 3.2.2. A2A coordinates setting **menu** +-- #### A2A coordinates setting **menu** -- -- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot. -- --- #### 3.2.3. A2A coordinates setting **methods** +-- #### A2A coordinates setting **methods** -- -- There are different methods that can be used to change the **System settings** using the \_SETTINGS object. -- @@ -135,34 +129,34 @@ -- - @{#SETTINGS.SetA2A_LL_DDM}(): Enable the LL DDM display formatting by default. Use @{SETTINGS.SetLL_Accuracy}() to adapt the accuracy of the Seconds formatting. -- - @{#SETTINGS.SetA2A_BULLS}(): Enable the BULLSeye display formatting by default. -- --- #### 3.2.4. A2A coordinates settings - additional notes +-- #### A2A coordinates settings - additional notes -- -- One additional note on BRAA. In a situation when a BRAA coordinate should be given, -- but there isn't any player context (no player unit to reference from), the MGRS formatting will be applied! -- --- ### 3.3. **Measurements** formatting +-- ### **Measurements** formatting -- --- #### 3.3.1. Measurements setting **types** +-- #### Measurements setting **types** -- -- Will customize the measurements system being used as part as part of the Command Center communications. -- -- - **Metrics** system: Applies the [Metrics system](https://en.wikipedia.org/wiki/Metric_system) ... -- - **Imperial** system: Applies the [Imperial system](https://en.wikipedia.org/wiki/Imperial_units) ... -- --- #### 3.3.2. Measurements setting **menu** +-- #### Measurements setting **menu** -- -- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot. -- --- #### 3.3.3. Measurements setting **methods** +-- #### Measurements setting **methods** -- -- There are different methods that can be used to change the **Default settings** using the \_SETTINGS object. -- -- - @{#SETTINGS.SetMetric}(): Enable the Metric system. -- - @{#SETTINGS.SetImperial}(): Enable the Imperial system. -- --- ### 3.4. **Message** display times +-- ### **Message** display times -- --- #### 3.4.1. Message setting **types** +-- #### Message setting **types** -- -- There are various **Message Types** that will influence the duration how long a message will appear as part of the Command Center communications. -- @@ -172,7 +166,7 @@ -- - **Overview report**: Provides a short report overview, the summary of the report. -- - **Detailed report**: Provides a complete report. -- --- #### 3.4.2. Message setting **menu** +-- #### Message setting **menu** -- -- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot. -- diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index a42d6b7c5..919623334 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1,7 +1,5 @@ --- **Core** -- SPAWN class dynamically spawns new groups of units in your missions. -- --- ![Banner Image](..\Presentations\SPAWN\SPAWN.JPG) --- -- === -- -- The documentation of the SPAWN class can be found further in this document. @@ -22,6 +20,7 @@ -- === -- -- @module Core.Spawn +-- @image Core_Spawn.JPG --- SPAWN Class @@ -37,13 +36,7 @@ -- @extends Core.Base#BASE ---- # SPAWN class, extends @{Core.Base#BASE} --- --- -- ![Banner Image](..\Presentations\SPAWN\SPAWN.JPG) --- --- === --- --- The SPAWN class allows to spawn dynamically new groups. +--- The SPAWN class allows to spawn dynamically new groups. -- Each SPAWN object needs to be have related **template groups** setup in the Mission Editor (ME), -- which is a normal group with the **Late Activation** flag set. -- This template group will never be activated in your mission. diff --git a/Moose Development/Moose/Core/SpawnStatic.lua b/Moose Development/Moose/Core/SpawnStatic.lua index f1715465a..52df548da 100644 --- a/Moose Development/Moose/Core/SpawnStatic.lua +++ b/Moose Development/Moose/Core/SpawnStatic.lua @@ -1,7 +1,5 @@ --- **Core** -- Spawn dynamically new STATICs in your missions. -- --- ![Banner Image](..\Presentations\SPAWNSTATIC\Dia1.JPG) --- -- === -- -- SPAWNSTATIC spawns static structures in your missions dynamically. See below the SPAWNSTATIC class documentation. @@ -30,6 +28,7 @@ -- === -- -- @module Core.SpawnStatic +-- @image Core_Spawnstatic.JPG @@ -37,9 +36,7 @@ -- @extends Core.Base#BASE ---- # SPAWNSTATIC class, extends @{Core.Base#BASE} --- --- The SPAWNSTATIC class allows to spawn dynamically new @{Static}s. +--- The SPAWNSTATIC class allows to spawn dynamically new @{Static}s. -- Through creating a copy of an existing static object template as defined in the Mission Editor (ME), -- SPAWNSTATIC can retireve the properties of the defined static object template (like type, category etc), and "copy" -- these properties to create a new static object and place it at the desired coordinate. diff --git a/Moose Development/Moose/Core/Spot.lua b/Moose Development/Moose/Core/Spot.lua index 22ba7eee8..0af7a7b02 100644 --- a/Moose Development/Moose/Core/Spot.lua +++ b/Moose Development/Moose/Core/Spot.lua @@ -1,7 +1,5 @@ --- **Core** -- Management of SPOT logistics, that can be transported from and to transportation carriers. -- --- ![Banner Image](..\Presentations\SPOT\Dia1.JPG) --- -- === -- -- SPOT implements the DCS Spot class functionality, but adds additional luxury to be able to: @@ -39,6 +37,7 @@ -- === -- -- @module Core.Spot +-- @image Core_Spot.JPG do @@ -47,9 +46,7 @@ do -- @extends Core.Fsm#FSM - --- # SPOT class, extends @{Fsm#FSM} - -- - -- SPOT implements the DCS Spot class functionality, but adds additional luxury to be able to: + --- SPOT implements the DCS Spot class functionality, but adds additional luxury to be able to: -- -- * Mark targets for a defined duration. -- * wiggle the spot at the target. diff --git a/Moose Development/Moose/Core/UserFlag.lua b/Moose Development/Moose/Core/UserFlag.lua index 8cfff730f..9bfa5a628 100644 --- a/Moose Development/Moose/Core/UserFlag.lua +++ b/Moose Development/Moose/Core/UserFlag.lua @@ -11,6 +11,8 @@ -- === -- -- @module Core.UserFlag +-- @image Core_Userflag.JPG +-- do -- UserFlag @@ -18,11 +20,9 @@ do -- UserFlag -- @extends Core.Base#BASE - --- # USERFLAG class, extends @{Core.Base#BASE} + --- Management of DCS User Flags. -- - -- Management of DCS User Flags. - -- - -- ## 1. USERFLAG constructor + -- ## USERFLAG constructor -- -- * @{#USERFLAG.New}(): Creates a new USERFLAG object. -- diff --git a/Moose Development/Moose/Core/UserSound.lua b/Moose Development/Moose/Core/UserSound.lua index 2a7614384..9b7204b2f 100644 --- a/Moose Development/Moose/Core/UserSound.lua +++ b/Moose Development/Moose/Core/UserSound.lua @@ -11,6 +11,7 @@ -- === -- -- @module Core.UserSound +-- @image Core_Usersound.JPG do -- UserSound @@ -18,11 +19,9 @@ do -- UserSound -- @extends Core.Base#BASE - --- # USERSOUND class, extends @{Core.Base#BASE} + --- Management of DCS User Sound. -- - -- Management of DCS User Sound. - -- - -- ## 1. USERSOUND constructor + -- ## USERSOUND constructor -- -- * @{#USERSOUND.New}(): Creates a new USERSOUND object. -- diff --git a/Moose Development/Moose/Core/Velocity.lua b/Moose Development/Moose/Core/Velocity.lua index 0888aa210..c8ef325f3 100644 --- a/Moose Development/Moose/Core/Velocity.lua +++ b/Moose Development/Moose/Core/Velocity.lua @@ -8,6 +8,7 @@ -- === -- -- @module Core.Velocity +-- @image Core_Velocity.JPG do -- Velocity @@ -15,11 +16,9 @@ do -- Velocity -- @extends Core.Base#BASE - --- # VELOCITY class, extends @{Core.Base#BASE} - -- -- VELOCITY models a speed, which can be expressed in various formats according the Settings. -- - -- ## 1. VELOCITY constructor + -- ## VELOCITY constructor -- -- * @{#VELOCITY.New}(): Creates a new VELOCITY object. -- diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index a6201d28a..ee8180931 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -1,7 +1,5 @@ --- **Core** -- ZONE classes define **zones** within your mission of **various forms**, with **various capabilities**. -- --- ![Banner Image](..\Presentations\ZONE\Dia1.JPG) --- -- === -- -- There are essentially two core functions that zones accomodate: @@ -35,6 +33,7 @@ -- === -- -- @module Core.Zone +-- @image Core_Zones.JPG --- @type ZONE_BASE @@ -43,9 +42,7 @@ -- @extends Core.Base#BASE ---- # ZONE_BASE class, extends @{Core.Base#BASE} --- --- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. +--- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. -- -- ## Each zone has a name: -- @@ -380,9 +377,7 @@ end -- @field Dcs.DCSTypes#Distance Radius The radius of the zone. -- @extends #ZONE_BASE ---- # ZONE_RADIUS class, extends @{Core.Zone#ZONE_BASE} --- --- The ZONE_RADIUS class defined by a zone name, a location and a radius. +--- The ZONE_RADIUS class defined by a zone name, a location and a radius. -- This class implements the inherited functions from Core.Zone#ZONE_BASE taking into account the own zone format and properties. -- -- ## ZONE_RADIUS constructor @@ -951,9 +946,7 @@ end -- @extends #ZONE_RADIUS ---- # ZONE class, extends @{Core.Zone#ZONE_RADIUS} --- --- The ZONE class, defined by the zone name as defined within the Mission Editor. +--- The ZONE class, defined by the zone name as defined within the Mission Editor. -- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. -- -- ## ZONE constructor @@ -1023,9 +1016,7 @@ end -- @field Wrapper.Unit#UNIT ZoneUNIT -- @extends Core.Zone#ZONE_RADIUS ---- # ZONE_UNIT class, extends @{Core.Zone#ZONE_RADIUS} --- --- The ZONE_UNIT class defined by a zone around a @{Unit#UNIT} with a radius. +--- The ZONE_UNIT class defined by a zone around a @{Unit#UNIT} with a radius. -- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. -- -- @field #ZONE_UNIT @@ -1116,9 +1107,7 @@ end -- @extends #ZONE_RADIUS ---- # ZONE_GROUP class, extends @{Core.Zone#ZONE_RADIUS} --- --- The ZONE_GROUP class defines by a zone around a @{Wrapper.Group#GROUP} with a radius. The current leader of the group defines the center of the zone. +--- The ZONE_GROUP class defines by a zone around a @{Wrapper.Group#GROUP} with a radius. The current leader of the group defines the center of the zone. -- This class implements the inherited functions from @{Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties. -- -- @field #ZONE_GROUP @@ -1197,9 +1186,7 @@ end -- @extends #ZONE_BASE ---- # ZONE_POLYGON_BASE class, extends @{Core.Zone#ZONE_BASE} --- --- The ZONE_POLYGON_BASE class defined by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon. +--- The ZONE_POLYGON_BASE class defined by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon. -- This class implements the inherited functions from @{Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties. -- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. -- @@ -1474,9 +1461,7 @@ end -- @extends #ZONE_POLYGON_BASE ---- # ZONE_POLYGON class, extends @{Core.Zone#ZONE_POLYGON_BASE} --- --- The ZONE_POLYGON class defined by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon. +--- The ZONE_POLYGON class defined by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon. -- This class implements the inherited functions from @{Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties. -- -- ## Declare a ZONE_POLYGON directly in the DCS mission editor! From 4941b5ee98a52046290255db2cfc0eba046b9488 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 28 May 2018 00:33:58 +0200 Subject: [PATCH 133/420] ARTY v0.9.2 Added tactical nukes. --- .../Moose/Functional/Artillery.lua | 185 +++++++++++++++++- 1 file changed, 182 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index f8a210b8e..78978518a 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -77,7 +77,11 @@ -- @field #table ammomissiles Table holding names of the missile types which are included when counting the ammo. Default is {"weapons.missiles"} which includes some guided missiles. -- @field #number Nshots Number of shots fired on current target. -- @field #number minrange Minimum firing range in kilometers. Targets closer than this distance are not engaged. Default 0.5 km. --- @field #number maxrange Maximum firing range in kilometers. Targets further away than this distance are not engaged. Default 10000 km. +-- @field #number maxrange Maximum firing range in kilometers. Targets further away than this distance are not engaged. Default 10000 km. +-- @field #number nukewarhead Explosion strength of tactical nuclear warhead in kg TNT. Default 75000. +-- @field #number nukerange Demolition range of tactical nuclear explostions. +-- @field #boolean nukefire Ignite additional fires and smoke for nuclear explosions Default true. +-- @field #number nukefires Number of nuclear fires. -- @extends Core.Fsm#FSM_CONTROLLABLE ---# ARTY class, extends @{Core.Fsm#FSM_CONTROLLABLE} @@ -365,6 +369,10 @@ ARTY={ Nshots=0, minrange=500, maxrange=1000000, + nukewarhead=75000, + nukerange=nil, + nukefire=true, + nukefires=nil, } --- Weapong type ID. http://wiki.hoggit.us/view/DCS_enum_weapon_flag @@ -376,7 +384,8 @@ ARTY.WeaponType={ UnguidedAny=805339120, GuidedMissile=268402688, CruiseMissile=2097152, - AntiShipMissile=65536, + AntiShipMissile=65536, + TacticalNuke=666, } --- Some ID to identify who we are in output of the DCS.log file. @@ -385,7 +394,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #string version -ARTY.version="0.9.1" +ARTY.version="0.9.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -843,6 +852,14 @@ function ARTY:onafterStart(Controllable, From, Event, To) -- Get Ammo. self.Nammo0, self.Nshells0, self.Nrockets0, self.Nmissiles0=self:GetAmmo(self.Debug) + -- Init nuclear explosion parameters if they were not set by user. + if self.nukerange==nil then + self.nukerange=1500/75000*self.nukewarhead -- linear dependence + end + if self.nukefires==nil then + self.nukefires=20/1000/1000*self.nukerange*self.nukerange + end + local text=string.format("\n******************************************************\n") text=text..string.format("Arty group = %s\n", Controllable:GetName()) text=text..string.format("Artillery attribute = %s\n", tostring(self.IsArtillery)) @@ -870,6 +887,9 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("Rearming coord dist = %d m\n", dist) text=text..string.format("Rearming ARTY roads = %s\n", tostring(self.RearmingArtyOnRoad)) end + text=text..string.format("Nuclear warhead = %d tons TNT\n", self.nukewarhead/1000) + text=text..string.format("Nuclear demolition = %d m\n", self.nukerange) + text=text..string.format("Nuclear fires = %d (active=%s)\n", self.nukefires, tostring(self.nukefire)) text=text..string.format("******************************************************\n") text=text..string.format("Targets:\n") for _, target in pairs(self.targets) do @@ -952,6 +972,115 @@ end -- Event Handling ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Model a nuclear blast/destruction by creating fires and destroy scenery. +-- @param #ARTY self +-- @param Core.Point#COORDINATE _coord Coordinate of the impact point (center of the blast). +function ARTY:_NuclearBlast(_coord) + + local S0=self.nukewarhead + local R0=self.nukerange + + -- Number of fires + local N0=self.nukefires + + -- Create an explosion at the last known position. + _coord:Explosion(S0) + + -- Huge fire at direct impact point. + if self.nukefire then + _coord:BigSmokeAndFireHuge() + end + + -- Create a table of fire coordinates within the demolition zone. + local _fires={} + for i=1,N0 do + local _fire=_coord:GetRandomCoordinateInRadius(R0) + local _dist=_fire:Get2DDistance(_coord) + table.insert(_fires, {distance=_dist, coord=_fire}) + end + + -- Sort scenery wrt to distance from impact point. + local _sort = function(a,b) return a.distance < b.distance end + table.sort(_fires,_sort) + + local function _explosion(R) + -- At R=R0 ==> explosion strength is 1% of S0 at impact point. + local alpha=math.log(100) + local strength=S0*math.exp(-alpha*R/R0) + env.info(string.format("FF: nuklear explosion strength s(%.1f m) = %.10f (s/s0=%.1f %%), alpha=%.3f", R, strength, strength/S0*100, alpha)) + return strength + end + + local function ignite(_fires) + for _,fire in pairs(_fires) do + local _fire=fire.coord --Core.Point#COORDINATE + + -- Get distance to impact and calc exponential explosion strength. + local R=_fire:Get2DDistance(_coord) + local S=_explosion(R) + env.info(string.format("FF: explosion r=%.1f, s=%.3f", R, S)) + + -- Get a random Big Smoke and fire object. + local _preset=math.random(0,7) + local _density=S/S0 --math.random()+0.1 + + _fire:BigSmokeAndFire(_preset,_density) + _fire:Explosion(S) + + end + end + + if self.nukefire then + ignite(_fires) + end + +--[[ + local ZoneNuke=ZONE_RADIUS:New("Nukezone", _coord:GetVec2(), 2000) + + -- Scan for Scenery objects. + ZoneNuke:Scan(Object.Category.SCENERY) + + -- Array with all possible hideouts, i.e. scenery objects in the vicinity of the group. + local scenery={} + + for SceneryTypeName, SceneryData in pairs(ZoneNuke:GetScannedScenery()) do + for SceneryName, SceneryObject in pairs(SceneryData) do + + local SceneryObject = SceneryObject -- Wrapper.Scenery#SCENERY + + -- Position of the scenery object. + local spos=SceneryObject:GetCoordinate() + + -- Distance from group to impact point. + local distance= spos:Get2DDistance(_coord) + + -- Place markers on every possible scenery object. + if self.Debug then + local MarkerID=spos:MarkToAll(string.format("%s scenery object %s", self.Controllable:GetName(), SceneryObject:GetTypeName())) + local text=string.format("%s scenery: %s, Coord %s", self.Controllable:GetName(), SceneryObject:GetTypeName(), SceneryObject:GetCoordinate():ToStringLLDMS()) + self:T2(SUPPRESSION.id..text) + end + + -- Add to table. + table.insert(scenery, {object=SceneryObject, distance=distance}) + + --SceneryObject:Destroy() + end + end + + -- Sort scenery wrt to distance from impact point. +-- local _sort = function(a,b) return a.distance < b.distance end +-- table.sort(scenery,_sort) + +-- for _,object in pairs(scenery) do +-- local sobject=object -- Wrapper.Scenery#SCENERY +-- sobject:Destroy() +-- end + +]] + +end + --- Eventhandler for shot event. -- @param #ARTY self -- @param Core.Event#EVENTDATA EventData @@ -985,6 +1114,49 @@ function ARTY:_OnEventShot(EventData) self:T(ARTY.id..text) MESSAGE:New(text, 5):ToAllIf(self.report or self.Debug) + -- Last known position of the weapon fired. + local _lastpos={x=0, y=0, z=0} + + --- Track the position of the weapon if it is supposed to model a tac nuke. + -- @param #table _weapon + local function _TrackWeapon(_weapon) + + -- When the pcall status returns false the weapon has hit. + local _status,_currpos = pcall( + function() + return _weapon:getPoint() + end) + + self:T(ARTY.id..string.format("ARTY %s: Weapon still in air: %s", self.Controllable:GetName(), tostring(_status))) + + if _status then + + -- Update last position. + _lastpos={x=_currpos.x, y=_currpos.y, z=_currpos.z} + + -- Check again in 0.05 seconds. + --return timer.getTime() + self.dtBombtrack + return timer.getTime() + 0.05 + + else + + local _impactcoord=COORDINATE:NewFromVec3(_lastpos) + + -- Create a "nuclear" explosion and blast at the impact point. + SCHEDULER:New(nil, ARTY._NuclearBlast, {self,_impactcoord}, 1.0) + --self:_NuclearBlast(_impactcoord) + + end + + end + + -- Start track the shell if we want to model a tactical nuke. + if self.currentTarget.weapontype==ARTY.WeaponType.TacticalNuke then + self:T(ARTY.id..string.format("ARTY %s: Tracking of weapon starts in five seconds.", self.Controllable:GetName())) + timer.scheduleFunction(_TrackWeapon, EventData.weapon, timer.getTime() + 5.0) + end + + -- Get current ammo. local _nammo,_nshells,_nrockets,_nmissiles=self:GetAmmo() @@ -1725,6 +1897,11 @@ function ARTY:_FireAtCoord(coord, radius, nshells, weapontype) -- Controllable. local group=self.Controllable --Wrapper.Group#GROUP + + -- Tactical nukes are actually cannon shells. + if weapontype==ARTY.WeaponType.TacticalNuke then + weapontype=ARTY.WeaponType.Cannon + end -- Set ROE to weapon free. group:OptionROEOpenFire() @@ -2254,6 +2431,8 @@ function ARTY:_WeaponTypeName(tnumber) name="Guided Missiles" elseif tnumber==ARTY.WeaponType.AntiShipMissile then name="Anti-Ship Missiles" + elseif tnumber==ARTY.WeaponType.TacticalNuke then + name="Tactical Nukes" end return name end From 0553e4b14eace4021e001e71f8497b16c1347ad7 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 28 May 2018 06:12:48 +0200 Subject: [PATCH 134/420] Fixing AI_CARGO_HELICOPTER to interprete the real height correctly and also allow for pickup when the helo is still speeding more than 7 km/h. --- Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua | 6 +----- .../Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua | 8 +------- Moose Development/Moose/AI/AI_Cargo_Helicopter.lua | 4 ++-- Moose Development/Moose/Wrapper/Group.lua | 2 +- 4 files changed, 5 insertions(+), 15 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 378c881fe..6bce0816e 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -13,11 +13,7 @@ -- @extends Core.Fsm#FSM ---- # AI\_CARGO\_DISPATCHER class, extends @{Core.Fsm#FSM} --- --- === --- --- AI\_CARGO\_DISPATCHER brings a dynamic cargo handling capability for AI groups. +--- A dynamic cargo handling capability for AI groups. -- -- Carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. -- The AI\_CARGO\_DISPATCHER module uses the @{Cargo} capabilities within the MOOSE framework, to enable Carrier GROUP objects diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index bfa86e06e..8a53f1069 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -15,13 +15,7 @@ -- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER ---- # AI\_CARGO\_DISPATCHER\_HELICOPTER class, extends @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} --- --- ![Banner Image](..\Presentations\AI_CARGO_DISPATCHER_HELICOPTER\Dia1.JPG) --- --- === --- --- AI\_CARGO\_DISPATCHER\_HELICOPTER brings a dynamic cargo handling capability for AI helicopter groups. +--- A dynamic cargo handling capability for AI helicopter groups. -- -- Helicopters can be mobilized to intelligently transport infantry and other cargo within the simulation. -- The AI\_CARGO\_DISPATCHER\_HELICOPTER module uses the @{Cargo} capabilities within the MOOSE framework. diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index b0725cc0a..1983540d5 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -219,7 +219,7 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To ) self:F( { Helicopter:GetName(), Height = Helicopter:GetHeight( true ), Velocity = Helicopter:GetVelocityKMH() } ) if self.RoutePickup == true then - if Helicopter:GetHeight( true ) <= 2 and Helicopter:GetVelocityKMH() < 5 then + if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 7 then self:Load( Helicopter:GetPointVec2() ) self.RoutePickup = false self.Relocating = true @@ -227,7 +227,7 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To ) end if self.RouteDeploy == true then - if Helicopter:GetHeight( true ) <= 2 and Helicopter:GetVelocityKMH() < 5 then + if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 7 then self:Unload( true ) self.RouteDeploy = false self.Transporting = false diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index f0abf4069..18b34e171 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -543,7 +543,7 @@ function GROUP:GetHeight( FromGround ) local GroupPosition = DCSUnit:getPosition() if FromGround == true then - local LandHeight = land.getHeight( { GroupPosition.p.x, GroupPosition.p.z } ) + local LandHeight = land.getHeight( { x = GroupPosition.p.x, y = GroupPosition.p.z } ) GroupHeight = GroupHeight + ( GroupPosition.p.y - LandHeight ) else GroupHeight = GroupHeight + GroupPosition.p.y From c5dcd63dea33899f1fd40d7cf343676f2a9b3cdf Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 28 May 2018 06:39:41 +0200 Subject: [PATCH 135/420] Updated Settings documentation. --- Moose Development/Moose/Core/Settings.lua | 50 +++++++++++------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/Moose Development/Moose/Core/Settings.lua b/Moose Development/Moose/Core/Settings.lua index 8bd51a63b..77de7dac2 100644 --- a/Moose Development/Moose/Core/Settings.lua +++ b/Moose Development/Moose/Core/Settings.lua @@ -33,29 +33,29 @@ -- -- So, when there isn't any **Player setting** defined for a player for a specific setting, or, the player cannot be identified, the **Default setting** will be used instead. -- --- ## \_SETTINGS object +-- # 1) \_SETTINGS object -- -- MOOSE defines by default a singleton object called **\_SETTINGS**. Use this object to modify all the **Default settings** for a running mission. -- For each player, MOOSE will automatically allocate also a **player settings** object, and will expose a radio menu to allow the player to adapt the settings to his own preferences. -- --- ## SETTINGS Menu +-- # 2) SETTINGS Menu -- -- Settings can be adapted by the Players and by the Mission Administrator through **radio menus, which are automatically available in the mission**. -- These menus can be found **on level F10 under "Settings"**. There are two kinds of menus generated by the system. -- --- ### Default settings menu +-- ## 2.1) Default settings menu -- -- A menu is created automatically per Command Center that allows to modify the **Default** settings. -- So, when joining a CC unit, a menu will be available that allows to change the settings parameters **FOR ALL THE PLAYERS**! -- Note that the **Default settings** will only be used when a player has not choosen its own settings. -- --- ### Player settings menu +-- ## 2.2) Player settings menu -- -- A menu is created automatically per Player Slot (group) that allows to modify the **Player** settings. -- So, when joining a slot, a menu wil be available that allows to change the settings parameters **FOR THE PLAYER ONLY**! -- Note that when a player has not chosen a specific setting, the **Default settings** will be used. -- --- ### Show or Hide the Player Setting menus +-- ## 2.3) Show or Hide the Player Setting menus -- -- Of course, it may be requried not to show any setting menus. In this case, a method is available on the **\_SETTINGS object**. -- Use @{#SETTINGS.SetPlayerMenuOff}() to hide the player menus, and use @{#SETTINGS.SetPlayerMenuOn}() show the player menus. @@ -69,14 +69,14 @@ -- -- But only when a player exits and reenters the slot these settings will have effect! -- -- --- ## Settings +-- # 3) Settings -- -- There are different settings that are managed and applied within the MOOSE framework. -- See below a comprehensive description of each. -- --- ### **A2G coordinates** display formatting +-- ## 3.1) **A2G coordinates** display formatting -- --- #### A2G coordinates setting **types** +-- ### 3.1.1) A2G coordinates setting **types** -- -- Will customize which display format is used to indicate A2G coordinates in text as part of the Command Center communications. -- @@ -85,11 +85,11 @@ -- - A2G LL DMS: Lattitude Longitude [Degrees Minutes Seconds](https://en.wikipedia.org/wiki/Geographic_coordinate_conversion). The accuracy can also be adapted. -- - A2G LL DDM: Lattitude Longitude [Decimal Degrees Minutes](https://en.wikipedia.org/wiki/Decimal_degrees). The accuracy can also be adapted. -- --- #### A2G coordinates setting **menu** +-- ### 3.1.2) A2G coordinates setting **menu** -- -- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot. -- --- #### A2G coordinates setting **methods** +-- ### 3.1.3) A2G coordinates setting **methods** -- -- There are different methods that can be used to change the **System settings** using the \_SETTINGS object. -- @@ -98,14 +98,14 @@ -- - @{#SETTINGS.SetA2G_LL_DMS}(): Enable the LL DMS display formatting by default. Use @{SETTINGS.SetLL_Accuracy}() to adapt the accuracy of the Seconds formatting. -- - @{#SETTINGS.SetA2G_LL_DDM}(): Enable the LL DDM display formatting by default. Use @{SETTINGS.SetLL_Accuracy}() to adapt the accuracy of the Seconds formatting. -- --- #### A2G coordinates setting - additional notes +-- ### 3.1.4) A2G coordinates setting - additional notes -- -- One additional note on BR. In a situation when a BR coordinate should be given, -- but there isn't any player context (no player unit to reference from), the MGRS formatting will be applied! -- --- ### **A2A coordinates** formatting +-- ## 3.2) **A2A coordinates** formatting -- --- #### A2A coordinates setting **types** +-- ### 3.2.1) A2A coordinates setting **types** -- -- Will customize which display format is used to indicate A2A coordinates in text as part of the Command Center communications. -- @@ -115,11 +115,11 @@ -- - A2A LL DDM: Lattitude Longitude [Decimal Degrees and Minutes](https://en.wikipedia.org/wiki/Decimal_degrees). The accuracy can also be adapted. -- - A2A BULLS: [Bullseye](http://falcon4.wikidot.com/concepts:bullseye). -- --- #### A2A coordinates setting **menu** +-- ### 3.2.2) A2A coordinates setting **menu** -- -- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot. -- --- #### A2A coordinates setting **methods** +-- ### 3.2.3) A2A coordinates setting **methods** -- -- There are different methods that can be used to change the **System settings** using the \_SETTINGS object. -- @@ -129,34 +129,34 @@ -- - @{#SETTINGS.SetA2A_LL_DDM}(): Enable the LL DDM display formatting by default. Use @{SETTINGS.SetLL_Accuracy}() to adapt the accuracy of the Seconds formatting. -- - @{#SETTINGS.SetA2A_BULLS}(): Enable the BULLSeye display formatting by default. -- --- #### A2A coordinates settings - additional notes +-- ### 3.2.4) A2A coordinates settings - additional notes -- -- One additional note on BRAA. In a situation when a BRAA coordinate should be given, -- but there isn't any player context (no player unit to reference from), the MGRS formatting will be applied! -- --- ### **Measurements** formatting +-- ## 3.3) **Measurements** formatting -- --- #### Measurements setting **types** +-- ### 3.3.1) Measurements setting **types** -- -- Will customize the measurements system being used as part as part of the Command Center communications. -- -- - **Metrics** system: Applies the [Metrics system](https://en.wikipedia.org/wiki/Metric_system) ... -- - **Imperial** system: Applies the [Imperial system](https://en.wikipedia.org/wiki/Imperial_units) ... -- --- #### Measurements setting **menu** +-- ### 3.3.2) Measurements setting **menu** -- -- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot. -- --- #### Measurements setting **methods** +-- ### 3.3.3) Measurements setting **methods** -- -- There are different methods that can be used to change the **Default settings** using the \_SETTINGS object. -- -- - @{#SETTINGS.SetMetric}(): Enable the Metric system. -- - @{#SETTINGS.SetImperial}(): Enable the Imperial system. -- --- ### **Message** display times +-- ## 3.4) **Message** display times -- --- #### Message setting **types** +-- ### 3.4.1) Message setting **types** -- -- There are various **Message Types** that will influence the duration how long a message will appear as part of the Command Center communications. -- @@ -166,7 +166,7 @@ -- - **Overview report**: Provides a short report overview, the summary of the report. -- - **Detailed report**: Provides a complete report. -- --- #### Message setting **menu** +-- ### 3.4.2) Message setting **menu** -- -- The settings can be changed by using the **Default settings menu** on the Command Center or the **Player settings menu** on the Player Slot. -- @@ -175,7 +175,7 @@ -- So the player can choose its own amount of seconds how long a message should be displayed of a certain type. -- Note that **Update** messages can be chosen not to be displayed at all! -- --- #### 3.4.3. Message setting **methods** +-- ### 3.4.3) Message setting **methods** -- -- There are different methods that can be used to change the **System settings** using the \_SETTINGS object. -- @@ -723,7 +723,7 @@ do -- SETTINGS end --- Removes the player menu from the PlayerUnit. - --- @param #SETTINGS self + -- @param #SETTINGS self -- @param Wrapper.Client#CLIENT PlayerUnit -- @return #SETTINGS self function SETTINGS:RemovePlayerMenu( PlayerUnit ) From 21ce0cac8d15d9a26bf2636b7538f0eb00296bf2 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 28 May 2018 14:38:04 +0200 Subject: [PATCH 136/420] ARTY v0.9.3 ARTY: - Improved tac nukes implementation. Added user functions. - Added relocation option after engagements. COORDINATE: - Added get surface type function. --- Moose Development/Moose/Core/Point.lua | 10 + .../Moose/Functional/Artillery.lua | 246 ++++++++++++++---- 2 files changed, 212 insertions(+), 44 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 53e8bc543..155b2529e 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -958,6 +958,16 @@ do -- COORDINATE return Path end + --- Gets the surface type at the coordinate. + -- @param #COORDINATE self + -- @return Dcs.DCSland#SurfaceType Surface type. + function COORDINATE:GetSurfaceType() + local vec2=self:GetVec2() + local surface=land.getSurfaceType(vec2) + self:MarkToAll("Surface type = "..surface) + return surface + end + --- Creates an explosion at the point of a certain intensity. -- @param #COORDINATE self -- @param #number ExplosionIntensity diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 78978518a..1434522f4 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -53,6 +53,7 @@ -- @field #number Nshells0 Initial amount of shells of the whole group. -- @field #number Nrockets0 Initial amount of rockets of the whole group. -- @field #number Nmissiles0 Initial amount of missiles of the whole group. +-- @field #number Nukes0 Initial amount of tactical nukes of the whole group. -- @field #number FullAmmo Full amount of all ammunition taking the number of alive units into account. -- @field #number StatusInterval Update interval in seconds between status updates. Default 10 seconds. -- @field #number WaitForShotTime Max time in seconds to wait until fist shot event occurs after target is assigned. If time is passed without shot, the target is deleted. Default is 300 seconds. @@ -79,9 +80,13 @@ -- @field #number minrange Minimum firing range in kilometers. Targets closer than this distance are not engaged. Default 0.5 km. -- @field #number maxrange Maximum firing range in kilometers. Targets further away than this distance are not engaged. Default 10000 km. -- @field #number nukewarhead Explosion strength of tactical nuclear warhead in kg TNT. Default 75000. +-- @field #number Nukes Number of nuclear shells, the group has available. Default is same number as normal shells. Note that if normal shells are empty, firing nukes is also not possible any more. -- @field #number nukerange Demolition range of tactical nuclear explostions. -- @field #boolean nukefire Ignite additional fires and smoke for nuclear explosions Default true. --- @field #number nukefires Number of nuclear fires. +-- @field #number nukefires Number of nuclear fires and subexplosions. +-- @field #boolean relocateafterfire Group will relocate after each firing task. Default false. +-- @field #number relocateRmin Minimum distance in meters the group will look for places to relocate. +-- @field #number relocateRmax Maximum distance in meters the group will look for places to relocate. -- @extends Core.Fsm#FSM_CONTROLLABLE ---# ARTY class, extends @{Core.Fsm#FSM_CONTROLLABLE} @@ -343,6 +348,7 @@ ARTY={ Nshells0=0, Nrockets0=0, Nmissiles0=0, + Nukes0=0, FullAmmo=0, StatusInterval=10, WaitForShotTime=300, @@ -370,9 +376,13 @@ ARTY={ minrange=500, maxrange=1000000, nukewarhead=75000, - nukerange=nil, - nukefire=true, + Nukes=nil, + nukefire=false, nukefires=nil, + nukerange=nil, + relocateafterfire=false, + relocateRmin=300, + relocateRmax=800, } --- Weapong type ID. http://wiki.hoggit.us/view/DCS_enum_weapon_flag @@ -385,7 +395,7 @@ ARTY.WeaponType={ GuidedMissile=268402688, CruiseMissile=2097152, AntiShipMissile=65536, - TacticalNuke=666, + TacticalNukes=666, } --- Some ID to identify who we are in output of the DCS.log file. @@ -394,7 +404,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #string version -ARTY.version="0.9.2" +ARTY.version="0.9.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -468,7 +478,6 @@ function ARTY:New(group) -- Maximum speed in km/h. self.SpeedMax=group:GetSpeedMax() - --self.SpeedMax=self.DCSdesc.speedMax*3.6 -- Set speed to 0.7 of maximum. self.Speed=self.SpeedMax * 0.7 @@ -610,9 +619,9 @@ end --- Assign coordinate to where the ARTY group should move. -- @param #ARTY self --- @param Core.Point#COORDINATE coord Coordinates of the target. --- @param #string time (Optional) Day time at which the group should start moving. Passed as a string in format "08:13:45". --- @param #number speed (Optinal) Speed in km/h the group should move at. Default 50 km/h. +-- @param Core.Point#COORDINATE coord Coordinates of the new position. +-- @param #string time (Optional) Day time at which the group should start moving. Passed as a string in format "08:13:45". Default is now. +-- @param #number speed (Optinal) Speed in km/h the group should move at. Default 70% of max posible speed of group. -- @param #boolean onroad (Optional) If true, group will mainly use roads. Default off, i.e. go directly towards the specified coordinate. -- @param #boolean cancel (Optional) If true, cancel any running attack when move should begin. Default is false. -- @param #string name (Optional) Name of the coordinate. Default is LL DMS string of the coordinate. If the name was already given, the numbering "#01", "#02",... is appended automatically. @@ -629,8 +638,18 @@ function ARTY:AssignMoveCoord(coord, time, speed, onroad, cancel, name) -- Default is current time if no time was specified. time=time or self:_SecondsToClock(timer.getAbsTime()) - -- Default speed is 50 km/h. - speed=speed or 50 + -- Get max speed of group. + local speedmax=self.Controllable:GetSpeedMax() + + -- Default speed is 70% of max speed. + if speed then + speed=math.min(speed, speedmax) + elseif self.Speed then + speed=self.Speed + else + speed=speedmax*0.7 + end + -- Default is off road. if onroad==nil then @@ -648,6 +667,10 @@ function ARTY:AssignMoveCoord(coord, time, speed, onroad, cancel, name) -- Prepare move array. local _move={name=_name, coord=coord, time=_time, speed=speed, onroad=onroad, cancel=cancel} + if self.Debug then + coord:MarkToAll(string.format("Battery %s move position.", self.Controllable:GetName())) + end + -- Add to table. table.insert(self.moves, _move) @@ -831,6 +854,51 @@ function ARTY:SetMissileTypes(tableofnames) end end +--- Set number of tactical nuclear warheads available to the group. +-- Note that it can be max the number of normal shells. Also if all normal shells are empty, firing nuclear shells is also not possible any more until group gets rearmed. +-- @param #ARTY self +-- @param #number n Number of warheads for the whole group. +function ARTY:SetTacNukeShells(n) + self.Nukes=n +end + +--- Set nuclear warhead explosion strength. +-- @param #ARTY self +-- @param #number strength Explosion strength in kilo tons TNT. Default is 0.075 kt. +function ARTY:SetTacNukeWarhead(strength) + self.nukewarhead=strength or 0.075 + self.nukewarhead=self.nukewarhead*1000*1000 -- convert to kg TNT. +end + +--- Set nuclear fires and extra demolition explosions. +-- @param #ARTY self +-- @param #number nfires (Optional) Number of big smoke and fire objects created in the demolition zone. +-- @param #number demolitionrange (Optional) Demolition range in meters. +function ARTY:SetTacNukeFires(nfires, range) + self.nukefire=true + self.nukefires=nfires + self.nukerange=range +end + +--- Set relocate after firing. Group will find a new location after each engagement. Default is off +-- @param #ARTY self +-- @param #number switch (Optional) If true, activate relocation. If false, deactivate relocation. +function ARTY:SetRelocateAfterEngagement(switch) + if switch==nil then + switch=true + end + self.relocateafterfire=switch +end + +--- Set relocation distance. +-- @param #ARTY self +-- @param #number rmax (Optional) Max distance in meters, the group will move to relocate. Default is 800 m. +-- @param #number rmin (Optional) Min distance in meters, the group will move to relocate. Default is 300 m. +function ARTY:SetRelocateDistance(rmax, rmin) + self.relocateRmax=rmax or 800 + self.relocateRmin=rmin or 300 +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- FSM Start Event ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -859,6 +927,11 @@ function ARTY:onafterStart(Controllable, From, Event, To) if self.nukefires==nil then self.nukefires=20/1000/1000*self.nukerange*self.nukerange end + if self.Nukes==nil then + self.Nukes0=self.Nshells0 + else + self.Nukes0=self.Nukes + end local text=string.format("\n******************************************************\n") text=text..string.format("Arty group = %s\n", Controllable:GetName()) @@ -874,6 +947,10 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("Number of shells = %d\n", self.Nshells0) text=text..string.format("Number of rockets = %d\n", self.Nrockets0) text=text..string.format("Number of missiles = %d\n", self.Nmissiles0) + text=text..string.format("Number of nukes = %d\n", self.Nukes0) + text=text..string.format("Nuclear warhead = %d tons TNT\n", self.nukewarhead/1000) + text=text..string.format("Nuclear demolition = %d m\n", self.nukerange) + text=text..string.format("Nuclear fires = %d (active=%s)\n", self.nukefires, tostring(self.nukefire)) if self.RearmingGroup or self.RearmingPlaceCoord then text=text..string.format("Rearming safe dist. = %d m\n", self.RearmingDistance) end @@ -887,9 +964,9 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("Rearming coord dist = %d m\n", dist) text=text..string.format("Rearming ARTY roads = %s\n", tostring(self.RearmingArtyOnRoad)) end - text=text..string.format("Nuclear warhead = %d tons TNT\n", self.nukewarhead/1000) - text=text..string.format("Nuclear demolition = %d m\n", self.nukerange) - text=text..string.format("Nuclear fires = %d (active=%s)\n", self.nukefires, tostring(self.nukefire)) + text=text..string.format("Relocate after fire = %s\n", tostring(self.relocateafterfire)) + text=text..string.format("Relocate min dist. = %d\n m", self.relocateRmin) + text=text..string.format("Relocate max dist. = %d\n m", self.relocateRmax) text=text..string.format("******************************************************\n") text=text..string.format("Targets:\n") for _, target in pairs(self.targets) do @@ -948,6 +1025,7 @@ function ARTY:_StatusReport() text=text..string.format("Number of shells = %d\n", Nshells) text=text..string.format("Number of rockets = %d\n", Nrockets) text=text..string.format("Number of missiles = %d\n", Nmissiles) + text=text..string.format("Number of nukes = %d\n", self.Nukes) if self.currentTarget then text=text..string.format("Current Target = %s\n", tostring(self.currentTarget.name)) text=text..string.format("Curr. Tgt assigned = %d\n", Tnow-self.currentTarget.Tassigned) @@ -987,9 +1065,9 @@ function ARTY:_NuclearBlast(_coord) _coord:Explosion(S0) -- Huge fire at direct impact point. - if self.nukefire then - _coord:BigSmokeAndFireHuge() - end + --if self.nukefire then + _coord:BigSmokeAndFireHuge() + --end -- Create a table of fire coordinates within the demolition zone. local _fires={} @@ -1030,7 +1108,7 @@ function ARTY:_NuclearBlast(_coord) end end - if self.nukefire then + if self.nukefire==true then ignite(_fires) end @@ -1144,21 +1222,25 @@ function ARTY:_OnEventShot(EventData) -- Create a "nuclear" explosion and blast at the impact point. SCHEDULER:New(nil, ARTY._NuclearBlast, {self,_impactcoord}, 1.0) - --self:_NuclearBlast(_impactcoord) end end -- Start track the shell if we want to model a tactical nuke. - if self.currentTarget.weapontype==ARTY.WeaponType.TacticalNuke then - self:T(ARTY.id..string.format("ARTY %s: Tracking of weapon starts in five seconds.", self.Controllable:GetName())) - timer.scheduleFunction(_TrackWeapon, EventData.weapon, timer.getTime() + 5.0) + if self.currentTarget.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes>0 then + self:T(ARTY.id..string.format("ARTY %s: Tracking of weapon starts in two seconds.", self.Controllable:GetName())) + timer.scheduleFunction(_TrackWeapon, EventData.weapon, timer.getTime() + 2.0) end -- Get current ammo. local _nammo,_nshells,_nrockets,_nmissiles=self:GetAmmo() + + -- Decrease available nukes. + if self.currentTarget.weapontype==ARTY.WeaponType.TacticalNukes then + self.Nukes=self.Nukes-1 + end if _nammo==0 then @@ -1173,30 +1255,36 @@ function ARTY:_OnEventShot(EventData) -- Weapon type name for current target. local _weapontype=self:_WeaponTypeName(self.currentTarget.weapontype) self:T(ARTY.id..string.format("Group %s ammo: total=%d, shells=%d, rockets=%d, missiles=%d", self.Controllable:GetName(), _nammo, _nshells, _nrockets, _nmissiles)) - self:T2(ARTY.id..string.format("Group %s uses weapontype %s for current target.", self.Controllable:GetName(), _weapontype)) + self:T(ARTY.id..string.format("Group %s uses weapontype %s for current target.", self.Controllable:GetName(), _weapontype)) -- Special weapon type requested ==> Check if corresponding ammo is empty. if self.currentTarget.weapontype==ARTY.WeaponType.Cannon and _nshells==0 then - self:T(ARTY.id.."Group %s, cannons requested but shells empty.", self.Controllable:GetName()) + self:T(ARTY.id..string.format("Group %s, cannons requested but shells empty.", self.Controllable:GetName())) + self:CeaseFire(self.currentTarget) + return + + elseif self.currentTarget.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes<=0 then + + self:T(ARTY.id..string.format("Group %s, tactical nukes requested but nukes empty.", self.Controllable:GetName())) self:CeaseFire(self.currentTarget) return elseif self.currentTarget.weapontype==ARTY.WeaponType.Rockets and _nrockets==0 then - self:T(ARTY.id.."Group %s, rockets requested but rockets empty.", self.Controllable:GetName()) + self:T(ARTY.id..string.format("Group %s, rockets requested but rockets empty.", self.Controllable:GetName())) self:CeaseFire(self.currentTarget) return elseif self.currentTarget.weapontype==ARTY.WeaponType.UnguidedAny and _nshells+_nrockets==0 then - self:T(ARTY.id.."Group %s, unguided weapon requested but shells AND rockets empty.", self.Controllable:GetName()) + self:T(ARTY.id..string.format("Group %s, unguided weapon requested but shells AND rockets empty.", self.Controllable:GetName())) self:CeaseFire(self.currentTarget) return elseif (self.currentTarget.weapontype==ARTY.WeaponType.GuidedMissile or self.currentTarget.weapontype==ARTY.WeaponType.CruiseMissile or self.currentTarget.weapontype==ARTY.WeaponType.AntiShipMissile) and _nmissiles==0 then - self:T(ARTY.id.."Group %s, guided, anti-ship or cruise missiles requested but all missiles empty.", self.Controllable:GetName()) + self:T(ARTY.id..string.format("Group %s, guided, anti-ship or cruise missiles requested but all missiles empty.", self.Controllable:GetName())) self:CeaseFire(self.currentTarget) return @@ -1389,6 +1477,35 @@ function ARTY:onbeforeOpenFire(Controllable, From, Event, To, target) return false end + -- Get ammo. + local Nammo, Nshells, Nrockets, Nmissiles=self:GetAmmo() + local nfire=Nammo + if target.weapontype==ARTY.WeaponType.Auto then + nfire=Nammo + elseif target.weapontype==ARTY.WeaponType.Cannon then + nfire=Nshells + elseif target.weapontype==ARTY.WeaponType.TacticalNukes then + nfire=self.Nukes + elseif target.weapontype==ARTY.WeaponType.Rockets then + nfire=Nrockets + elseif target.weapontype==ARTY.WeaponType.UnguidedAny then + nfire=Nshells+Nrockets + elseif target.weapontype==ARTY.WeaponType.GuidedMissile then + nfire=Nmissiles + elseif target.weapontype==ARTY.WeaponType.CruiseMissile then + nfire=Nmissiles + elseif target.weapontype==ARTY.WeaponType.AntiShipMissile then + nfire=Nmissiles + end + + -- Adjust if less than requested ammo is left. + target.nshells=math.min(target.nshells, nfire) + + -- No ammo left ==> deny transition. + if target.nshells<1 then + return false + end + return true end @@ -1401,10 +1518,7 @@ end -- @param #table target Array holding the target info. function ARTY:onafterOpenFire(Controllable, From, Event, To, target) self:_EventFromTo("onafterOpenFire", Event, From, To) - - --local _coord=target.coord --Core.Point#COORDINATE - --_coord:MarkToAll("Arty Target") - + -- Get target array index. local id=self:_GetTargetIndexByName(target.name) @@ -1425,37 +1539,46 @@ function ARTY:onafterOpenFire(Controllable, From, Event, To, target) local Nammo, Nshells, Nrockets, Nmissiles=self:GetAmmo() local nfire=Nammo local _type="shots" - if self.WeaponType==ARTY.WeaponType.Auto then + if target.weapontype==ARTY.WeaponType.Auto then nfire=Nammo _type="shots" - elseif self.WeaponType==ARTY.WeaponType.Cannon then + elseif target.weapontype==ARTY.WeaponType.Cannon then nfire=Nshells _type="shells" - elseif self.WeaponType==ARTY.WeaponType.Rockets then + elseif target.weapontype==ARTY.WeaponType.TacticalNukes then + nfire=self.Nukes + _type="nuclear shells" + elseif target.weapontype==ARTY.WeaponType.Rockets then nfire=Nrockets _type="rockets" - elseif self.WeaponType==ARTY.WeaponType.UnguidedAny then + elseif target.weapontype==ARTY.WeaponType.UnguidedAny then nfire=Nshells+Nrockets _type="shells or rockets" - elseif self.WeaponType==ARTY.WeaponType.GuidedMissile then + elseif target.weapontype==ARTY.WeaponType.GuidedMissile then nfire=Nmissiles _type="guided missiles" - elseif self.WeaponType==ARTY.WeaponType.CruiseMissile then + elseif target.weapontype==ARTY.WeaponType.CruiseMissile then nfire=Nmissiles _type="cruise missiles" - elseif self.WeaponType==ARTY.WeaponType.AntiShipMissile then + elseif target.weapontype==ARTY.WeaponType.AntiShipMissile then nfire=Nmissiles _type="anti-ship missiles" end -- Adjust if less than requested ammo is left. - local _n=math.min(target.nshells, nfire) + target.nshells=math.min(target.nshells, nfire) -- Send message. - local text=string.format("%s, opening fire on target %s with %d %s. Distance %.1f km.", Controllable:GetName(), target.name, _n, _type, range/1000) + local text=string.format("%s, opening fire on target %s with %d %s. Distance %.1f km.", Controllable:GetName(), target.name, target.nshells, _type, range/1000) self:T(ARTY.id..text) MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report) + if self.Debug then + local _coord=target.coord --Core.Point#COORDINATE + local text=string.format("ARTY %s, Target %s, n=%d, weapon=%s", self.Controllable:GetName(), target.name, target.nshells, self:_WeaponTypeName(target.weapontype)) + _coord:MarkToAll(text) + end + -- Start firing. self:_FireAtCoord(target.coord, target.radius, target.nshells, target.weapontype) @@ -1510,6 +1633,11 @@ function ARTY:onafterCeaseFire(Controllable, From, Event, To, target) -- ARTY group has no current target any more. self.currentTarget=nil + + -- Relocate position + if self.relocateafterfire then + self:_Relocate() + end end @@ -1654,6 +1782,9 @@ function ARTY:onafterRearmed(Controllable, From, Event, To) self:T(ARTY.id..text) MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) + -- "Rearm" tactical nukes as well. + self.Nukes=self.Nukes0 + -- Route ARTY group back to where it came from (if distance is > 100 m). local d1=self.Controllable:GetCoordinate():Get2DDistance(self.InitialCoord) if d1 > self.RearmingDistance then @@ -1899,7 +2030,7 @@ function ARTY:_FireAtCoord(coord, radius, nshells, weapontype) local group=self.Controllable --Wrapper.Group#GROUP -- Tactical nukes are actually cannon shells. - if weapontype==ARTY.WeaponType.TacticalNuke then + if weapontype==ARTY.WeaponType.TacticalNukes then weapontype=ARTY.WeaponType.Cannon end @@ -1916,7 +2047,33 @@ function ARTY:_FireAtCoord(coord, radius, nshells, weapontype) group:SetTask(fire) end +--- Relocate to another position, e.g. after an engagement to avoid couter strikes. +-- @param #ARTY self +function ARTY:_Relocate() + -- Current position. + local _pos=self.Controllable:GetCoordinate() + + local _new=nil + local _gotit=false + local _n=0 + local _nmax=1000 + repeat + -- Get a random coordinate. + _new=_pos:GetRandomCoordinateInRadius(self.relocateRmax, self.relocateRmin) + local _surface=_new:GetSurfaceType() + + -- Check that new coordinate is not water(-ish). + if _surface~=land.SurfaceType.WATER and _surface~=land.SurfaceType.SHALLOW_WATER then + _gotit=true + end + until _gotit or _n>_nmax + + -- Assign relocation + if _gotit then + self:AssignMoveCoord(_new, nil, nil, false, false) + end +end --- Sort targets with respect to priority and number of times it was already engaged. -- @param #ARTY self @@ -2042,7 +2199,8 @@ end -- @param #ARTY self -- @return #table Target which is due to be attacked now or nil if no target could be found. function ARTY:_CheckNormalTargets() - + self:F3() + -- Sort targets w.r.t. prio and number times engaged already. self:_SortTargetQueuePrio() @@ -2403,7 +2561,7 @@ function ARTY:_TargetInRange(target) end -- Remove target if ARTY group cannot move. No change to be ever in range. - if self.Speed==0 then + if self.SpeedMax<1 and _inrange==false then self:RemoveTarget(target.name) end @@ -2431,7 +2589,7 @@ function ARTY:_WeaponTypeName(tnumber) name="Guided Missiles" elseif tnumber==ARTY.WeaponType.AntiShipMissile then name="Anti-Ship Missiles" - elseif tnumber==ARTY.WeaponType.TacticalNuke then + elseif tnumber==ARTY.WeaponType.TacticalNukes then name="Tactical Nukes" end return name From cb6673332d2110cc6f61df00e8c32a50ce11663d Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 28 May 2018 16:47:32 +0200 Subject: [PATCH 137/420] ARTY v0.9.4 ARTY: improved outofammo/rearming behaviour --- Moose Development/Moose/Core/Point.lua | 1 - .../Moose/Functional/Artillery.lua | 63 +++++++++++-------- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 155b2529e..0a90f6440 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -964,7 +964,6 @@ do -- COORDINATE function COORDINATE:GetSurfaceType() local vec2=self:GetVec2() local surface=land.getSurfaceType(vec2) - self:MarkToAll("Surface type = "..surface) return surface end diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 1434522f4..a31ce26c0 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -404,7 +404,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #string version -ARTY.version="0.9.3" +ARTY.version="0.9.4" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1242,14 +1242,10 @@ function ARTY:_OnEventShot(EventData) self.Nukes=self.Nukes-1 end - if _nammo==0 then - + local _outofammo=false + if _nammo==0 then self:T(ARTY.id..string.format("Group %s completely out of ammo.", self.Controllable:GetName())) - self:CeaseFire(self.currentTarget) - self:Winchester() - - -- Current target is deallocated ==> return - return + _outofammo=true end -- Weapon type name for current target. @@ -1258,47 +1254,65 @@ function ARTY:_OnEventShot(EventData) self:T(ARTY.id..string.format("Group %s uses weapontype %s for current target.", self.Controllable:GetName(), _weapontype)) -- Special weapon type requested ==> Check if corresponding ammo is empty. + local _partlyoutofammo=false if self.currentTarget.weapontype==ARTY.WeaponType.Cannon and _nshells==0 then self:T(ARTY.id..string.format("Group %s, cannons requested but shells empty.", self.Controllable:GetName())) - self:CeaseFire(self.currentTarget) - return + _partlyoutofammo=true elseif self.currentTarget.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes<=0 then self:T(ARTY.id..string.format("Group %s, tactical nukes requested but nukes empty.", self.Controllable:GetName())) - self:CeaseFire(self.currentTarget) - return + _partlyoutofammo=true elseif self.currentTarget.weapontype==ARTY.WeaponType.Rockets and _nrockets==0 then self:T(ARTY.id..string.format("Group %s, rockets requested but rockets empty.", self.Controllable:GetName())) - self:CeaseFire(self.currentTarget) - return + _partlyoutofammo=true elseif self.currentTarget.weapontype==ARTY.WeaponType.UnguidedAny and _nshells+_nrockets==0 then self:T(ARTY.id..string.format("Group %s, unguided weapon requested but shells AND rockets empty.", self.Controllable:GetName())) - self:CeaseFire(self.currentTarget) - return + _partlyoutofammo=true elseif (self.currentTarget.weapontype==ARTY.WeaponType.GuidedMissile or self.currentTarget.weapontype==ARTY.WeaponType.CruiseMissile or self.currentTarget.weapontype==ARTY.WeaponType.AntiShipMissile) and _nmissiles==0 then self:T(ARTY.id..string.format("Group %s, guided, anti-ship or cruise missiles requested but all missiles empty.", self.Controllable:GetName())) - self:CeaseFire(self.currentTarget) - return + _partlyoutofammo=true - end + end -- Check if number of shots reached max. + local _ceasefire=false if self.Nshots >= self.currentTarget.nshells then local text=string.format("Group %s stop firing on target %s.", self.Controllable:GetName(), self.currentTarget.name) self:T(ARTY.id..text) MESSAGE:New(text, 5):ToAllIf(self.Debug) -- Cease fire. + _ceasefire=true + end + + -- Check if we are (partly) out of ammo. + if _outofammo or _partlyoutofammo then + _ceasefire=true + end + + -- Cease fire on current target. + if _ceasefire then self:CeaseFire(self.currentTarget) end + + -- Group is out of ammo (or partly and can rearm) ==> Winchester (==> Rearm). + if _outofammo or (_partlyoutofammo and self.RearmingGroup ~=nil) then + self:Winchester() + return + end + + -- Relocate position + if self.Nshots >= self.currentTarget.nshells and self.relocateafterfire then + self:_Relocate() + end else self:E(ARTY.id..string.format("ERROR: No current target for group %s?!", self.Controllable:GetName())) @@ -1633,12 +1647,7 @@ function ARTY:onafterCeaseFire(Controllable, From, Event, To, target) -- ARTY group has no current target any more. self.currentTarget=nil - - -- Relocate position - if self.relocateafterfire then - self:_Relocate() - end - + end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -2776,11 +2785,11 @@ function ARTY:_Move(group, ToCoord, Speed, OnRoad) local _last=ToCoord:GetClosestPointToRoad() -- First point on road. - path[#path+1]=_first:WaypointGround(Speed, "On road") + path[#path+1]=_first:WaypointGround(Speed, "On Road") task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, false) -- Last point on road. - path[#path+1]=_last:WaypointGround(Speed, "On road") + path[#path+1]=_last:WaypointGround(Speed, "On Road") task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, false) end From 89275048014ea17b3b8d322319ee1e5182ef3980 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 28 May 2018 16:51:01 +0200 Subject: [PATCH 138/420] Minor --- Moose Development/Moose/Functional/Artillery.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index a31ce26c0..b6c0b86b0 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -1085,7 +1085,7 @@ function ARTY:_NuclearBlast(_coord) -- At R=R0 ==> explosion strength is 1% of S0 at impact point. local alpha=math.log(100) local strength=S0*math.exp(-alpha*R/R0) - env.info(string.format("FF: nuklear explosion strength s(%.1f m) = %.10f (s/s0=%.1f %%), alpha=%.3f", R, strength, strength/S0*100, alpha)) + self:T2(ARTY.id..string.format("Nuclear explosion strength s(%.1f m) = %.5f (s/s0=%.1f %%), alpha=%.3f", R, strength, strength/S0*100, alpha)) return strength end @@ -1096,7 +1096,7 @@ function ARTY:_NuclearBlast(_coord) -- Get distance to impact and calc exponential explosion strength. local R=_fire:Get2DDistance(_coord) local S=_explosion(R) - env.info(string.format("FF: explosion r=%.1f, s=%.3f", R, S)) + self:T2(ARTY.id..string.format("Explosion r=%.1f, s=%.3f", R, S)) -- Get a random Big Smoke and fire object. local _preset=math.random(0,7) From 1861e742b023d00c9c4288d2bfc0a03d7359ea52 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 28 May 2018 17:07:55 +0200 Subject: [PATCH 139/420] ARTY v0.9.5 Added/fixed relocate option --- Moose Development/Moose/Functional/Artillery.lua | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index b6c0b86b0..1242e44cd 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -404,7 +404,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #string version -ARTY.version="0.9.4" +ARTY.version="0.9.5" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1284,6 +1284,7 @@ function ARTY:_OnEventShot(EventData) -- Check if number of shots reached max. local _ceasefire=false + local _relocate=false if self.Nshots >= self.currentTarget.nshells then local text=string.format("Group %s stop firing on target %s.", self.Controllable:GetName(), self.currentTarget.name) self:T(ARTY.id..text) @@ -1291,6 +1292,10 @@ function ARTY:_OnEventShot(EventData) -- Cease fire. _ceasefire=true + + if self.relocateafterfire then + _relocate=true + end end -- Check if we are (partly) out of ammo. @@ -1310,7 +1315,7 @@ function ARTY:_OnEventShot(EventData) end -- Relocate position - if self.Nshots >= self.currentTarget.nshells and self.relocateafterfire then + if _relocate then self:_Relocate() end @@ -2078,7 +2083,7 @@ function ARTY:_Relocate() end until _gotit or _n>_nmax - -- Assign relocation + -- Assign relocation. if _gotit then self:AssignMoveCoord(_new, nil, nil, false, false) end From f03f9a33088676ce214d0519c8ce74f710b16818 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Mon, 28 May 2018 20:03:26 +0200 Subject: [PATCH 140/420] Landing event made a bit more loose. --- Moose Development/Moose/AI/AI_Cargo_Helicopter.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 1983540d5..a92dd1e95 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -219,7 +219,7 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To ) self:F( { Helicopter:GetName(), Height = Helicopter:GetHeight( true ), Velocity = Helicopter:GetVelocityKMH() } ) if self.RoutePickup == true then - if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 7 then + if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 10 then self:Load( Helicopter:GetPointVec2() ) self.RoutePickup = false self.Relocating = true @@ -227,7 +227,7 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To ) end if self.RouteDeploy == true then - if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 7 then + if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 10 then self:Unload( true ) self.RouteDeploy = false self.Transporting = false From b6ac79a9dfa4f767c345dc9c0c8ae3203b29d29c Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 28 May 2018 22:19:35 +0200 Subject: [PATCH 141/420] ARTY Mark WIP --- Moose Development/Moose/Core/Event.lua | 9 +++++ .../Moose/Functional/Artillery.lua | 37 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index dd739a0d9..57559d965 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -933,6 +933,15 @@ function EVENT:onEvent( Event ) --Event.WeaponTgtDCSUnit = Event.Weapon:getTarget() end + if Event.idx then + Event.MarkID=Event.idx + Event.MarkVec3=Event.pos + Event.MarkCoordinate=COORDINATE:NewFromVec3(Event.pos) + Event.MarkText=Event.text + Event.MarkCoalition=Event.coalition + Event.MarkGroupID = Event.groupID + end + if Event.cargo then Event.Cargo = Event.cargo Event.CargoName = Event.cargo.Name diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 1242e44cd..ba6300655 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -1003,11 +1003,34 @@ function ARTY:onafterStart(Controllable, From, Event, To) -- Add event handler. self:HandleEvent(EVENTS.Shot, self._OnEventShot) self:HandleEvent(EVENTS.Dead, self._OnEventDead) + self:HandleEvent(EVENTS.MarkAdded, self._OnEventMarkAdded) + + -- Add DCS event handler. + world.addEventHandler(self) -- Start checking status. self:__Status(self.StatusInterval) end +--- After "Start" event. Initialized ROE and alarm state. Starts the event handler. +-- @param #ARTY self +-- @param #table Event +function ART:onEvent(Event) + + if Event then + + if Event.id==world.event.S_EVENT_MARK_ADDED then + env.info("FF mark added") + elseif Event.id==world.event.S_EVENT_MARK_CHANGE then + env.info("FF mark changed") + elseif Event.id==world.event.S_EVENT_MARK_REMOVED then + env.info("FF mark removed") + end + + end + +end + --- After "Start" event. Initialized ROE and alarm state. Starts the event handler. -- @param #ARTY self function ARTY:_StatusReport() @@ -1159,6 +1182,18 @@ function ARTY:_NuclearBlast(_coord) end +--- Eventhandler for shot event. +-- @param #ARTY self +-- @param Core.Event#EVENTDATA EventData +function ARTY:_OnMarkAdded(EventData) + self:F(EventData) + if EventData.MarkCoordinate then + local coord=EventData.MarkCoordinate --Core.Point#COORDINATE + + coord:SmokeGreen() + end +end + --- Eventhandler for shot event. -- @param #ARTY self -- @param Core.Event#EVENTDATA EventData @@ -2081,6 +2116,8 @@ function ARTY:_Relocate() if _surface~=land.SurfaceType.WATER and _surface~=land.SurfaceType.SHALLOW_WATER then _gotit=true end + -- Increase counter. + _n=_n+1 until _gotit or _n>_nmax -- Assign relocation. From 43b320ca9063877c91c5dc21c24a882d4e3e7d6d Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Tue, 29 May 2018 22:34:33 +0200 Subject: [PATCH 142/420] New versions with documentation --- .../Moose/AI/AI_A2A_Dispatcher.lua | 2 +- .../Moose/Functional/Artillery.lua | 10 +--- .../Moose/Functional/CleanUp.lua | 6 +-- .../Moose/Functional/Designate.lua | 7 ++- .../Moose/Functional/Detection.lua | 11 ++-- Moose Development/Moose/Functional/Escort.lua | 54 +++++++++---------- .../Moose/Functional/MissileTrainer.lua | 3 +- .../Moose/Functional/PseudoATC.lua | 5 +- Moose Development/Moose/Functional/RAT.lua | 9 +--- Moose Development/Moose/Functional/Range.lua | 9 +--- .../Moose/Functional/Scoring.lua | 7 +-- Moose Development/Moose/Functional/Sead.lua | 1 + .../Moose/Functional/Suppression.lua | 6 +-- .../Moose/Functional/ZoneCaptureCoalition.lua | 5 +- .../Moose/Tasking/CommandCenter.lua | 5 +- .../Moose/Tasking/DetectionManager.lua | 5 +- Moose Development/Moose/Tasking/Mission.lua | 1 + Moose Development/Moose/Tasking/Task_A2A.lua | 4 +- .../Moose/Tasking/Task_A2A_Dispatcher.lua | 9 ++-- Moose Development/Moose/Tasking/Task_A2G.lua | 4 +- .../Moose/Tasking/Task_A2G_Dispatcher.lua | 6 +-- .../Moose/Tasking/Task_Cargo_CSAR.lua | 3 +- .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 6 +-- .../Moose/Tasking/Task_Cargo_Transport.lua | 4 +- Moose Development/Moose/Wrapper/Airbase.lua | 5 +- Moose Development/Moose/Wrapper/Client.lua | 5 +- .../Moose/Wrapper/Controllable.lua | 6 +-- Moose Development/Moose/Wrapper/Group.lua | 6 +-- .../Moose/Wrapper/Identifiable.lua | 4 +- Moose Development/Moose/Wrapper/Object.lua | 4 +- .../Moose/Wrapper/Positionable.lua | 5 +- Moose Development/Moose/Wrapper/Scenery.lua | 5 +- Moose Development/Moose/Wrapper/Static.lua | 5 +- Moose Development/Moose/Wrapper/Unit.lua | 6 +-- 34 files changed, 85 insertions(+), 148 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index 146f35d4d..6f0c7045d 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -731,7 +731,7 @@ do -- AI_A2A_DISPATCHER -- -- In the mission editor, setup a group with task Refuelling. A tanker unit of the correct coalition will be automatically selected. -- Then, use the method @{#AI_A2A_DISPATCHER.SetDefaultTanker}() to set the tanker for the dispatcher. - -- Use the method @{#AI_A2A_DISPATCHER.SetDefaultFuelTreshold}() to set the %-tage left in the defender airplane tanks when a refuel action is needed. + -- Use the method @{#AI_A2A_DISPATCHER.SetDefaultFuelThreshold}() to set the %-tage left in the defender airplane tanks when a refuel action is needed. -- -- When the tanker specified is alive and in the air, the tanker will be used for refuelling. -- diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 1242e44cd..acd0811d4 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -1,12 +1,7 @@ -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- **Functional** - (R2.4) Control artillery units. -- -- === -- --- ![Banner Image](..\Presentations\ARTY\ARTY_Main.png) --- --- ==== --- -- The ARTY class can be used to easily assign and manage targets for artillery units. -- -- ## Features: @@ -39,6 +34,7 @@ -- -- ==== -- @module Functional.Arty +-- @image Artillery.JPG ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- ARTY class @@ -89,9 +85,7 @@ -- @field #number relocateRmax Maximum distance in meters the group will look for places to relocate. -- @extends Core.Fsm#FSM_CONTROLLABLE ----# ARTY class, extends @{Core.Fsm#FSM_CONTROLLABLE} --- --- The ARTY class enables mission designers easily to assign targets for artillery units. Since the implementation is based on a Finite State Model (FSM), the mission designer can +--- Enables mission designers easily to assign targets for artillery units. Since the implementation is based on a Finite State Model (FSM), the mission designer can -- interact with the process at certain events or states. -- -- A new ARTY object can be created with the @{#ARTY.New}(*group*) contructor. diff --git a/Moose Development/Moose/Functional/CleanUp.lua b/Moose Development/Moose/Functional/CleanUp.lua index f7414e190..04f4be5b6 100644 --- a/Moose Development/Moose/Functional/CleanUp.lua +++ b/Moose Development/Moose/Functional/CleanUp.lua @@ -8,6 +8,7 @@ -- === -- -- @module Functional.CleanUp +-- @image CleanUp_Airbases.JPG --- @type CLEANUP_AIRBASE.__ Methods which are not intended for mission designers, but which are used interally by the moose designer :-) -- @field #map<#string,Wrapper.Airbase#AIRBASE> Airbases Map of Airbases. @@ -16,11 +17,8 @@ --- @type CLEANUP_AIRBASE -- @extends #CLEANUP_AIRBASE.__ ---- # CLEANUP_AIRBASE, extends @{Core.Base#BASE} +--- Keeps airbases clean, and tries to guarantee continuous airbase operations, even under combat. -- --- ![Banner Image](..\Presentations\CLEANUP_AIRBASE\Dia1.JPG) --- --- The CLEANUP_AIRBASE class keeps airbases clean, and tries to guarantee continuous airbase operations, even under combat. -- Specific airbases need to be provided that need to be guarded. Each airbase registered, will be guarded within a zone of 8 km around the airbase. -- Any unit that fires a missile, or shoots within the zone of an airbase, will be monitored by CLEANUP_AIRBASE. -- Within the 8km zone, units cannot fire any missile, which prevents the airbase runway to receive missile or bomb hits. diff --git a/Moose Development/Moose/Functional/Designate.lua b/Moose Development/Moose/Functional/Designate.lua index dce33f924..c6818c456 100644 --- a/Moose Development/Moose/Functional/Designate.lua +++ b/Moose Development/Moose/Functional/Designate.lua @@ -2,7 +2,7 @@ -- -- === -- --- DESIGNATE is orchestrating the designation of potential targets executed by a Recce group, +-- Orchestrate the designation of potential targets executed by a Recce group, -- and communicates these to a dedicated attacking group of players, -- so that following a dynamically generated menu system, -- each detected set of potential targets can be lased or smoked... @@ -29,16 +29,15 @@ -- * **FlightControl**: Design & Programming -- -- @module Functional.Designate - +-- @image Designation.JPG do -- DESIGNATE --- @type DESIGNATE -- @extends Core.Fsm#FSM_PROCESS - --- # DESIGNATE class, extends @{Fsm#FSM} + --- Manage the designation of detected targets. -- - -- DESIGNATE is managing the designation of detected targets. -- Targets detected by recce will be communicated to a group of attacking players. -- A menu system is made available that allows to: -- diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 93755e614..30de4a170 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -2,11 +2,7 @@ -- -- === -- --- ![Banner Image](..\Presentations\DETECTION\Dia1.JPG) --- --- === --- --- DETECTION classes facilitate the detection of enemy units within the battle zone executed by FACs (Forward Air Controllers) or RECCEs (Reconnassance Units). +-- Facilitate the detection of enemy units within the battle zone executed by FACs (Forward Air Controllers) or RECCEs (Reconnassance Units). -- DETECTION uses the in-built detection capabilities of DCS World, but adds new functionalities. -- -- Find the DETECTION classes documentation further in this document in the globals section. @@ -30,6 +26,7 @@ -- * FlightControl : Analysis, Design, Programming, Testing -- -- @module Functional.Detection +-- @image Detection.JPG ----BASE:TraceClass("DETECTION_BASE") ----BASE:TraceClass("DETECTION_AREAS") @@ -46,9 +43,7 @@ do -- DETECTION_BASE -- @field #number DetectionRun -- @extends Core.Fsm#FSM - --- DETECTION_BASE class, extends @{Fsm#FSM} - -- - -- The DETECTION_BASE class defines the core functions to administer detected objects. + --- Defines the core functions to administer detected objects. -- The DETECTION_BASE class will detect objects within the battle zone for a list of @{Wrapper.Group}s detecting targets following (a) detection method(s). -- -- ## DETECTION_BASE constructor diff --git a/Moose Development/Moose/Functional/Escort.lua b/Moose Development/Moose/Functional/Escort.lua index 755770c84..b2c27bdc9 100644 --- a/Moose Development/Moose/Functional/Escort.lua +++ b/Moose Development/Moose/Functional/Escort.lua @@ -2,61 +2,60 @@ -- -- === -- --- @{#ESCORT} class --- === --- The @{#ESCORT} class allows you to interact with escorting AI on your flight and take the lead. +-- Allows you to interact with escorting AI on your flight and take the lead. +-- -- Each escorting group can be commanded with a whole set of radio commands (radio menu in your flight, and then F10). -- -- The radio commands will vary according the category of the group. The richest set of commands are with Helicopters and AirPlanes. -- Ships and Ground troops will have a more limited set, but they can provide support through the bombing of targets designated by the other escorts. -- --- RADIO MENUs that can be created: --- === +-- # RADIO MENUs that can be created: +-- -- Find a summary below of the current available commands: -- --- Navigation ...: --- --------------- +-- ## Navigation ...: +-- -- Escort group navigation functions: -- -- * **"Join-Up and Follow at x meters":** The escort group fill follow you at about x meters, and they will follow you. -- * **"Flare":** Provides menu commands to let the escort group shoot a flare in the air in a color. -- * **"Smoke":** Provides menu commands to let the escort group smoke the air in a color. Note that smoking is only available for ground and naval troops. -- --- Hold position ...: --- ------------------ +-- ## Hold position ...: +-- -- Escort group navigation functions: -- -- * **"At current location":** Stops the escort group and they will hover 30 meters above the ground at the position they stopped. -- * **"At client location":** Stops the escort group and they will hover 30 meters above the ground at the position they stopped. -- --- Report targets ...: --- ------------------- +-- ## Report targets ...: +-- -- Report targets will make the escort group to report any target that it identifies within a 8km range. Any detected target can be attacked using the 4. Attack nearby targets function. (see below). -- -- * **"Report now":** Will report the current detected targets. -- * **"Report targets on":** Will make the escort group to report detected targets and will fill the "Attack nearby targets" menu list. -- * **"Report targets off":** Will stop detecting targets. -- --- Scan targets ...: --- ----------------- +-- ## Scan targets ...: +-- -- Menu items to pop-up the escort group for target scanning. After scanning, the escort group will resume with the mission or defined task. -- -- * **"Scan targets 30 seconds":** Scan 30 seconds for targets. -- * **"Scan targets 60 seconds":** Scan 60 seconds for targets. -- --- Attack targets ...: --- ------------------- +-- ## Attack targets ...: +-- -- This menu item will list all detected targets within a 15km range. Depending on the level of detection (known/unknown) and visuality, the targets type will also be listed. -- --- Request assistance from ...: --- ---------------------------- +-- ## Request assistance from ...: +-- -- This menu item will list all detected targets within a 15km range, as with the menu item **Attack Targets**. -- This menu item allows to request attack support from other escorts supporting the current client group. -- eg. the function allows a player to request support from the Ship escort to attack a target identified by the Plane escort with its Tomahawk missiles. -- eg. the function allows a player to request support from other Planes escorting to bomb the unit with illumination missiles or bombs, so that the main plane escort can attack the area. -- --- ROE ...: --- -------- +-- ## ROE ...: +-- -- Sets the Rules of Engagement (ROE) of the escort group when in flight. -- -- * **"Hold Fire":** The escort group will hold fire. @@ -64,8 +63,8 @@ -- * **"Open Fire":** The escort group will open fire on designated targets. -- * **"Weapon Free":** The escort group will engage with any target. -- --- Evasion ...: --- ------------ +-- ## Evasion ...: +-- -- Will define the evasion techniques that the escort group will perform during flight or combat. -- -- * **"Fight until death":** The escort group will have no reaction to threats. @@ -73,19 +72,19 @@ -- * **"Evade enemy fire":** The rescort group will evade enemy fire before firing. -- * **"Go below radar and evade fire":** The escort group will perform evasive vertical manoeuvres. -- --- Resume Mission ...: --- ------------------- +-- ## Resume Mission ...: +-- -- Escort groups can have their own mission. This menu item will allow the escort group to resume their Mission from a given waypoint. -- Note that this is really fantastic, as you now have the dynamic of taking control of the escort groups, and allowing them to resume their path or mission. -- --- ESCORT construction methods. --- === +-- # ESCORT construction methods. +-- -- Create a new SPAWN object with the @{#ESCORT.New} method: -- -- * @{#ESCORT.New}: Creates a new ESCORT object from a @{Wrapper.Group#GROUP} for a @{Wrapper.Client#CLIENT}, with an optional briefing text. -- --- ESCORT initialization methods. --- === +-- # ESCORT initialization methods. +-- -- The following menus are created within the RADIO MENU (F10) of an active unit hosted by a player: -- -- * @{#ESCORT.MenuFollowAt}: Creates a menu to make the escort follow the client. @@ -115,6 +114,7 @@ -- -- -- @module Functional.Escort +-- @image Escorting.JPG --- ESCORT class -- @type ESCORT diff --git a/Moose Development/Moose/Functional/MissileTrainer.lua b/Moose Development/Moose/Functional/MissileTrainer.lua index 151964515..cc8830a14 100644 --- a/Moose Development/Moose/Functional/MissileTrainer.lua +++ b/Moose Development/Moose/Functional/MissileTrainer.lua @@ -2,8 +2,6 @@ -- -- === -- --- 1) @{MissileTrainer#MISSILETRAINER} class, extends @{Core.Base#BASE} --- === -- The @{#MISSILETRAINER} class uses the DCS world messaging system to be alerted of any missiles fired, and when a missile would hit your aircraft, -- the class will destroy the missile within a certain range, to avoid damage to your aircraft. -- It suports the following functionality: @@ -78,6 +76,7 @@ -- Together with the **476 virtual team**, we tested the MISSILETRAINER class, and got much positive feedback! -- -- @module Functional.MissileTrainer +-- @image Missile_Trainer.JPG --- The MISSILETRAINER class diff --git a/Moose Development/Moose/Functional/PseudoATC.lua b/Moose Development/Moose/Functional/PseudoATC.lua index c1a639029..b8a9f2cd0 100644 --- a/Moose Development/Moose/Functional/PseudoATC.lua +++ b/Moose Development/Moose/Functional/PseudoATC.lua @@ -1,4 +1,3 @@ -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- **Functional** - (R2.4) Rudimentary ATC. -- -- ![Banner Image](..\Presentations\PSEUDOATC\PSEUDOATC_Main.jpg) @@ -39,6 +38,7 @@ -- -- ==== -- @module Functional.PseudoATC +-- @image Pseudo_ATC.JPG ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- PSEUDOATC class @@ -53,8 +53,7 @@ -- @field #boolean eventsmoose If true, events are handled by MOOSE. If false, events are handled directly by DCS eventhandler. -- @extends Core.Base#BASE ----# PSEUDOATC class, extends @{Core.Base#BASE} --- The PSEUDOATC class adds some rudimentary ATC functionality via the radio menu. +--- The PSEUDOATC class adds some rudimentary ATC functionality via the radio menu. -- -- Local weather reports can be requested for nearby airports and player's mission waypoints. -- The weather report includes diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 31b3fc369..9e192a795 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -1,12 +1,7 @@ -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- **Functional** - (R2.2) - Create random airtraffic in your missions. -- -- === -- --- ![Banner Image](..\Presentations\RAT\RAT.png) --- --- === --- -- The aim of the RAT class is to fill the empty DCS world with randomized air traffic and bring more life to your airports. -- -- In particular, it is designed to spawn AI air units at random airports. These units will be assigned a random flight path to another random airport on the map. @@ -60,6 +55,7 @@ -- -- === -- @module Functional.Rat +-- @module RAT.JPG ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- RAT class @@ -152,8 +148,7 @@ -- @field #boolean useparkingdb Parking spots are added to data base once an aircraft has used it. These spots can later be used by other aircraft. Default is true. -- @extends Core.Spawn#SPAWN ----# RAT class, extends @{Core.Spawn#SPAWN} --- The RAT class implements an easy to use way to randomly fill your map with AI aircraft. +--- The RAT class implements an easy to use way to randomly fill your map with AI aircraft. -- -- -- ## Airport Selection diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index c21f6656f..a7448cecd 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -1,12 +1,7 @@ -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- **Functional** - (R2.3) - Range Practice. -- -- === -- --- ![Banner Image](..\Presentations\RANGE\RANGE_Main.png) --- --- === --- -- The RANGE class enables easy set up of bombing and strafing ranges within DCS World. -- -- Implementation is based on the [Simple Range Script](https://forums.eagle.ru/showthread.php?t=157991) by [Ciribob](https://forums.eagle.ru/member.php?u=112175), which itself was motivated @@ -49,6 +44,7 @@ -- -- === -- @module Functional.Range +-- @image Range.JPG ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- RANGE class @@ -87,8 +83,7 @@ -- @field #boolean trackmissiles If true (default), all missile types are tracked and impact point to closest bombing target is evaluated. -- @extends Core.Base#BASE ----# RANGE class, extends @{Core.Base#BASE} --- The RANGE class enables a mission designer to easily set up practice ranges in DCS. A new RANGE object can be created with the @{#RANGE.New}(rangename) contructor. +--- The RANGE class enables a mission designer to easily set up practice ranges in DCS. A new RANGE object can be created with the @{#RANGE.New}(rangename) contructor. -- The parameter "rangename" defindes the name of the range. It has to be unique since this is also the name displayed in the radio menu. -- -- Generally, a range consists of strafe pits and bombing targets. For strafe pits the number of hits for each pass is counted and tabulated. diff --git a/Moose Development/Moose/Functional/Scoring.lua b/Moose Development/Moose/Functional/Scoring.lua index c6468a1e5..6d0204b22 100644 --- a/Moose Development/Moose/Functional/Scoring.lua +++ b/Moose Development/Moose/Functional/Scoring.lua @@ -2,11 +2,7 @@ -- -- === -- --- ![Banner Image](..\Presentations\SCORING\Dia1.JPG) --- --- === --- --- The @{#SCORING} class administers the scoring of player achievements, +-- Administers the scoring of player achievements, -- and creates a CSV file logging the scoring events and results for use at team or squadron websites. -- -- SCORING automatically calculates the threat level of the objects hit and destroyed by players, @@ -207,6 +203,7 @@ -- * **FlightControl**: Concept, Design & Programming. -- -- @module Functional.Scoring +-- @image Scoring.JPG --- The Scoring class diff --git a/Moose Development/Moose/Functional/Sead.lua b/Moose Development/Moose/Functional/Sead.lua index d9d9bfffb..db25b81e1 100644 --- a/Moose Development/Moose/Functional/Sead.lua +++ b/Moose Development/Moose/Functional/Sead.lua @@ -3,6 +3,7 @@ -- === -- -- @module Functional.Sead +-- @image SEAD.JPG --- The SEAD class -- @type SEAD diff --git a/Moose Development/Moose/Functional/Suppression.lua b/Moose Development/Moose/Functional/Suppression.lua index 929e8b718..c0d2278ef 100644 --- a/Moose Development/Moose/Functional/Suppression.lua +++ b/Moose Development/Moose/Functional/Suppression.lua @@ -1,8 +1,6 @@ ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- **Functional** - (R2.4) Suppress fire of ground units when they get hit. -- --- ![Banner Image](..\Presentations\SUPPRESSION\Suppression_Main.png) --- -- ==== -- -- When ground units get hit by (suppressive) enemy fire, they will not be able to shoot back for a certain amount of time. @@ -32,6 +30,7 @@ -- -- ==== -- @module Functional.Suppression +-- @image Suppression.JPG ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -76,8 +75,7 @@ -- @extends Core.Fsm#FSM_CONTROLLABLE -- ----# SUPPRESSION class, extends @{Core.Fsm#FSM_CONTROLLABLE} --- Mimic suppressive enemy fire and let groups flee or retreat. +--- Mimic suppressive enemy fire and let groups flee or retreat. -- -- ## Suppression Process -- diff --git a/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua b/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua index 7c1610687..b4551e87f 100644 --- a/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua +++ b/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua @@ -2,10 +2,6 @@ -- -- === -- --- ![Banner Image](..\Presentations\ZONE_CAPTURE_COALITION\Dia1.JPG) --- --- === --- -- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/CAZ - Capture Zones) -- -- - CAZ-000 - Capture Zone: Demonstrates the basic concept of capturing a zone. @@ -22,6 +18,7 @@ -- === -- -- @module Functional.ZoneCaptureCoalition +-- @image Capture_Zones.JPG do -- ZONE_CAPTURE_COALITION diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index 725a634b3..74d50dfb6 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -10,6 +10,7 @@ -- === -- -- @module Tasking.CommandCenter +-- @image Task_Command_Center.JPG @@ -23,9 +24,7 @@ -- @extends Core.Base#BASE ---- # COMMANDCENTER class, extends @{Core.Base#BASE} --- --- The COMMANDCENTER class governs multiple missions, the tasking and the reporting. +--- Governs multiple missions, the tasking and the reporting. -- -- The commandcenter communicates important messages between the various groups of human players executing tasks in missions. -- diff --git a/Moose Development/Moose/Tasking/DetectionManager.lua b/Moose Development/Moose/Tasking/DetectionManager.lua index e6a6aaf19..a716c11ff 100644 --- a/Moose Development/Moose/Tasking/DetectionManager.lua +++ b/Moose Development/Moose/Tasking/DetectionManager.lua @@ -1,9 +1,7 @@ ---- This module contains the DETECTION_MANAGER class and derived classes. +--- **Tasking** - This module contains the DETECTION_MANAGER class and derived classes. -- -- === -- --- 1) @{DetectionManager#DETECTION_MANAGER} class, extends @{Fsm#FSM} --- === -- The @{DetectionManager#DETECTION_MANAGER} class defines the core functions to report detected objects to groups. -- Reportings can be done in several manners, and it is up to the derived classes if DETECTION_MANAGER to model the reporting behaviour. -- @@ -41,6 +39,7 @@ -- ### Author: FlightControl - Framework Design & Programming -- -- @module Tasking.DetectionManager +-- @image Task_Detection_Manager.JPG do -- DETECTION MANAGER diff --git a/Moose Development/Moose/Tasking/Mission.lua b/Moose Development/Moose/Tasking/Mission.lua index e541b7bbb..ced7f3d1a 100644 --- a/Moose Development/Moose/Tasking/Mission.lua +++ b/Moose Development/Moose/Tasking/Mission.lua @@ -9,6 +9,7 @@ -- === -- -- @module Tasking.Mission +-- @image Task_Mission.JPG --- The MISSION class -- @type MISSION diff --git a/Moose Development/Moose/Tasking/Task_A2A.lua b/Moose Development/Moose/Tasking/Task_A2A.lua index b3c268100..122356bd5 100644 --- a/Moose Development/Moose/Tasking/Task_A2A.lua +++ b/Moose Development/Moose/Tasking/Task_A2A.lua @@ -17,9 +17,7 @@ do -- TASK_A2A -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK - --- # TASK_A2A class, extends @{Task#TASK} - -- - -- The TASK_A2A class defines Air To Air tasks for a @{Set} of Target Units, + --- Defines Air To Air tasks for a @{Set} of Target Units, -- based on the tasking capabilities defined in @{Task#TASK}. -- The TASK_A2A is implemented using a @{Fsm#FSM_TASK}, and has the following statuses: -- diff --git a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua index 3ca89bf8e..204eb87a3 100644 --- a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua @@ -1,6 +1,6 @@ --- **Tasking** - The TASK_A2A_DISPATCHER creates and manages player TASK_A2A tasks based on detected targets. -- --- The @{#TASK_A2A_DISPATCHER} classes implement the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of EWR installation groups. +-- Implement the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of EWR installation groups. -- -- === -- @@ -11,6 +11,7 @@ -- === -- -- @module Tasking.Task_A2A_Dispatcher +-- @image Task_A2A_Dispatcher.JPG do -- TASK_A2A_DISPATCHER @@ -18,11 +19,7 @@ do -- TASK_A2A_DISPATCHER -- @type TASK_A2A_DISPATCHER -- @extends Tasking.DetectionManager#DETECTION_MANAGER - --- # TASK_A2A_DISPATCHER class, extends @{Tasking#DETECTION_MANAGER} - -- - -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia1.JPG) - -- - -- The @{#TASK_A2A_DISPATCHER} class implements the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of EWR installation groups. + --- Orchestrates the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of EWR installation groups. -- -- ![Banner Image](..\Presentations\TASK_A2A_DISPATCHER\Dia3.JPG) -- diff --git a/Moose Development/Moose/Tasking/Task_A2G.lua b/Moose Development/Moose/Tasking/Task_A2G.lua index 59cef82ea..1c6fa1276 100644 --- a/Moose Development/Moose/Tasking/Task_A2G.lua +++ b/Moose Development/Moose/Tasking/Task_A2G.lua @@ -17,9 +17,7 @@ do -- TASK_A2G -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK - --- # TASK_A2G class, extends @{Task#TASK} - -- - -- The TASK_A2G class defines Air To Ground tasks for a @{Set} of Target Units, + --- The TASK_A2G class defines Air To Ground tasks for a @{Set} of Target Units, -- based on the tasking capabilities defined in @{Task#TASK}. -- The TASK_A2G is implemented using a @{Fsm#FSM_TASK}, and has the following statuses: -- diff --git a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua index 65d1796b7..27493d738 100644 --- a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua @@ -9,6 +9,7 @@ -- === -- -- @module Tasking.Task_A2G_Dispatcher +-- @image Task_A2G_Dispatcher.JPG do -- TASK_A2G_DISPATCHER @@ -19,11 +20,8 @@ do -- TASK_A2G_DISPATCHER -- @field Tasking.Mission#MISSION Mission -- @extends Tasking.DetectionManager#DETECTION_MANAGER - --- # TASK\_A2G\_DISPATCHER class, extends @{DetectionManager#DETECTION_MANAGER} + --- Orchestrates dynamic **A2G Task Dispatching** based on the detection results of a linked @{Detection} object. -- - -- ![](..\Presentations\TASK\_A2G\_DISPATCHER\Dia1.JPG) - -- - -- The TASK\_A2G\_DISPATCHER class orchestrates dynamic **A2G Task Dispatching** based on the detection results of a linked @{Detection} object. -- It uses the Tasking System within the MOOSE framework, which is a multi-player Tasking Orchestration system. -- It provides a truly dynamic battle environment for pilots and ground commanders to engage upon, -- in a true co-operation environment wherein **Multiple Teams** will collaborate in Missions to **achieve a common Mission Goal**. diff --git a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua index 1326cec30..616f8a89b 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua @@ -1,10 +1,9 @@ --- **Tasking** -- Models tasks for players to execute CSAR @{Cargo} downed pilots. -- --- ![Banner Image](..\Presentations\TASK_CARGO\Dia1.JPG) --- -- === -- -- @module Tasking.Task_Cargo_CSAR +-- @image Task_Cargo_CSAR.JPG do -- TASK_CARGO_CSAR diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index 9603f5f4f..16dc70cf0 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -22,11 +22,7 @@ do -- TASK_CARGO_DISPATCHER -- @field Tasking.Task#TASK Task - --- # TASK_CARGO_DISPATCHER class, extends @{Task_Manager#TASK_MANAGER} - -- - -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia1.JPG) - -- - -- The @{#TASK_CARGO_DISPATCHER} class implements the dynamic dispatching of cargo tasks. + --- Implements the dynamic dispatching of cargo tasks. -- -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia3.JPG) -- diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua index f906b87f8..88bcb0ecb 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua @@ -1,9 +1,9 @@ --- **Tasking** -- The TASK_CARGO models tasks for players to transport @{Cargo}. -- --- ![Banner Image](..\Presentations\TASK_CARGO\Dia1.JPG) --- -- === +-- -- @module Tasking.Task_Cargo_Transport +-- @image Task_Cargo_Transport.JPG do -- TASK_CARGO_TRANSPORT diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 1f7f98cbf..50c331293 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -9,14 +9,13 @@ -- === -- -- @module Wrapper.Airbase +-- @image Wrapper_Airbase.JPG --- @type AIRBASE -- @extends Wrapper.Positionable#POSITIONABLE ---- # AIRBASE class, extends @{Wrapper.Positionable#POSITIONABLE} --- --- AIRBASE is a wrapper class to handle the DCS Airbase objects: +--- AIRBASE is a wrapper class to handle the DCS Airbase objects: -- -- * Support all DCS Airbase APIs. -- * Enhance with Airbase specific APIs not in the DCS Airbase API set. diff --git a/Moose Development/Moose/Wrapper/Client.lua b/Moose Development/Moose/Wrapper/Client.lua index 4f9b80f83..0c1eefdaf 100644 --- a/Moose Development/Moose/Wrapper/Client.lua +++ b/Moose Development/Moose/Wrapper/Client.lua @@ -9,6 +9,7 @@ -- === -- -- @module Wrapper.Client +-- @image Wrapper_Client.JPG --- The CLIENT class @@ -16,9 +17,7 @@ -- @extends Wrapper.Unit#UNIT ---- # CLIENT class, extends @{Wrapper.Unit#UNIT} --- --- Clients are those **Units** defined within the Mission Editor that have the skillset defined as __Client__ or __Player__. +--- Clients are those **Units** defined within the Mission Editor that have the skillset defined as __Client__ or __Player__. -- Note that clients are NOT the same as Units, they are NOT necessarily alive. -- The CLIENT class is a wrapper class to handle the DCS Unit objects that have the skillset defined as __Client__ or __Player__: -- diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 0bd00a060..5e7a15ef9 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -9,7 +9,7 @@ -- === -- -- @module Wrapper.Controllable - +-- @image Wrapper_Controllable.JPG --- @type CONTROLLABLE @@ -19,9 +19,7 @@ ---- # CONTROLLABLE class, extends @{Wrapper.Positionable#POSITIONABLE} --- --- CONTROLLABLE is a wrapper class to handle the "DCS Controllable objects", which are Groups and Units: +--- CONTROLLABLE is a wrapper class to handle the "DCS Controllable objects", which are Groups and Units: -- -- * Support all DCS Controllable APIs. -- * Enhance with Controllable specific APIs not in the DCS Controllable API set. diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 18b34e171..fe4d5e20b 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -24,6 +24,7 @@ -- === -- -- @module Wrapper.Group +-- @image Wrapper_Group.JPG --- @type GROUP @@ -31,10 +32,7 @@ -- @field #string GroupName The name of the group. ---- --- # GROUP class, extends @{Wrapper.Controllable#CONTROLLABLE} --- --- For each DCS Group object alive within a running mission, a GROUP wrapper object (instance) will be created within the _@{DATABASE} object. +--- For each DCS Group object alive within a running mission, a GROUP wrapper object (instance) will be created within the _@{DATABASE} object. -- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Group objects are spawned (using the @{SPAWN} class). -- -- The GROUP class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference diff --git a/Moose Development/Moose/Wrapper/Identifiable.lua b/Moose Development/Moose/Wrapper/Identifiable.lua index bbc6e482b..94d946f3c 100644 --- a/Moose Development/Moose/Wrapper/Identifiable.lua +++ b/Moose Development/Moose/Wrapper/Identifiable.lua @@ -14,9 +14,7 @@ -- @extends Wrapper.Object#OBJECT -- @field #string IdentifiableName The name of the identifiable. ---- # IDENTIFIABLE class, extends @{Wrapper.Object#OBJECT} --- --- The IDENTIFIABLE class is a wrapper class to handle the DCS Identifiable objects: +--- The IDENTIFIABLE class is a wrapper class to handle the DCS Identifiable objects: -- -- * Support all DCS Identifiable APIs. -- * Enhance with Identifiable specific APIs not in the DCS Identifiable API set. diff --git a/Moose Development/Moose/Wrapper/Object.lua b/Moose Development/Moose/Wrapper/Object.lua index 6438cf807..b2b034a30 100644 --- a/Moose Development/Moose/Wrapper/Object.lua +++ b/Moose Development/Moose/Wrapper/Object.lua @@ -16,9 +16,7 @@ -- @field #string ObjectName The name of the Object. ---- # OBJECT class, extends @{Core.Base#BASE} --- --- OBJECT handles the DCS Object objects: +--- OBJECT handles the DCS Object objects: -- -- * Support all DCS Object APIs. -- * Enhance with Object specific APIs not in the DCS Object API set. diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index d01a7c26b..296cc0119 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -9,6 +9,7 @@ -- === -- -- @module Wrapper.Positionable +-- @image Wrapper_Positionable.JPG --- @type POSITIONABLE.__ Methods which are not intended for mission designers, but which are used interally by the moose designer :-) -- @extends Wrapper.Identifiable#IDENTIFIABLE @@ -17,9 +18,7 @@ -- @extends Wrapper.Identifiable#IDENTIFIABLE ---- # POSITIONABLE class, extends @{Wrapper.Identifiable#IDENTIFIABLE} --- --- The POSITIONABLE class is a wrapper class to handle the POSITIONABLE objects: +--- The POSITIONABLE class is a wrapper class to handle the POSITIONABLE objects: -- -- * Support all DCS APIs. -- * Enhance with POSITIONABLE specific APIs not in the DCS API set. diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index 8160aa955..31bfbf05a 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -9,6 +9,7 @@ -- === -- -- @module Wrapper.Scenery +-- @image Wrapper_Scenery.JPG @@ -16,9 +17,7 @@ -- @extends Wrapper.Positionable#POSITIONABLE ---- # SCENERY class, extends @{Wrapper.Positionable#POSITIONABLE} --- --- Scenery objects are defined on the map. +--- Scenery objects are defined on the map. -- The @{Scenery#SCENERY} class is a wrapper class to handle the DCS Scenery objects: -- -- * Wraps the DCS Scenery objects. diff --git a/Moose Development/Moose/Wrapper/Static.lua b/Moose Development/Moose/Wrapper/Static.lua index bab5b8d70..7ca5699b3 100644 --- a/Moose Development/Moose/Wrapper/Static.lua +++ b/Moose Development/Moose/Wrapper/Static.lua @@ -9,14 +9,13 @@ -- === -- -- @module Wrapper.Static +-- @image Wrapper_Static.JPG --- @type STATIC -- @extends Wrapper.Positionable#POSITIONABLE ---- # STATIC class, extends @{Wrapper.Positionable#POSITIONABLE} --- --- Statics are **Static Units** defined within the Mission Editor. +--- Statics are **Static Units** defined within the Mission Editor. -- Note that Statics are almost the same as Units, but they don't have a controller. -- The @{Static#STATIC} class is a wrapper class to handle the DCS Static objects: -- diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 71bb9ecfb..03278f9c7 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -18,15 +18,13 @@ -- === -- -- @module Wrapper.Unit +-- @image Wrapper_Unit.JPG --- @type UNIT -- @extends Wrapper.Controllable#CONTROLLABLE ---- --- # UNIT class, extends @{Wrapper.Controllable#CONTROLLABLE} --- --- For each DCS Unit object alive within a running mission, a UNIT wrapper object (instance) will be created within the _@{DATABASE} object. +--- For each DCS Unit object alive within a running mission, a UNIT wrapper object (instance) will be created within the _@{DATABASE} object. -- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Unit objects are spawned (using the @{SPAWN} class). -- -- The UNIT class **does not contain a :New()** method, rather it provides **:Find()** methods to retrieve the object reference From fd4d7f49a54175ffa2676566f15a1d70043faf67 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 30 May 2018 00:39:43 +0200 Subject: [PATCH 143/420] ARTY v0.9.6 Added first WIP version with Markers. --- .../Moose/Functional/Artillery.lua | 289 ++++++++++++++++-- 1 file changed, 266 insertions(+), 23 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index ba6300655..8fffe20a5 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -87,6 +87,8 @@ -- @field #boolean relocateafterfire Group will relocate after each firing task. Default false. -- @field #number relocateRmin Minimum distance in meters the group will look for places to relocate. -- @field #number relocateRmax Maximum distance in meters the group will look for places to relocate. +-- @field #boolean markallow If true, Players are allowed to assign targets and moves for ARTY group by placing markers on the F10 map. Default is false. +-- @field #number markkey Authorization key. Only player who know this key can assign targets and moves via markers on the F10 map. Default no authorization required. -- @extends Core.Fsm#FSM_CONTROLLABLE ---# ARTY class, extends @{Core.Fsm#FSM_CONTROLLABLE} @@ -366,11 +368,8 @@ ARTY={ RearmingArtyOnRoad=false, InitialCoord=nil, report=true, - --ammoshells={"weapons.shells"}, ammoshells={}, - --ammorockets={"weapons.nurs"}, ammorockets={}, - --ammomissiles={"weapons.missiles"}, ammomissiles={}, Nshots=0, minrange=500, @@ -383,6 +382,8 @@ ARTY={ relocateafterfire=false, relocateRmin=300, relocateRmax=800, + markallow=false, + markkey=nil, } --- Weapong type ID. http://wiki.hoggit.us/view/DCS_enum_weapon_flag @@ -404,7 +405,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #string version -ARTY.version="0.9.5" +ARTY.version="0.9.6" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -625,16 +626,29 @@ end -- @param #boolean onroad (Optional) If true, group will mainly use roads. Default off, i.e. go directly towards the specified coordinate. -- @param #boolean cancel (Optional) If true, cancel any running attack when move should begin. Default is false. -- @param #string name (Optional) Name of the coordinate. Default is LL DMS string of the coordinate. If the name was already given, the numbering "#01", "#02",... is appended automatically. +-- @param #boolean unique (Optional) Move is unique. If the move name is already known, the move is rejected. Default false. -- @return #string Name of the move. Can be used for further reference, e.g. deleting the move from the list. -function ARTY:AssignMoveCoord(coord, time, speed, onroad, cancel, name) - self:F({coord=coord, time=time, speed=speed, onroad=onroad, cancel=cancel, name=name}) - - -- Name of the target. - local _name=name or coord:ToStringLLDMS() +function ARTY:AssignMoveCoord(coord, time, speed, onroad, cancel, name, unique) + self:F({coord=coord, time=time, speed=speed, onroad=onroad, cancel=cancel, name=name, unique=unique}) - -- Check if the name has already been used for another target. If so, the function returns a new unique name. - _name=self:_CheckName(self.moves, _name) + -- Default + if unique==nil then + unique=false + end + -- Name of the target. + local _name=name or coord:ToStringLLDMS() + local _unique=true + + -- Check if the name has already been used for another target. If so, the function returns a new unique name. + _name,_unique=self:_CheckName(self.moves, _name, not unique) + + -- Move name should be unique and is not. + if unique==true and _unique==false then + self:T(ARTY.id..string.format("%s: move %s should have a unique name but name was already given. Rejecting move!", self.Controllable:GetName(), _name)) + return nil + end + -- Default is current time if no time was specified. time=time or self:_SecondsToClock(timer.getAbsTime()) @@ -649,8 +663,7 @@ function ARTY:AssignMoveCoord(coord, time, speed, onroad, cancel, name) else speed=speedmax*0.7 end - - + -- Default is off road. if onroad==nil then onroad=false @@ -899,6 +912,20 @@ function ARTY:SetRelocateDistance(rmax, rmin) self.relocateRmin=rmin or 300 end +--- Enable assigning targets by placing markers on the F10 map. +-- @param #ARTY self +-- @param #number key (Optional) Authorization key. Only players knowing this key can assign targets. Default is no authorization required. +function ARTY:SetMarkTargetsOn(key) + self.markkey=key + self.markallow=true +end + +--- Disable assigning targets by placing markers on the F10 map. +-- @param #ARTY self +function ARTY:SetMarkTargetsOff() + self.markallow=false +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- FSM Start Event ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1012,23 +1039,233 @@ function ARTY:onafterStart(Controllable, From, Event, To) self:__Status(self.StatusInterval) end +--- Extract engagement assignments and parameters from mark text. +-- @param #ARTY self +-- @param #string text Marker text to be analyzed. +-- @return #table Table with assignment parameters, e.g. number of shots, radius, time etc. +function ARTY:_Markertext(text) + self:F(text) + + -- Assignment parameters. + local assignment={} + assignment.battery={} + assignment.move=false + assignment.engage=false + assignment.time=nil + assignment.nshells=nil + assignment.prio=nil + assignment.maxengage=nil + assignment.radius=nil + assignment.weapontype=nil + assignment.speed=nil + assignment.onroad=nil + assignment.key=nil + + if text:lower():find("arty") then + env.info("FF: Found arty command:") + + if text:lower():find("engage") then + assignment.engage=true + elseif text:lower():find("move") then + assignment.move=true + else + self:E(ARTY.id.."ERROR: Neither ENGAGE nor MOVE keyword specified!") + return + end + + -- keywords are split by "," + local keywords=self:_split(text, ",") + + for _,key in pairs(keywords) do + + local s=self:_split(key, " ") + local val=s[2] + + -- Battery name, i.e. which ARTY group should fire. + if key:lower():find("battery") then + + local v=self:_split(text, '"') + + table.insert(assignment.battery, v[2]) + env.info(string.format("FF: Battery=%s.", v[2])) + + elseif key:lower():find("time") then + + if val:lower():find("now") then + assignment.time=self:_SecondsToClock(timer.getTime0()+5) + else + assignment.time=val + end + env.info(string.format("FF: Time=%s.", val)) + + elseif key:lower():find("shots") then + + assignment.nshells=tonumber(s[2]) + env.info(string.format("FF: Shots=%s.", val)) + + elseif key:lower():find("prio") then + + assignment.prio=tonumber(val) + env.info(string.format("FF: Prio=%s.", val)) + + elseif key:lower():find("maxengage") then + + assignment.maxengage=tonumber(val) + env.info(string.format("FF: Maxengage=%s.", val)) + + elseif key:lower():find("radius") then + + assignment.radius=tonumber(val) + env.info(string.format("Radius=%s.", val)) + + elseif key:lower():find("weapon") then + + if val:lower():find("cannon") then + assignment.weapontype=ARTY.WeaponType.Cannon + elseif val:lower():find("rocket") then + assignment.weapontype=ARTY.WeaponType.Rockets + elseif val:lower():find("missile") then + assignment.weapontype=ARTY.WeaponType.GuidedMissile + elseif val:lower():find("nuke") then + assignment.weapontype=ARTY.WeaponType.TacticalNukes + else + assignment.weapontype=ARTY.WeaponType.Auto + end + env.info(string.format("FF: Weapon=%s.", val)) + + elseif key:lower():find("speed") then + + assignment.speed=tonumber(val) + env.info(string.format("FF: Speed=%s.", val)) + + elseif key:lower():find("road") then + + assignment.onroad=true + env.info(string.format("FF: Onroad=true.")) + + elseif key:lower():find("key") then + + assignment.key=tonumber(val) + env.info(string.format("FF: Key=%s.", val)) + + end + + end + else + env.info("FF: This is NO arty command!") + end + + return assignment +end + --- After "Start" event. Initialized ROE and alarm state. Starts the event handler. -- @param #ARTY self -- @param #table Event -function ART:onEvent(Event) +function ARTY:onEvent(Event) - if Event then + if Event == nil or Event.idx == nil then + self:T3("Skipping onEvent. Event or Event.idx unknown.") + return true + end + + local batteryname=self.Controllable:GetName() + local batterycoalition=self.Controllable:GetCoalition() - if Event.id==world.event.S_EVENT_MARK_ADDED then - env.info("FF mark added") - elseif Event.id==world.event.S_EVENT_MARK_CHANGE then - env.info("FF mark changed") - elseif Event.id==world.event.S_EVENT_MARK_REMOVED then - env.info("FF mark removed") + env.info(string.format("Event captured = %s", tostring(batteryname))) + env.info(string.format("Event id = %s", tostring(Event.id))) + env.info(string.format("Event time = %s", tostring(Event.time))) + env.info(string.format("Event idx = %s", tostring(Event.idx))) + env.info(string.format("Event coalition = %s", tostring(Event.coalition))) + env.info(string.format("Event group id = %s", tostring(Event.groupID))) + env.info(string.format("Event text = %s", tostring(Event.text))) + self:E({eventid=Event.id, vec3=Event.pos}) + if Event.initiator~=nil then + local _unitname=Event.initiator:getName() + env.info(string.format("Event ini unit name = %s", tostring(_unitname))) + end + + + if Event.id==world.event.S_EVENT_MARK_ADDED then + self:E({event="S_EVENT_MARK_ADDED", vec3=Event.pos}) + + elseif Event.id==world.event.S_EVENT_MARK_CHANGE then + self:E({event="S_EVENT_MARK_CHANGE", vec3=Event.pos}) + + -- Check if marker has a text and the "arty" keyword. + if Event.text~=nil and Event.text:lower():find("arty") then + + -- Check if we have the right coalition and text has arty keyword. + if batterycoalition==Event.coalition or self.markkey~=nil then + + -- Evaluate marker text and extract parameters. + local _assign=self:_Markertext(Event.text) + + local _n=#_assign.battery + env.info("FF: number of batteries assigned to target = ".._n) + + -- Check if job is assigned to this ARTY group. Default is for all ARTY groups. + local _assigned=true + if _n>0 then + _assigned=false + for _,bat in pairs(_assign.battery) do + env.info(string.format("FF: compare %s=%s ==> %s",batteryname, bat, tostring(batteryname==bat))) + if batteryname==bat then + _assigned=true + end + end + end + + -- We are meant. + if _assigned then + + -- Convert (wrong x-->z, z-->x) vec3 + local vec3={y=Event.pos.y, x=Event.pos.z, z=Event.pos.x} + -- Get coordinate from vec3. + local _coord=COORDINATE:NewFromVec3(vec3) + + if _assign.move then + + -- Create a new name. + local _name=string.format("Marked Move ID=%d for battery %s", Event.idx, batteryname) + self:E(ARTY.id.._name) + + -- Assign a relocation of the arty group. + self:AssignMoveCoord(_coord, _assign.time, _assign.speed, _assign.onroad, _assign.cancel,_name, true) + + else + + -- Create a new name. + local _name=string.format("Marked Target ID=%d for battery %s", Event.idx, batteryname) + self:E(ARTY.id.._name) + + -- Assign a new firing engagement. + self:AssignTargetCoord(_coord,_assign.prio,_assign.radius,_assign.nshells,_assign.maxengage,_assign.time,_assign.weapontype, _name, true) + + end + end + + end + end + + elseif Event.id==world.event.S_EVENT_MARK_REMOVED then + self:E({event="S_EVENT_MARK_REMOVED", vec3=Event.pos}) + + -- Check if we have the right coalition. + if batterycoalition==Event.coalition and Event.text:lower():find("arty") then + + -- This should be the unique name of the target or move. + + if Event.text:lower():find("move") then + local _name=string.format("Marked Move ID=%d for battery %s", Event.idx, batteryname) + self:RemoveMove(_name) + else + local _name=string.format("Marked Target ID=%d for battery %s", Event.idx, batteryname) + self:RemoveTarget(_name) + end end end - + end --- After "Start" event. Initialized ROE and alarm state. Starts the event handler. @@ -1037,6 +1274,12 @@ function ARTY:_StatusReport() -- Get Ammo. local Nammo, Nshells, Nrockets, Nmissiles=self:GetAmmo() + local Nnukes + if self.Nukes==nil then + Nnukes=0 + else + Nnukes=self.Nukes + end local Tnow=timer.getTime() local Clock=self:_SecondsToClock(timer.getAbsTime()) @@ -1048,7 +1291,7 @@ function ARTY:_StatusReport() text=text..string.format("Number of shells = %d\n", Nshells) text=text..string.format("Number of rockets = %d\n", Nrockets) text=text..string.format("Number of missiles = %d\n", Nmissiles) - text=text..string.format("Number of nukes = %d\n", self.Nukes) + text=text..string.format("Number of nukes = %d\n", Nnukes) if self.currentTarget then text=text..string.format("Current Target = %s\n", tostring(self.currentTarget.name)) text=text..string.format("Curr. Tgt assigned = %d\n", Tnow-self.currentTarget.Tassigned) From da452ed8ce07a7c1636fcb019beb437e4ee4fe68 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 30 May 2018 21:17:09 +0200 Subject: [PATCH 144/420] ARTY v0.9.7 Improved Mark Target Assignments. Improved documentation. --- .../Moose/Functional/Artillery.lua | 159 +++++++++++++++--- 1 file changed, 132 insertions(+), 27 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 4ecae201b..0cbc4474d 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -12,6 +12,8 @@ -- * Special weapon types can be selected for each attack, e.g. cruise missiles for Naval units. -- * Automatic rearming once the artillery is out of ammo. -- * New targets can be added during the mission, e.g. when they are detected by recon units. +-- * Modeling of tactical nuclear shells. +-- * Targets and relocations can be assigned by placing markers on the F10 map. -- * Finite state machine implementation. Mission designer can interact when certain events occur. -- -- ==== @@ -192,9 +194,9 @@ -- Unfortunately, there is no easy way to count only those ammo types useable as artillery. Therefore, to keep the implementation general the user -- can specify the names of the ammo types by the following functions: -- --- * @{#ARTY.SetShellTypes}(*tableofnames*): Defines the ammo types for unguided cannons. Default is *tableofnames*={"weapons.shells"}, i.e. **all** types of shells are counted. --- * @{#ARTY.SetRocketTypes}(*tableofnames*): Defines the ammo types of unguided rockets. Default is *tableofnames*={"weapons.nurs"}, i.e. **all** types of rockets are counted. --- * @{#ARTY.SetMissileTypes}(*tableofnames*): Defines the ammo types of guided missiles. Default is *tableofnames*={"weapons.missiles"}, i.e. **all** types of missiles are counted. +-- * @{#ARTY.SetShellTypes}(*tableofnames*): Defines the ammo types for unguided cannons, e.g. *tableofnames*={"weapons.shells"}, i.e. **all** types of shells are counted. +-- * @{#ARTY.SetRocketTypes}(*tableofnames*): Defines the ammo types of unguided rockets, e.g. *tableofnames*={"weapons.nurs"}, i.e. **all** types of rockets are counted. +-- * @{#ARTY.SetMissileTypes}(*tableofnames*): Defines the ammo types of guided missiles, e.g. is *tableofnames*={"weapons.missiles"}, i.e. **all** types of missiles are counted. -- -- **Note** that the default parameters "weapons.shells", "weapons.nurs", "weapons.missiles" **should in priciple** capture all the corresponding ammo types. -- However, the logic searches for the string "weapon.missies" in the ammo type. Especially for missiles, this string is often not contained in the ammo type descriptor. @@ -253,10 +255,66 @@ -- -- After the rearming is complete, both groups will move back to their original positions. -- +-- ## Tactical Nukes +-- +-- ARTY groups that can fire shells can also be used to fire tactical nukes. This is simply achieved by setting the weapon type to **ARTY.WeaponType.TacticalNukes** in the +-- @{#ARTY.AssignTargetCoord}() function. +-- +-- The default explostion strength is 0.075 kilo tons TNT. The can be changed with the @{#ARTY.SetTacNukeWarhead}(*strength*), where *strength* is given in kilo tons TNT. +-- +-- By default, all available conventional shells can be used as nuclear shells. However, it is possible to restrict the number with the @{#ARTY.SetTacNukeShells}(*n*) function +-- to only have *n* nuclear shells available. Note that the group must always have convenctional shells left in order to fire a nuclear shell. +-- +-- ## Assignments via Markers on F10 Map +-- +-- Targets and relocations can be assigned by players via placing a mark on the F10 map. The marker text must contain certain keywords. +-- +-- This feature can be turned on with the @{#ARTY.SetMarkAssignmentsOn}(*key*). The parameter *key* is optional. When set, it can be used as PIN, i.e. only +-- player who know the correct key are able to assign targets or relocations. Default behavior is that all players belonging to the same coalition as the +-- ARTY group are able to assign targets and moves. +-- +-- ### Target Assignments +-- A new target can be assigned by writing **arty engage** in the marker text. This can be followed by a comma separated lists of optional keywords and parameters: +-- +-- * *time* Time for which which the engagement is schedules, e.g. 08:42. Default is as soon as possible. +-- * *prio* Priority of the engagement as number between 1 (high prio) and 100 (low prio). Default is 50. +-- * *shots* Number of shots (shells, rockets or missiles) fired at each engagement. Default is 5. +-- * *engage* Number of times the target is engaged. Default is 1. +-- * *radius* Scattering radius of the fired shots in meters. Default is 100 m. +-- * *weapon* Type of weapon to be used. Valid parameters are *cannon*, *rocket*, *missile*, *nuke*. Default is automatic selection. +-- * *battery* Name of the ARTY group that the target is assigned to. Note that the name is case sensitive and has to be given in quotation marks. Default is all ARTY groups of the right coalition. +-- * *key* A number to authorize the target assignment. Only specifing the correct number will trigger an engagement. +-- +-- Here are examples of valid marker texts: +-- arty engage! +-- arty engage! shots 20, prio 10, time 08:15, weapon cannons +-- arty engage! battery "Blue Paladin 1" "Blue MRLS 1", shots 10, time 10:15 +-- arty engage! battery "Blue Paladin 1", key 666 +-- +-- Note that the keywords and parameters are case insensitve. Only exception are the battery group names. These must be exactly the same as the names of the goups defined +-- in the mission editor. +-- +-- ### Relocation Assignments +-- +-- Markers can also be used to relocate the group with the keyphrase **arty move**. This is done in a similar way as assigning targets. Here, the (optional) keywords and parameters are: +-- +-- * *time* Time for which which the relocation/move is schedules, e.g. 08:42. Default is as soon as possible. +-- * *speed* The speed in km/h the group will drive at. Default is 70% of its max possible speed. +-- * *onroad* Group will use mainly roads. Default is off, i.e. it will go in a straight line from its current position to the assigned coordinate. +-- * *cancel* Group will cancel all running firing engagements and immidiately start to move. Default is that group will wait until is current assignment is over. +-- * *battery* Name of the ARTY group that the relocation is assigned to. +-- +-- Here are some examples: +-- arty move! time 23:45, speed 50, onroad, cancel +-- arty move! battery "Blue Paladin", onroad +-- arty move, cancel, speed 10, onroad +-- -- ## Fine Tuning -- -- The mission designer has a few options to tailor the ARTY object according to his needs. -- +-- * @{#ARTY.SetRelocateAfterEngagement}() will cause the ARTY group to change its position after each firing assignment. +-- * @{#ARTY.SetRelocateDistance}(*rmax*, *rmin*) sets the max/min distance for relocation of the group. Default distance is randomly between 300 and 800 m. -- * @{#ARTY.RemoveAllTargets}() removes all targets from the target queue. -- * @{#ARTY.RemoveTarget}(*name*) deletes the target with *name* from the target queue. -- * @{#ARTY.SetMaxFiringRange}(*range*) defines the maximum firing range. Targets further away than this distance are not engaged. @@ -399,7 +457,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #string version -ARTY.version="0.9.6" +ARTY.version="0.9.7" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -793,7 +851,7 @@ function ARTY:SetDebugOFF() self.Debug=false end ---- Delete a target from target list. +--- Delete a target from target list. If the target is currently engaged, it is cancelled. -- @param #ARTY self -- @param #string name Name of the target. function ARTY:RemoveTarget(name) @@ -804,6 +862,12 @@ function ARTY:RemoveTarget(name) table.remove(self.targets, id) end self:T(ARTY.id..string.format("Group %s: Number of targets = %d.", self.Controllable:GetName(), #self.targets)) + if self.currentTarget then + if self.currentTarget.name==name then + self:T(ARTY.id..string.format("Group %s: Cancelling current target %s (id=%d).", self.Controllable:GetName(), name, id)) + self:CeaseFire(self.currentTarget) + end + end end --- Delete a move from move list. @@ -906,10 +970,10 @@ function ARTY:SetRelocateDistance(rmax, rmin) self.relocateRmin=rmin or 300 end ---- Enable assigning targets by placing markers on the F10 map. +--- Enable assigning targets and moves by placing markers on the F10 map. -- @param #ARTY self -- @param #number key (Optional) Authorization key. Only players knowing this key can assign targets. Default is no authorization required. -function ARTY:SetMarkTargetsOn(key) +function ARTY:SetMarkAssignmentsOn(key) self.markkey=key self.markallow=true end @@ -918,6 +982,7 @@ end -- @param #ARTY self function ARTY:SetMarkTargetsOff() self.markallow=false + self.markkey=nil end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -962,8 +1027,8 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("Number of units = %d\n", self.IniGroupStrength) text=text..string.format("Speed max = %d km/h\n", self.SpeedMax) text=text..string.format("Speed default = %d km/h\n", self.Speed) - text=text..string.format("Min range = %d km\n", self.minrange/1000) - text=text..string.format("Max range = %d km\n", self.maxrange/1000) + text=text..string.format("Min range = %.1f km\n", self.minrange/1000) + text=text..string.format("Max range = %.1f km\n", self.maxrange/1000) text=text..string.format("Total ammo count = %d\n", self.Nammo0) text=text..string.format("Number of shells = %d\n", self.Nshells0) text=text..string.format("Number of rockets = %d\n", self.Nrockets0) @@ -986,8 +1051,10 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("Rearming ARTY roads = %s\n", tostring(self.RearmingArtyOnRoad)) end text=text..string.format("Relocate after fire = %s\n", tostring(self.relocateafterfire)) - text=text..string.format("Relocate min dist. = %d\n m", self.relocateRmin) - text=text..string.format("Relocate max dist. = %d\n m", self.relocateRmax) + text=text..string.format("Relocate min dist. = %d m\n", self.relocateRmin) + text=text..string.format("Relocate max dist. = %d m\n", self.relocateRmax) + text=text..string.format("Marker assignments = %s\n", tostring(self.markallow)) + text=text..string.format("Marker auth. key = %s\n", tostring(self.markkey)) text=text..string.format("******************************************************\n") text=text..string.format("Targets:\n") for _, target in pairs(self.targets) do @@ -1055,9 +1122,7 @@ function ARTY:_Markertext(text) assignment.onroad=nil assignment.key=nil - if text:lower():find("arty") then - env.info("FF: Found arty command:") - + if text:lower():find("arty") then if text:lower():find("engage") then assignment.engage=true elseif text:lower():find("move") then @@ -1071,6 +1136,7 @@ function ARTY:_Markertext(text) local keywords=self:_split(text, ",") for _,key in pairs(keywords) do + --env.info("key="..key) local s=self:_split(key, " ") local val=s[2] @@ -1078,10 +1144,12 @@ function ARTY:_Markertext(text) -- Battery name, i.e. which ARTY group should fire. if key:lower():find("battery") then - local v=self:_split(text, '"') - - table.insert(assignment.battery, v[2]) - env.info(string.format("FF: Battery=%s.", v[2])) + local v=self:_split(key, '"') + + for i=2,#v,2 do + table.insert(assignment.battery, v[i]) + env.info(string.format("FF: Battery=%s.", v[i])) + end elseif key:lower():find("time") then @@ -1110,7 +1178,7 @@ function ARTY:_Markertext(text) elseif key:lower():find("radius") then assignment.radius=tonumber(val) - env.info(string.format("Radius=%s.", val)) + env.info(string.format("FF: Radius=%s.", val)) elseif key:lower():find("weapon") then @@ -1188,18 +1256,15 @@ function ARTY:onEvent(Event) -- Check if marker has a text and the "arty" keyword. if Event.text~=nil and Event.text:lower():find("arty") then - -- Check if we have the right coalition and text has arty keyword. - if batterycoalition==Event.coalition or self.markkey~=nil then + -- Check if the coalition is the same or an authorization key has been defined. + if (batterycoalition==Event.coalition and self.markkey==nil) or self.markkey~=nil then -- Evaluate marker text and extract parameters. local _assign=self:_Markertext(Event.text) - - local _n=#_assign.battery - env.info("FF: number of batteries assigned to target = ".._n) - + -- Check if job is assigned to this ARTY group. Default is for all ARTY groups. local _assigned=true - if _n>0 then + if #_assign.battery>0 then _assigned=false for _,bat in pairs(_assign.battery) do env.info(string.format("FF: compare %s=%s ==> %s",batteryname, bat, tostring(batteryname==bat))) @@ -1209,8 +1274,27 @@ function ARTY:onEvent(Event) end end + -- Check if the authorization key is required and if it is valid. + local _validkey=true + if self.markkey~=nil then + _validkey=false + if _assign.key~=nil then + _validkey=self.markkey==_assign.key + end + self:T(ARTY.id..string.format("%s, authkey=%s == %s=playerkey ==> valid=%s", batteryname, tostring(self.markkey), tostring(_assign.key), tostring(_validkey))) + local text="" + if _assign.key==nil then + text=string.format("%s, authorization required but did not receive a key!", batteryname) + elseif _validkey==false then + text=string.format("%s, authorization required but did receive an incorrect key (key=%s)!", batteryname, tostring(_assign.key)) + elseif _validkey==true then + text=string.format("%s, authorization successful!", batteryname) + end + MESSAGE:New(text, 20):ToCoalitionIf(batterycoalition, self.report or self.Debug) + end + -- We are meant. - if _assigned then + if _assigned and _validkey then -- Convert (wrong x-->z, z-->x) vec3 local vec3={y=Event.pos.y, x=Event.pos.z, z=Event.pos.x} @@ -1219,6 +1303,9 @@ function ARTY:onEvent(Event) if _assign.move then + local text=string.format("%s, received new relocation assignment.", batteryname) + MESSAGE:New(text, 20):ToCoalitionIf(batterycoalition, self.report or self.Debug) + -- Create a new name. local _name=string.format("Marked Move ID=%d for battery %s", Event.idx, batteryname) self:E(ARTY.id.._name) @@ -1228,6 +1315,24 @@ function ARTY:onEvent(Event) else + local text=string.format("%s, received new target assignment.", batteryname) + if _assign.time then + text=text..string.format("\nTime %s",_assign.time) + end + if _assign.prio then + text=text..string.format("\nPrio %d",_assign.prio) + end + if _assign.nshells then + text=text..string.format("\nShots %d",_assign.nshells) + end + if _assign.maxengage then + text=text..string.format("\nEngagements %d",_assign.maxengage) + end + if _assign.weapontype then + text=text..string.format("\nWeapon %s",self:_WeaponTypeName(_assign.weapontype)) + end + MESSAGE:New(text, 20):ToCoalitionIf(batterycoalition, self.report or self.Debug) + -- Create a new name. local _name=string.format("Marked Target ID=%d for battery %s", Event.idx, batteryname) self:E(ARTY.id.._name) From f556077ff6b9c4d8655e47ad9001981dd91b92c9 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Fri, 1 Jun 2018 06:52:36 +0200 Subject: [PATCH 145/420] Cleanup --- Moose Development/Moose/AI/AI_A2A.lua | 8 +- Moose Development/Moose/AI/AI_A2A_Cap.lua | 14 +- Moose Development/Moose/AI/AI_A2A_Gci.lua | 2 +- Moose Development/Moose/AI/AI_A2A_Patrol.lua | 18 +- Moose Development/Moose/AI/AI_BAI.lua | 28 +- Moose Development/Moose/AI/AI_Balancer.lua | 4 +- Moose Development/Moose/AI/AI_CAP.lua | 10 +- Moose Development/Moose/AI/AI_CAS.lua | 28 +- Moose Development/Moose/AI/AI_Patrol.lua | 26 +- .../Moose/Actions/Act_Account.lua | 4 +- .../Moose/Actions/Act_Assign.lua | 3 +- .../Moose/Actions/Act_Assist.lua | 8 +- Moose Development/Moose/Actions/Act_JTAC.lua | 198 --- .../Moose/Actions/Act_Pickup.lua | 173 --- Moose Development/Moose/Actions/Act_Route.lua | 3 +- Moose Development/Moose/Core/Base.lua | 20 +- Moose Development/Moose/Core/Database.lua | 6 +- Moose Development/Moose/Core/Event.lua | 30 +- Moose Development/Moose/Core/Menu.lua | 4 +- Moose Development/Moose/Core/Point.lua | 114 +- Moose Development/Moose/Core/Set.lua | 2 +- Moose Development/Moose/Core/Spawn.lua | 14 +- Moose Development/Moose/Core/UserSound.lua | 4 +- Moose Development/Moose/Core/Velocity.lua | 2 +- Moose Development/Moose/Core/Zone.lua | 84 +- Moose Development/Moose/DCS.lua | 1072 +++++++++++++++++ Moose Development/Moose/Dcs/DCSAirbase.lua | 54 - .../Moose/Dcs/DCSCoalitionObject.lua | 20 - Moose Development/Moose/Dcs/DCSCommand.lua | 10 - Moose Development/Moose/Dcs/DCSController.lua | 115 -- Moose Development/Moose/Dcs/DCSGroup.lua | 83 -- Moose Development/Moose/Dcs/DCSObject.lua | 73 -- .../Moose/Dcs/DCSStaticObject.lua | 34 - Moose Development/Moose/Dcs/DCSTask.lua | 15 - Moose Development/Moose/Dcs/DCSTime.lua | 8 - Moose Development/Moose/Dcs/DCSTypes.lua | 246 ---- Moose Development/Moose/Dcs/DCSUnit.lua | 241 ---- Moose Development/Moose/Dcs/DCSVec3.lua | 11 - Moose Development/Moose/Dcs/DCSZone.lua | 10 - Moose Development/Moose/Dcs/DCScoalition.lua | 16 - Moose Development/Moose/Dcs/DCScountry.lua | 27 - Moose Development/Moose/Dcs/DCSenv.lua | 27 - Moose Development/Moose/Dcs/DCSland.lua | 26 - Moose Development/Moose/Dcs/DCStimer.lua | 45 - Moose Development/Moose/Dcs/DCStrigger.lua | 8 - Moose Development/Moose/Dcs/DCSworld.lua | 35 - .../Moose/Functional/ATC_Ground.lua | 2 +- .../Moose/Functional/CleanUp.lua | 6 +- .../Moose/Functional/Detection.lua | 18 +- Moose Development/Moose/Functional/Escort.lua | 22 +- .../Moose/Functional/Movement.lua | 1 + .../Moose/Functional/Protect.lua | 305 ----- Moose Development/Moose/Functional/RAT.lua | 16 +- .../Moose/Functional/ZoneGoal.lua | 1 + .../Moose/Functional/ZoneGoalCargo.lua | 1 + .../Moose/Functional/ZoneGoalCoalition.lua | 1 + .../Moose/Tasking/CommandCenter.lua | 5 +- Moose Development/Moose/Tasking/Task.lua | 1 + Moose Development/Moose/Tasking/TaskInfo.lua | 1 + .../Moose/Tasking/TaskZoneCapture.lua | 1 + Moose Development/Moose/Tasking/Task_A2A.lua | 1 + Moose Development/Moose/Tasking/Task_A2G.lua | 1 + .../Moose/Tasking/Task_CARGO.lua | 1 + .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 1 + .../Moose/Tasking/Task_Manager.lua | 1 + .../Moose/Utilities/Routines.lua | 2 +- Moose Development/Moose/Utilities/Utils.lua | 1 + Moose Development/Moose/Wrapper/Airbase.lua | 2 +- Moose Development/Moose/Wrapper/Client.lua | 8 +- .../Moose/Wrapper/Controllable.lua | 160 +-- Moose Development/Moose/Wrapper/Group.lua | 44 +- .../Moose/Wrapper/Identifiable.lua | 13 +- Moose Development/Moose/Wrapper/Object.lua | 5 +- .../Moose/Wrapper/Positionable.lua | 46 +- Moose Development/Moose/Wrapper/Static.lua | 2 +- Moose Development/Moose/Wrapper/Unit.lua | 16 +- 76 files changed, 1486 insertions(+), 2182 deletions(-) delete mode 100644 Moose Development/Moose/Actions/Act_JTAC.lua delete mode 100644 Moose Development/Moose/Actions/Act_Pickup.lua create mode 100644 Moose Development/Moose/DCS.lua delete mode 100644 Moose Development/Moose/Dcs/DCSAirbase.lua delete mode 100644 Moose Development/Moose/Dcs/DCSCoalitionObject.lua delete mode 100644 Moose Development/Moose/Dcs/DCSCommand.lua delete mode 100644 Moose Development/Moose/Dcs/DCSController.lua delete mode 100644 Moose Development/Moose/Dcs/DCSGroup.lua delete mode 100644 Moose Development/Moose/Dcs/DCSObject.lua delete mode 100644 Moose Development/Moose/Dcs/DCSStaticObject.lua delete mode 100644 Moose Development/Moose/Dcs/DCSTask.lua delete mode 100644 Moose Development/Moose/Dcs/DCSTime.lua delete mode 100644 Moose Development/Moose/Dcs/DCSTypes.lua delete mode 100644 Moose Development/Moose/Dcs/DCSUnit.lua delete mode 100644 Moose Development/Moose/Dcs/DCSVec3.lua delete mode 100644 Moose Development/Moose/Dcs/DCSZone.lua delete mode 100644 Moose Development/Moose/Dcs/DCScoalition.lua delete mode 100644 Moose Development/Moose/Dcs/DCScountry.lua delete mode 100644 Moose Development/Moose/Dcs/DCSenv.lua delete mode 100644 Moose Development/Moose/Dcs/DCSland.lua delete mode 100644 Moose Development/Moose/Dcs/DCStimer.lua delete mode 100644 Moose Development/Moose/Dcs/DCStrigger.lua delete mode 100644 Moose Development/Moose/Dcs/DCSworld.lua delete mode 100644 Moose Development/Moose/Functional/Protect.lua diff --git a/Moose Development/Moose/AI/AI_A2A.lua b/Moose Development/Moose/AI/AI_A2A.lua index faaeb3745..4fbe0f37d 100644 --- a/Moose Development/Moose/AI/AI_A2A.lua +++ b/Moose Development/Moose/AI/AI_A2A.lua @@ -292,8 +292,8 @@ end --- Sets (modifies) the minimum and maximum speed of the patrol. -- @param #AI_A2A self --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h. +-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. +-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h. -- @return #AI_A2A self function AI_A2A:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed ) self:F2( { PatrolMinSpeed, PatrolMaxSpeed } ) @@ -305,8 +305,8 @@ end --- Sets the floor and ceiling altitude of the patrol. -- @param #AI_A2A self --- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. -- @return #AI_A2A self function AI_A2A:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude ) self:F2( { PatrolFloorAltitude, PatrolCeilingAltitude } ) diff --git a/Moose Development/Moose/AI/AI_A2A_Cap.lua b/Moose Development/Moose/AI/AI_A2A_Cap.lua index 5917ed848..e6abde8d9 100644 --- a/Moose Development/Moose/AI/AI_A2A_Cap.lua +++ b/Moose Development/Moose/AI/AI_A2A_Cap.lua @@ -101,13 +101,13 @@ AI_A2A_CAP = { -- @param #AI_A2A_CAP self -- @param Wrapper.Group#GROUP AICap -- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. --- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h. --- @param Dcs.DCSTypes#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target. --- @param Dcs.DCSTypes#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target. --- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO +-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h. +-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h. +-- @param DCS#Speed EngageMinSpeed The minimum speed of the @{Wrapper.Group} in km/h when engaging a target. +-- @param DCS#Speed EngageMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h when engaging a target. +-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO -- @return #AI_A2A_CAP function AI_A2A_CAP:New( AICap, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, PatrolAltType ) diff --git a/Moose Development/Moose/AI/AI_A2A_Gci.lua b/Moose Development/Moose/AI/AI_A2A_Gci.lua index 5d2e2567f..e968c7320 100644 --- a/Moose Development/Moose/AI/AI_A2A_Gci.lua +++ b/Moose Development/Moose/AI/AI_A2A_Gci.lua @@ -9,7 +9,7 @@ -- === -- -- @module AI.AI_A2A_GCI --- @image AI_AI_Ground_Control_Intercept.JPG +-- @image AI_Ground_Control_Intercept.JPG diff --git a/Moose Development/Moose/AI/AI_A2A_Patrol.lua b/Moose Development/Moose/AI/AI_A2A_Patrol.lua index 73d814ef6..e9ea266db 100644 --- a/Moose Development/Moose/AI/AI_A2A_Patrol.lua +++ b/Moose Development/Moose/AI/AI_A2A_Patrol.lua @@ -125,11 +125,11 @@ AI_A2A_PATROL = { -- @param #AI_A2A_PATROL self -- @param Wrapper.Group#GROUP AIPatrol -- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. --- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h. --- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO +-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h. +-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h. +-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO -- @return #AI_A2A_PATROL self -- @usage -- -- Define a new AI_A2A_PATROL Object. This PatrolArea will patrol a Group within PatrolZone between 3000 and 6000 meters, with a variying speed between 600 and 900 km/h. @@ -235,8 +235,8 @@ end --- Sets (modifies) the minimum and maximum speed of the patrol. -- @param #AI_A2A_PATROL self --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h. +-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Group} in km/h. +-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Group} in km/h. -- @return #AI_A2A_PATROL self function AI_A2A_PATROL:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed ) self:F2( { PatrolMinSpeed, PatrolMaxSpeed } ) @@ -249,8 +249,8 @@ end --- Sets the floor and ceiling altitude of the patrol. -- @param #AI_A2A_PATROL self --- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. -- @return #AI_A2A_PATROL self function AI_A2A_PATROL:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude ) self:F2( { PatrolFloorAltitude, PatrolCeilingAltitude } ) diff --git a/Moose Development/Moose/AI/AI_BAI.lua b/Moose Development/Moose/AI/AI_BAI.lua index 84ef87f92..d68e5b006 100644 --- a/Moose Development/Moose/AI/AI_BAI.lua +++ b/Moose Development/Moose/AI/AI_BAI.lua @@ -135,12 +135,12 @@ AI_BAI_ZONE = { --- Creates a new AI_BAI_ZONE object -- @param #AI_BAI_ZONE self -- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. --- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h. +-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. +-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h. -- @param Core.Zone#ZONE_BASE EngageZone The zone where the engage will happen. --- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO +-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO -- @return #AI_BAI_ZONE self function AI_BAI_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageZone, PatrolAltType ) @@ -177,24 +177,24 @@ function AI_BAI_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude -- @function [parent=#AI_BAI_ZONE] Engage -- @param #AI_BAI_ZONE self -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. - -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. - -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. + -- @param DCS#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. + -- @param DCS#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. -- If parameter is not defined the unit / controllable will choose expend on its own discretion. -- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. - -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. + -- @param DCS#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- Asynchronous Event Trigger for Event Engage. -- @function [parent=#AI_BAI_ZONE] __Engage -- @param #AI_BAI_ZONE self -- @param #number Delay The delay in seconds. -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. - -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. - -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. + -- @param DCS#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. + -- @param DCS#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. -- If parameter is not defined the unit / controllable will choose expend on its own discretion. -- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. - -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. + -- @param DCS#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- OnLeave Transition Handler for State Engaging. -- @function [parent=#AI_BAI_ZONE] OnLeaveEngaging @@ -481,10 +481,10 @@ end -- @param #string Event The Event string. -- @param #string To The To State string. -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. --- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. +-- @param DCS#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. +-- @param DCS#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. +-- @param DCS#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To, EngageSpeed, EngageAltitude, diff --git a/Moose Development/Moose/AI/AI_Balancer.lua b/Moose Development/Moose/AI/AI_Balancer.lua index 2648b4760..9b704e8a1 100644 --- a/Moose Development/Moose/AI/AI_Balancer.lua +++ b/Moose Development/Moose/AI/AI_Balancer.lua @@ -140,7 +140,7 @@ end --- Returns the AI to the nearest friendly @{Wrapper.Airbase#AIRBASE}. -- @param #AI_BALANCER self --- @param Dcs.DCSTypes#Distance ReturnThresholdRange If there is an enemy @{Wrapper.Client#CLIENT} within the ReturnThresholdRange given in meters, the AI will not return to the nearest @{Wrapper.Airbase#AIRBASE}. +-- @param DCS#Distance ReturnThresholdRange If there is an enemy @{Wrapper.Client#CLIENT} within the ReturnThresholdRange given in meters, the AI will not return to the nearest @{Wrapper.Airbase#AIRBASE}. -- @param Core.Set#SET_AIRBASE ReturnAirbaseSet The SET of @{Core.Set#SET_AIRBASE}s to evaluate where to return to. function AI_BALANCER:ReturnToNearestAirbases( ReturnThresholdRange, ReturnAirbaseSet ) @@ -151,7 +151,7 @@ end --- Returns the AI to the home @{Wrapper.Airbase#AIRBASE}. -- @param #AI_BALANCER self --- @param Dcs.DCSTypes#Distance ReturnThresholdRange If there is an enemy @{Wrapper.Client#CLIENT} within the ReturnThresholdRange given in meters, the AI will not return to the nearest @{Wrapper.Airbase#AIRBASE}. +-- @param DCS#Distance ReturnThresholdRange If there is an enemy @{Wrapper.Client#CLIENT} within the ReturnThresholdRange given in meters, the AI will not return to the nearest @{Wrapper.Airbase#AIRBASE}. function AI_BALANCER:ReturnToHomeAirbase( ReturnThresholdRange ) self.ToHomeAirbase = true diff --git a/Moose Development/Moose/AI/AI_CAP.lua b/Moose Development/Moose/AI/AI_CAP.lua index af783bc37..19d51ca81 100644 --- a/Moose Development/Moose/AI/AI_CAP.lua +++ b/Moose Development/Moose/AI/AI_CAP.lua @@ -122,11 +122,11 @@ AI_CAP_ZONE = { --- Creates a new AI_CAP_ZONE object -- @param #AI_CAP_ZONE self -- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. --- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h. --- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO +-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. +-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h. +-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO -- @return #AI_CAP_ZONE self function AI_CAP_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) diff --git a/Moose Development/Moose/AI/AI_CAS.lua b/Moose Development/Moose/AI/AI_CAS.lua index a80e348ac..bb70b61df 100644 --- a/Moose Development/Moose/AI/AI_CAS.lua +++ b/Moose Development/Moose/AI/AI_CAS.lua @@ -126,12 +126,12 @@ AI_CAS_ZONE = { --- Creates a new AI_CAS_ZONE object -- @param #AI_CAS_ZONE self -- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. --- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h. +-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. +-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h. -- @param Core.Zone#ZONE_BASE EngageZone The zone where the engage will happen. --- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO +-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO -- @return #AI_CAS_ZONE self function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageZone, PatrolAltType ) @@ -167,24 +167,24 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude -- @function [parent=#AI_CAS_ZONE] Engage -- @param #AI_CAS_ZONE self -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. - -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. - -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. + -- @param DCS#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. + -- @param DCS#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. -- If parameter is not defined the unit / controllable will choose expend on its own discretion. -- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. - -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. + -- @param DCS#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- Asynchronous Event Trigger for Event Engage. -- @function [parent=#AI_CAS_ZONE] __Engage -- @param #AI_CAS_ZONE self -- @param #number Delay The delay in seconds. -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. - -- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. - -- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. + -- @param DCS#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. + -- @param DCS#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. -- If parameter is not defined the unit / controllable will choose expend on its own discretion. -- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. - -- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. + -- @param DCS#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- OnLeave Transition Handler for State Engaging. -- @function [parent=#AI_CAS_ZONE] OnLeaveEngaging @@ -426,10 +426,10 @@ end -- @param #string Event The Event string. -- @param #string To The To State string. -- @param #number EngageSpeed (optional) The speed the Group will hold when engaging to the target zone. --- @param Dcs.DCSTypes#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. +-- @param DCS#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. +-- @param DCS#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. +-- @param DCS#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To, EngageSpeed, EngageAltitude, diff --git a/Moose Development/Moose/AI/AI_Patrol.lua b/Moose Development/Moose/AI/AI_Patrol.lua index 74e06492b..1553be007 100644 --- a/Moose Development/Moose/AI/AI_Patrol.lua +++ b/Moose Development/Moose/AI/AI_Patrol.lua @@ -33,10 +33,10 @@ -- @type AI_PATROL_ZONE -- @field Wrapper.Controllable#CONTROLLABLE AIControllable The @{Wrapper.Controllable} patrolling. -- @field Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. --- @field Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @field Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @field Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. --- @field Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h. +-- @field DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @field DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @field DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. +-- @field DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h. -- @field Core.Spawn#SPAWN CoordTest -- @extends Core.Fsm#FSM_CONTROLLABLE @@ -151,11 +151,11 @@ AI_PATROL_ZONE = { --- Creates a new AI_PATROL_ZONE object -- @param #AI_PATROL_ZONE self -- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed. --- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h. --- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO +-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. +-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h. +-- @param DCS#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO -- @return #AI_PATROL_ZONE self -- @usage -- -- Define a new AI_PATROL_ZONE Object. This PatrolArea will patrol an AIControllable within PatrolZone between 3000 and 6000 meters, with a variying speed between 600 and 900 km/h. @@ -450,8 +450,8 @@ end --- Sets (modifies) the minimum and maximum speed of the patrol. -- @param #AI_PATROL_ZONE self --- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. --- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h. +-- @param DCS#Speed PatrolMinSpeed The minimum speed of the @{Wrapper.Controllable} in km/h. +-- @param DCS#Speed PatrolMaxSpeed The maximum speed of the @{Wrapper.Controllable} in km/h. -- @return #AI_PATROL_ZONE self function AI_PATROL_ZONE:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed ) self:F2( { PatrolMinSpeed, PatrolMaxSpeed } ) @@ -464,8 +464,8 @@ end --- Sets the floor and ceiling altitude of the patrol. -- @param #AI_PATROL_ZONE self --- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. --- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. +-- @param DCS#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol. +-- @param DCS#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol. -- @return #AI_PATROL_ZONE self function AI_PATROL_ZONE:SetAltitude( PatrolFloorAltitude, PatrolCeilingAltitude ) self:F2( { PatrolFloorAltitude, PatrolCeilingAltitude } ) diff --git a/Moose Development/Moose/Actions/Act_Account.lua b/Moose Development/Moose/Actions/Act_Account.lua index 98b84d819..0d057dc24 100644 --- a/Moose Development/Moose/Actions/Act_Account.lua +++ b/Moose Development/Moose/Actions/Act_Account.lua @@ -4,8 +4,8 @@ -- -- === -- --- @module Account - +-- @module Actions.Account +-- @image MOOSE.JPG do -- ACT_ACCOUNT diff --git a/Moose Development/Moose/Actions/Act_Assign.lua b/Moose Development/Moose/Actions/Act_Assign.lua index 0bb843ab1..5e8984b18 100644 --- a/Moose Development/Moose/Actions/Act_Assign.lua +++ b/Moose Development/Moose/Actions/Act_Assign.lua @@ -77,7 +77,8 @@ -- -- === -- --- @module Assign +-- @module Actions.Assign +-- @image MOOSE.JPG do -- ACT_ASSIGN diff --git a/Moose Development/Moose/Actions/Act_Assist.lua b/Moose Development/Moose/Actions/Act_Assist.lua index ccd56e310..b5a473fda 100644 --- a/Moose Development/Moose/Actions/Act_Assist.lua +++ b/Moose Development/Moose/Actions/Act_Assist.lua @@ -1,9 +1,5 @@ --- (SP) (MP) (FSM) Route AI or players through waypoints or to zones. -- --- === --- --- # @{#ACT_ASSIST} FSM class, extends @{Fsm#FSM_PROCESS} --- -- ## ACT_ASSIST state machine: -- -- This class is a state machine: it manages a process that is triggered by events causing state transitions to occur. @@ -64,7 +60,9 @@ -- -- === -- --- @module Smoke +-- @module Actions.Assist +-- @image MOOSE.JPG + do -- ACT_ASSIST diff --git a/Moose Development/Moose/Actions/Act_JTAC.lua b/Moose Development/Moose/Actions/Act_JTAC.lua deleted file mode 100644 index 86f965eb0..000000000 --- a/Moose Development/Moose/Actions/Act_JTAC.lua +++ /dev/null @@ -1,198 +0,0 @@ ---- @module Process_JTAC - ---- PROCESS_JTAC class --- @type PROCESS_JTAC --- @field Wrapper.Unit#UNIT ProcessUnit --- @field Core.Set#SET_UNIT TargetSetUnit --- @extends Core.Fsm#FSM_PROCESS -PROCESS_JTAC = { - ClassName = "PROCESS_JTAC", - Fsm = {}, - TargetSetUnit = nil, -} - - ---- Creates a new DESTROY process. --- @param #PROCESS_JTAC self --- @param Tasking.Task#TASK Task --- @param Wrapper.Unit#UNIT ProcessUnit --- @param Core.Set#SET_UNIT TargetSetUnit --- @param Wrapper.Unit#UNIT FACUnit --- @return #PROCESS_JTAC self -function PROCESS_JTAC:New( Task, ProcessUnit, TargetSetUnit, FACUnit ) - - -- Inherits from BASE - local self = BASE:Inherit( self, PROCESS:New( "JTAC", Task, ProcessUnit ) ) -- #PROCESS_JTAC - - self.TargetSetUnit = TargetSetUnit - self.FACUnit = FACUnit - - self.DisplayInterval = 60 - self.DisplayCount = 30 - self.DisplayMessage = true - self.DisplayTime = 10 -- 10 seconds is the default - self.DisplayCategory = "HQ" -- Targets is the default display category - - - self.Fsm = FSM_PROCESS:New( self, { - initial = 'Assigned', - events = { - { name = 'Start', from = 'Assigned', to = 'CreatedMenu' }, - { name = 'JTACMenuUpdate', from = 'CreatedMenu', to = 'AwaitingMenu' }, - { name = 'JTACMenuAwait', from = 'AwaitingMenu', to = 'AwaitingMenu' }, - { name = 'JTACMenuSpot', from = 'AwaitingMenu', to = 'AwaitingMenu' }, - { name = 'JTACMenuCancel', from = 'AwaitingMenu', to = 'AwaitingMenu' }, - { name = 'JTACStatus', from = 'AwaitingMenu', to = 'AwaitingMenu' }, - { name = 'Fail', from = 'AwaitingMenu', to = 'Failed' }, - { name = 'Fail', from = 'CreatedMenu', to = 'Failed' }, - }, - callbacks = { - onStart = self.OnStart, - onJTACMenuUpdate = self.OnJTACMenuUpdate, - onJTACMenuAwait = self.OnJTACMenuAwait, - onJTACMenuSpot = self.OnJTACMenuSpot, - onJTACMenuCancel = self.OnJTACMenuCancel, - }, - endstates = { 'Failed' } - } ) - - self:HandleEvent( EVENTS.Dead, self.EventDead ) - - return self -end - ---- Process Events - ---- StateMachine callback function for a PROCESS --- @param #PROCESS_JTAC self --- @param Core.Fsm#FSM_PROCESS Fsm --- @param #string Event --- @param #string From --- @param #string To -function PROCESS_JTAC:OnStart( Fsm, From, Event, To ) - - self:NextEvent( Fsm.JTACMenuUpdate ) -end - ---- StateMachine callback function for a PROCESS --- @param #PROCESS_JTAC self --- @param Core.Fsm#FSM_PROCESS Fsm --- @param #string Event --- @param #string From --- @param #string To -function PROCESS_JTAC:OnJTACMenuUpdate( Fsm, From, Event, To ) - - local function JTACMenuSpot( MenuParam ) - self:F( MenuParam.TargetUnit.UnitName ) - local self = MenuParam.self - local TargetUnit = MenuParam.TargetUnit - - self:NextEvent( self.Fsm.JTACMenuSpot, TargetUnit ) - end - - local function JTACMenuCancel( MenuParam ) - self:F( MenuParam ) - local self = MenuParam.self - local TargetUnit = MenuParam.TargetUnit - - self:NextEvent( self.Fsm.JTACMenuCancel, TargetUnit ) - end - - - -- Loop each unit in the target set, and determine the threat levels map table. - local UnitThreatLevels = self.TargetSetUnit:GetUnitThreatLevels() - - self:F( {"UnitThreadLevels", UnitThreatLevels } ) - - local JTACMenu = self.ProcessGroup:GetState( self.ProcessGroup, "JTACMenu" ) - - if not JTACMenu then - JTACMenu = MENU_GROUP:New( self.ProcessGroup, "JTAC", self.MissionMenu ) - for ThreatLevel, ThreatLevelTable in pairs( UnitThreatLevels ) do - local JTACMenuThreatLevel = MENU_GROUP:New( self.ProcessGroup, ThreatLevelTable.UnitThreatLevelText, JTACMenu ) - for ThreatUnitName, ThreatUnit in pairs( ThreatLevelTable.Units ) do - local JTACMenuUnit = MENU_GROUP:New( self.ProcessGroup, ThreatUnit:GetTypeName(), JTACMenuThreatLevel ) - MENU_GROUP_COMMAND:New( self.ProcessGroup, "Lase Target", JTACMenuUnit, JTACMenuSpot, { self = self, TargetUnit = ThreatUnit } ) - MENU_GROUP_COMMAND:New( self.ProcessGroup, "Cancel Target", JTACMenuUnit, JTACMenuCancel, { self = self, TargetUnit = ThreatUnit } ) - end - end - end - -end - ---- StateMachine callback function for a PROCESS --- @param #PROCESS_JTAC self --- @param Core.Fsm#FSM_PROCESS Fsm --- @param #string Event --- @param #string From --- @param #string To -function PROCESS_JTAC:OnJTACMenuAwait( Fsm, From, Event, To ) - - if self.DisplayCount >= self.DisplayInterval then - - local TaskJTAC = self.Task -- Tasking.Task#TASK_JTAC - TaskJTAC.Spots = TaskJTAC.Spots or {} - for TargetUnitName, SpotData in pairs( TaskJTAC.Spots) do - local TargetUnit = UNIT:FindByName( TargetUnitName ) - self.FACUnit:MessageToGroup( "Lasing " .. TargetUnit:GetTypeName() .. " with laser code " .. SpotData:getCode(), 15, self.ProcessGroup ) - end - self.DisplayCount = 1 - else - self.DisplayCount = self.DisplayCount + 1 - end - - self:NextEvent( Fsm.JTACMenuAwait ) -end - ---- StateMachine callback function for a PROCESS --- @param #PROCESS_JTAC self --- @param Core.Fsm#FSM_PROCESS Fsm --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT TargetUnit -function PROCESS_JTAC:OnJTACMenuSpot( Fsm, From, Event, To, TargetUnit ) - - local TargetUnitName = TargetUnit:GetName() - - local TaskJTAC = self.Task -- Tasking.Task#TASK_JTAC - - TaskJTAC.Spots = TaskJTAC.Spots or {} - TaskJTAC.Spots[TargetUnitName] = TaskJTAC.Spots[TargetUnitName] or {} - - local DCSFACObject = self.FACUnit:GetDCSObject() - local TargetVec3 = TargetUnit:GetVec3() - - TaskJTAC.Spots[TargetUnitName] = Spot.createInfraRed( self.FACUnit:GetDCSObject(), { x = 0, y = 1, z = 0 }, TargetUnit:GetVec3(), math.random( 1000, 9999 ) ) - - local SpotData = TaskJTAC.Spots[TargetUnitName] - self.FACUnit:MessageToGroup( "Lasing " .. TargetUnit:GetTypeName() .. " with laser code " .. SpotData:getCode(), 15, self.ProcessGroup ) - - self:NextEvent( Fsm.JTACMenuAwait ) -end - ---- StateMachine callback function for a PROCESS --- @param #PROCESS_JTAC self --- @param Core.Fsm#FSM_PROCESS Fsm --- @param #string Event --- @param #string From --- @param #string To --- @param Wrapper.Unit#UNIT TargetUnit -function PROCESS_JTAC:OnJTACMenuCancel( Fsm, From, Event, To, TargetUnit ) - - local TargetUnitName = TargetUnit:GetName() - - local TaskJTAC = self.Task -- Tasking.Task#TASK_JTAC - - TaskJTAC.Spots = TaskJTAC.Spots or {} - if TaskJTAC.Spots[TargetUnitName] then - TaskJTAC.Spots[TargetUnitName]:destroy() -- destroys the spot - TaskJTAC.Spots[TargetUnitName] = nil - end - - self.FACUnit:MessageToGroup( "Stopped lasing " .. TargetUnit:GetTypeName(), 15, self.ProcessGroup ) - - self:NextEvent( Fsm.JTACMenuAwait ) -end - - diff --git a/Moose Development/Moose/Actions/Act_Pickup.lua b/Moose Development/Moose/Actions/Act_Pickup.lua deleted file mode 100644 index 3ef76e0eb..000000000 --- a/Moose Development/Moose/Actions/Act_Pickup.lua +++ /dev/null @@ -1,173 +0,0 @@ ---- @module Process_Pickup - ---- PROCESS_PICKUP class --- @type PROCESS_PICKUP --- @field Wrapper.Unit#UNIT ProcessUnit --- @field Core.Set#SET_UNIT TargetSetUnit --- @extends Core.Fsm#FSM_PROCESS -PROCESS_PICKUP = { - ClassName = "PROCESS_PICKUP", - Fsm = {}, - TargetSetUnit = nil, -} - - ---- Creates a new DESTROY process. --- @param #PROCESS_PICKUP self --- @param Tasking.Task#TASK Task --- @param Wrapper.Unit#UNIT ProcessUnit --- @param Core.Set#SET_UNIT TargetSetUnit --- @return #PROCESS_PICKUP self -function PROCESS_PICKUP:New( Task, ProcessName, ProcessUnit ) - - -- Inherits from BASE - local self = BASE:Inherit( self, PROCESS:New( ProcessName, Task, ProcessUnit ) ) -- #PROCESS_PICKUP - - self.DisplayInterval = 30 - self.DisplayCount = 30 - self.DisplayMessage = true - self.DisplayTime = 10 -- 10 seconds is the default - self.DisplayCategory = "HQ" -- Targets is the default display category - - self.Fsm = FSM_PROCESS:New( self, { - initial = 'Assigned', - events = { - { name = 'Start', from = 'Assigned', to = 'Navigating' }, - { name = 'Start', from = 'Navigating', to = 'Navigating' }, - { name = 'Nearby', from = 'Navigating', to = 'Preparing' }, - { name = 'Pickup', from = 'Preparing', to = 'Loading' }, - { name = 'Load', from = 'Loading', to = 'Success' }, - { name = 'Fail', from = 'Assigned', to = 'Failed' }, - { name = 'Fail', from = 'Navigating', to = 'Failed' }, - { name = 'Fail', from = 'Preparing', to = 'Failed' }, - }, - callbacks = { - onStart = self.OnStart, - onNearby = self.OnNearby, - onPickup = self.OnPickup, - onLoad = self.OnLoad, - }, - endstates = { 'Success', 'Failed' } - } ) - - return self -end - ---- Process Events - ---- StateMachine callback function for a PROCESS --- @param #PROCESS_PICKUP self --- @param Core.Fsm#FSM_PROCESS Fsm --- @param #string Event --- @param #string From --- @param #string To -function PROCESS_PICKUP:OnStart( Fsm, From, Event, To ) - - self:NextEvent( Fsm.Start ) -end - ---- StateMachine callback function for a PROCESS --- @param #PROCESS_PICKUP self --- @param Core.Fsm#FSM_PROCESS Fsm --- @param #string Event --- @param #string From --- @param #string To -function PROCESS_PICKUP:OnNavigating( Fsm, From, Event, To ) - - local TaskGroup = self.ProcessUnit:GetGroup() - if self.DisplayCount >= self.DisplayInterval then - MESSAGE:New( "Your group with assigned " .. self.Task:GetName() .. " task has " .. self.TargetSetUnit:GetUnitTypesText() .. " targets left to be destroyed.", 5, "HQ" ):ToGroup( TaskGroup ) - self.DisplayCount = 1 - else - self.DisplayCount = self.DisplayCount + 1 - end - - return true -- Process always the event. - -end - - ---- StateMachine callback function for a PROCESS --- @param #PROCESS_PICKUP self --- @param Core.Fsm#FSM_PROCESS Fsm --- @param #string Event --- @param #string From --- @param #string To --- @param Core.Event#EVENTDATA Event -function PROCESS_PICKUP:OnHitTarget( Fsm, From, Event, To, Event ) - - - self.TargetSetUnit:Flush( self ) - - if self.TargetSetUnit:FindUnit( Event.IniUnitName ) then - self.TargetSetUnit:RemoveUnitsByName( Event.IniUnitName ) - local TaskGroup = self.ProcessUnit:GetGroup() - MESSAGE:New( "You hit a target. Your group with assigned " .. self.Task:GetName() .. " task has " .. self.TargetSetUnit:Count() .. " targets ( " .. self.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed.", 15, "HQ" ):ToGroup( TaskGroup ) - end - - - if self.TargetSetUnit:Count() > 0 then - self:NextEvent( Fsm.MoreTargets ) - else - self:NextEvent( Fsm.Destroyed ) - end -end - ---- StateMachine callback function for a PROCESS --- @param #PROCESS_PICKUP self --- @param Core.Fsm#FSM_PROCESS Fsm --- @param #string Event --- @param #string From --- @param #string To -function PROCESS_PICKUP:OnMoreTargets( Fsm, From, Event, To ) - - -end - ---- StateMachine callback function for a PROCESS --- @param #PROCESS_PICKUP self --- @param Core.Fsm#FSM_PROCESS Fsm --- @param #string Event --- @param #string From --- @param #string To --- @param Core.Event#EVENTDATA DCSEvent -function PROCESS_PICKUP:OnKilled( Fsm, From, Event, To ) - - self:NextEvent( Fsm.Restart ) - -end - ---- StateMachine callback function for a PROCESS --- @param #PROCESS_PICKUP self --- @param Core.Fsm#FSM_PROCESS Fsm --- @param #string Event --- @param #string From --- @param #string To -function PROCESS_PICKUP:OnRestart( Fsm, From, Event, To ) - - self:NextEvent( Fsm.Menu ) - -end - ---- StateMachine callback function for a PROCESS --- @param #PROCESS_PICKUP self --- @param Core.Fsm#FSM_PROCESS Fsm --- @param #string Event --- @param #string From --- @param #string To -function PROCESS_PICKUP:OnDestroyed( Fsm, From, Event, To ) - -end - ---- DCS Events - ---- @param #PROCESS_PICKUP self --- @param Core.Event#EVENTDATA Event -function PROCESS_PICKUP:EventDead( Event ) - - if Event.IniDCSUnit then - self:NextEvent( self.Fsm.HitTarget, Event ) - end -end - - diff --git a/Moose Development/Moose/Actions/Act_Route.lua b/Moose Development/Moose/Actions/Act_Route.lua index eba201588..87ee9a72e 100644 --- a/Moose Development/Moose/Actions/Act_Route.lua +++ b/Moose Development/Moose/Actions/Act_Route.lua @@ -72,7 +72,8 @@ -- -- === -- --- @module Route +-- @module Actions.Route +-- @image MOOSE.JPG do -- ACT_ROUTE diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index 12a8a5efe..996376961 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -604,8 +604,8 @@ end --- Creation of a Birth Event. -- @param #BASE self --- @param Dcs.DCSTypes#Time EventTime The time stamp of the event. --- @param Dcs.DCSWrapper.Object#Object Initiator The initiating object of the event. +-- @param DCS#Time EventTime The time stamp of the event. +-- @param DCS#Object Initiator The initiating object of the event. -- @param #string IniUnitName The initiating unit name. -- @param place -- @param subplace @@ -626,8 +626,8 @@ end --- Creation of a Crash Event. -- @param #BASE self --- @param Dcs.DCSTypes#Time EventTime The time stamp of the event. --- @param Dcs.DCSWrapper.Object#Object Initiator The initiating object of the event. +-- @param DCS#Time EventTime The time stamp of the event. +-- @param DCS#Object Initiator The initiating object of the event. function BASE:CreateEventCrash( EventTime, Initiator ) self:F( { EventTime, Initiator } ) @@ -642,8 +642,8 @@ end --- Creation of a Dead Event. -- @param #BASE self --- @param Dcs.DCSTypes#Time EventTime The time stamp of the event. --- @param Dcs.DCSWrapper.Object#Object Initiator The initiating object of the event. +-- @param DCS#Time EventTime The time stamp of the event. +-- @param DCS#Object Initiator The initiating object of the event. function BASE:CreateEventDead( EventTime, Initiator ) self:F( { EventTime, Initiator } ) @@ -658,8 +658,8 @@ end --- Creation of a Takeoff Event. -- @param #BASE self --- @param Dcs.DCSTypes#Time EventTime The time stamp of the event. --- @param Dcs.DCSWrapper.Object#Object Initiator The initiating object of the event. +-- @param DCS#Time EventTime The time stamp of the event. +-- @param DCS#Object Initiator The initiating object of the event. function BASE:CreateEventTakeoff( EventTime, Initiator ) self:F( { EventTime, Initiator } ) @@ -672,10 +672,10 @@ function BASE:CreateEventTakeoff( EventTime, Initiator ) world.onEvent( Event ) end --- TODO: Complete Dcs.DCSTypes#Event structure. +-- TODO: Complete DCS#Event structure. --- The main event handling function... This function captures all events generated for the class. -- @param #BASE self --- @param Dcs.DCSTypes#Event event +-- @param DCS#Event event function BASE:onEvent(event) --self:F( { BaseEventCodes[event.id], event } ) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index eea110521..37e792279 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -587,9 +587,9 @@ end --- Private method that registers new Group Templates within the DATABASE Object. -- @param #DATABASE self -- @param #table GroupTemplate --- @param Dcs.DCScoalition#coalition.side CoalitionSide The coalition.side of the object. --- @param Dcs.DCSObject#Object.Category CategoryID The Object.category of the object. --- @param Dcs.DCScountry#country.id CountryID the country.id of the object +-- @param DCS#coalition.side CoalitionSide The coalition.side of the object. +-- @param DCS#Object.Category CategoryID The Object.category of the object. +-- @param DCS#country.id CountryID the country.id of the object -- @return #DATABASE self function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, CategoryID, CountryID, GroupName ) diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index dd739a0d9..3f1b1cd57 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -226,34 +226,34 @@ EVENTS = { -- @type EVENTDATA -- @field #number id The identifier of the event. -- --- @field Dcs.DCSUnit#Unit initiator (UNIT/STATIC/SCENERY) The initiating @{Dcs.DCSUnit#Unit} or @{Dcs.DCSStaticObject#StaticObject}. --- @field Dcs.DCSObject#Object.Category IniObjectCategory (UNIT/STATIC/SCENERY) The initiator object category ( Object.Category.UNIT or Object.Category.STATIC ). --- @field Dcs.DCSUnit#Unit IniDCSUnit (UNIT/STATIC) The initiating @{DCSUnit#Unit} or @{DCSStaticObject#StaticObject}. +-- @field DCS#Unit initiator (UNIT/STATIC/SCENERY) The initiating @{DCS#Unit} or @{DCS#StaticObject}. +-- @field DCS#Object.Category IniObjectCategory (UNIT/STATIC/SCENERY) The initiator object category ( Object.Category.UNIT or Object.Category.STATIC ). +-- @field DCS#Unit IniDCSUnit (UNIT/STATIC) The initiating @{DCS#Unit} or @{DCSStaticObject#StaticObject}. -- @field #string IniDCSUnitName (UNIT/STATIC) The initiating Unit name. -- @field Wrapper.Unit#UNIT IniUnit (UNIT/STATIC) The initiating MOOSE wrapper @{Unit#UNIT} of the initiator Unit object. -- @field #string IniUnitName (UNIT/STATIC) The initiating UNIT name (same as IniDCSUnitName). --- @field Dcs.DCSGroup#Group IniDCSGroup (UNIT) The initiating {DCSGroup#Group}. +-- @field DCS#Group IniDCSGroup (UNIT) The initiating {DCSGroup#Group}. -- @field #string IniDCSGroupName (UNIT) The initiating Group name. -- @field Wrapper.Group#GROUP IniGroup (UNIT) The initiating MOOSE wrapper @{Wrapper.Group#GROUP} of the initiator Group object. -- @field #string IniGroupName UNIT) The initiating GROUP name (same as IniDCSGroupName). -- @field #string IniPlayerName (UNIT) The name of the initiating player in case the Unit is a client or player slot. --- @field Dcs.DCScoalition#coalition.side IniCoalition (UNIT) The coalition of the initiator. --- @field Dcs.DCSUnit#Unit.Category IniCategory (UNIT) The category of the initiator. +-- @field DCS#coalition.side IniCoalition (UNIT) The coalition of the initiator. +-- @field DCS#Unit.Category IniCategory (UNIT) The category of the initiator. -- @field #string IniTypeName (UNIT) The type name of the initiator. -- --- @field Dcs.DCSUnit#Unit target (UNIT/STATIC) The target @{Dcs.DCSUnit#Unit} or @{DCSStaticObject#StaticObject}. --- @field Dcs.DCSObject#Object.Category TgtObjectCategory (UNIT/STATIC) The target object category ( Object.Category.UNIT or Object.Category.STATIC ). --- @field Dcs.DCSUnit#Unit TgtDCSUnit (UNIT/STATIC) The target @{DCSUnit#Unit} or @{DCSStaticObject#StaticObject}. +-- @field DCS#Unit target (UNIT/STATIC) The target @{DCS#Unit} or @{DCSStaticObject#StaticObject}. +-- @field DCS#Object.Category TgtObjectCategory (UNIT/STATIC) The target object category ( Object.Category.UNIT or Object.Category.STATIC ). +-- @field DCS#Unit TgtDCSUnit (UNIT/STATIC) The target @{DCS#Unit} or @{DCSStaticObject#StaticObject}. -- @field #string TgtDCSUnitName (UNIT/STATIC) The target Unit name. -- @field Wrapper.Unit#UNIT TgtUnit (UNIT/STATIC) The target MOOSE wrapper @{Unit#UNIT} of the target Unit object. -- @field #string TgtUnitName (UNIT/STATIC) The target UNIT name (same as TgtDCSUnitName). --- @field Dcs.DCSGroup#Group TgtDCSGroup (UNIT) The target {DCSGroup#Group}. +-- @field DCS#Group TgtDCSGroup (UNIT) The target {DCSGroup#Group}. -- @field #string TgtDCSGroupName (UNIT) The target Group name. -- @field Wrapper.Group#GROUP TgtGroup (UNIT) The target MOOSE wrapper @{Wrapper.Group#GROUP} of the target Group object. -- @field #string TgtGroupName (UNIT) The target GROUP name (same as TgtDCSGroupName). -- @field #string TgtPlayerName (UNIT) The name of the target player in case the Unit is a client or player slot. --- @field Dcs.DCScoalition#coalition.side TgtCoalition (UNIT) The coalition of the target. --- @field Dcs.DCSUnit#Unit.Category TgtCategory (UNIT) The category of the target. +-- @field DCS#coalition.side TgtCoalition (UNIT) The coalition of the target. +-- @field DCS#Unit.Category TgtCategory (UNIT) The category of the target. -- @field #string TgtTypeName (UNIT) The type name of the target. -- -- @field weapon The weapon used during the event. @@ -457,7 +457,7 @@ end --- Initializes the Events structure for the event -- @param #EVENT self --- @param Dcs.DCSWorld#world.event EventID +-- @param DCS#world.event EventID -- @param Core.Base#BASE EventClass -- @return #EVENT.Events function EVENT:Init( EventID, EventClass ) @@ -483,7 +483,7 @@ end --- Removes a subscription -- @param #EVENT self -- @param Core.Base#BASE EventClass The self instance of the class for which the event is. --- @param Dcs.DCSWorld#world.event EventID +-- @param DCS#world.event EventID -- @return #EVENT.Events function EVENT:RemoveEvent( EventClass, EventID ) @@ -503,7 +503,7 @@ end --- Resets subscriptions -- @param #EVENT self -- @param Core.Base#BASE EventClass The self instance of the class for which the event is. --- @param Dcs.DCSWorld#world.event EventID +-- @param DCS#world.event EventID -- @return #EVENT.Events function EVENT:Reset( EventObject ) --R2.1 diff --git a/Moose Development/Moose/Core/Menu.lua b/Moose Development/Moose/Core/Menu.lua index b93cb59c9..03d95c47f 100644 --- a/Moose Development/Moose/Core/Menu.lua +++ b/Moose Development/Moose/Core/Menu.lua @@ -585,7 +585,7 @@ do -- MENU_COALITION --- MENU_COALITION constructor. Creates a new MENU_COALITION object and creates the menu for a complete coalition. -- @param #MENU_COALITION self - -- @param Dcs.DCSCoalition#coalition.side Coalition The coalition owning the menu. + -- @param DCS#coalition.side Coalition The coalition owning the menu. -- @param #string MenuText The text for the menu. -- @param #table ParentMenu The parent menu. This parameter can be ignored if you want the menu to be located at the perent menu of DCS world (under F10 other). -- @return #MENU_COALITION self @@ -683,7 +683,7 @@ do -- MENU_COALITION_COMMAND --- MENU_COALITION constructor. Creates a new radio command item for a coalition, which can invoke a function with parameters. -- @param #MENU_COALITION_COMMAND self - -- @param Dcs.DCSCoalition#coalition.side Coalition The coalition owning the menu. + -- @param DCS#coalition.side Coalition The coalition owning the menu. -- @param #string MenuText The text for the menu. -- @param Core.Menu#MENU_COALITION ParentMenu The parent menu. -- @param CommandMenuFunction A function that is called when the menu key is pressed. diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 0a90f6440..be99b5d75 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -174,9 +174,9 @@ do -- COORDINATE --- COORDINATE constructor. -- @param #COORDINATE self - -- @param Dcs.DCSTypes#Distance x The x coordinate of the Vec3 point, pointing to the North. - -- @param Dcs.DCSTypes#Distance y The y coordinate of the Vec3 point, pointing to the Right. - -- @param Dcs.DCSTypes#Distance z The z coordinate of the Vec3 point, pointing to the Right. + -- @param DCS#Distance x The x coordinate of the Vec3 point, pointing to the North. + -- @param DCS#Distance y The y coordinate of the Vec3 point, pointing to the Right. + -- @param DCS#Distance z The z coordinate of the Vec3 point, pointing to the Right. -- @return #COORDINATE function COORDINATE:New( x, y, z ) @@ -204,8 +204,8 @@ do -- COORDINATE --- Create a new COORDINATE object from Vec2 coordinates. -- @param #COORDINATE self - -- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 point. - -- @param Dcs.DCSTypes#Distance LandHeightAdd (optional) The default height if required to be evaluated will be the land height of the x, y coordinate. You can specify an extra height to be added to the land height. + -- @param DCS#Vec2 Vec2 The Vec2 point. + -- @param DCS#Distance LandHeightAdd (optional) The default height if required to be evaluated will be the land height of the x, y coordinate. You can specify an extra height to be added to the land height. -- @return #COORDINATE function COORDINATE:NewFromVec2( Vec2, LandHeightAdd ) @@ -224,7 +224,7 @@ do -- COORDINATE --- Create a new COORDINATE object from Vec3 coordinates. -- @param #COORDINATE self - -- @param Dcs.DCSTypes#Vec3 Vec3 The Vec3 point. + -- @param DCS#Vec3 Vec3 The Vec3 point. -- @return #COORDINATE function COORDINATE:NewFromVec3( Vec3 ) @@ -238,7 +238,7 @@ do -- COORDINATE --- Return the coordinates of the COORDINATE in Vec3 format. -- @param #COORDINATE self - -- @return Dcs.DCSTypes#Vec3 The Vec3 format coordinate. + -- @return DCS#Vec3 The Vec3 format coordinate. function COORDINATE:GetVec3() return { x = self.x, y = self.y, z = self.z } end @@ -246,7 +246,7 @@ do -- COORDINATE --- Return the coordinates of the COORDINATE in Vec2 format. -- @param #COORDINATE self - -- @return Dcs.DCSTypes#Vec2 The Vec2 format coordinate. + -- @return DCS#Vec2 The Vec2 format coordinate. function COORDINATE:GetVec2() return { x = self.x, y = self.z } end @@ -273,7 +273,7 @@ do -- COORDINATE --- Calculate the distance from a reference @{#COORDINATE}. -- @param #COORDINATE self -- @param #COORDINATE PointVec2Reference The reference @{#COORDINATE}. - -- @return Dcs.DCSTypes#Distance The distance from the reference @{#COORDINATE} in meters. + -- @return DCS#Distance The distance from the reference @{#COORDINATE} in meters. function COORDINATE:DistanceFromPointVec2( PointVec2Reference ) self:F2( PointVec2Reference ) @@ -285,8 +285,8 @@ do -- COORDINATE --- Add a Distance in meters from the COORDINATE orthonormal plane, with the given angle, and calculate the new COORDINATE. -- @param #COORDINATE self - -- @param Dcs.DCSTypes#Distance Distance The Distance to be added in meters. - -- @param Dcs.DCSTypes#Angle Angle The Angle in degrees. + -- @param DCS#Distance Distance The Distance to be added in meters. + -- @param DCS#Angle Angle The Angle in degrees. -- @return #COORDINATE The new calculated COORDINATE. function COORDINATE:Translate( Distance, Angle ) local SX = self.x @@ -300,9 +300,9 @@ do -- COORDINATE --- Return a random Vec2 within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE. -- @param #COORDINATE self - -- @param Dcs.DCSTypes#Distance OuterRadius - -- @param Dcs.DCSTypes#Distance InnerRadius - -- @return Dcs.DCSTypes#Vec2 Vec2 + -- @param DCS#Distance OuterRadius + -- @param DCS#Distance InnerRadius + -- @return DCS#Vec2 Vec2 function COORDINATE:GetRandomVec2InRadius( OuterRadius, InnerRadius ) self:F2( { OuterRadius, InnerRadius } ) @@ -332,8 +332,8 @@ do -- COORDINATE --- Return a random Coordinate within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE. -- @param #COORDINATE self - -- @param Dcs.DCSTypes#Distance OuterRadius - -- @param Dcs.DCSTypes#Distance InnerRadius + -- @param DCS#Distance OuterRadius + -- @param DCS#Distance InnerRadius -- @return #COORDINATE function COORDINATE:GetRandomCoordinateInRadius( OuterRadius, InnerRadius ) self:F2( { OuterRadius, InnerRadius } ) @@ -344,9 +344,9 @@ do -- COORDINATE --- Return a random Vec3 within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE. -- @param #COORDINATE self - -- @param Dcs.DCSTypes#Distance OuterRadius - -- @param Dcs.DCSTypes#Distance InnerRadius - -- @return Dcs.DCSTypes#Vec3 Vec3 + -- @param DCS#Distance OuterRadius + -- @param DCS#Distance InnerRadius + -- @return DCS#Vec3 Vec3 function COORDINATE:GetRandomVec3InRadius( OuterRadius, InnerRadius ) local RandomVec2 = self:GetRandomVec2InRadius( OuterRadius, InnerRadius ) @@ -409,7 +409,7 @@ do -- COORDINATE --- Return a direction vector Vec3 from COORDINATE to the COORDINATE. -- @param #COORDINATE self -- @param #COORDINATE TargetCoordinate The target COORDINATE. - -- @return Dcs.DCSTypes#Vec3 DirectionVec3 The direction vector in Vec3 format. + -- @return DCS#Vec3 DirectionVec3 The direction vector in Vec3 format. function COORDINATE:GetDirectionVec3( TargetCoordinate ) return { x = TargetCoordinate.x - self.x, y = TargetCoordinate.y - self.y, z = TargetCoordinate.z - self.z } end @@ -428,7 +428,7 @@ do -- COORDINATE --- Return an angle in radians from the COORDINATE using a direction vector in Vec3 format. -- @param #COORDINATE self - -- @param Dcs.DCSTypes#Vec3 DirectionVec3 The direction vector in Vec3 format. + -- @param DCS#Vec3 DirectionVec3 The direction vector in Vec3 format. -- @return #number DirectionRadians The angle in radians. function COORDINATE:GetAngleRadians( DirectionVec3 ) local DirectionRadians = math.atan2( DirectionVec3.z, DirectionVec3.x ) @@ -441,7 +441,7 @@ do -- COORDINATE --- Return an angle in degrees from the COORDINATE using a direction vector in Vec3 format. -- @param #COORDINATE self - -- @param Dcs.DCSTypes#Vec3 DirectionVec3 The direction vector in Vec3 format. + -- @param DCS#Vec3 DirectionVec3 The direction vector in Vec3 format. -- @return #number DirectionRadians The angle in degrees. function COORDINATE:GetAngleDegrees( DirectionVec3 ) local AngleRadians = self:GetAngleRadians( DirectionVec3 ) @@ -453,7 +453,7 @@ do -- COORDINATE --- Return the 2D distance in meters between the target COORDINATE and the COORDINATE. -- @param #COORDINATE self -- @param #COORDINATE TargetCoordinate The target COORDINATE. - -- @return Dcs.DCSTypes#Distance Distance The distance in meters. + -- @return DCS#Distance Distance The distance in meters. function COORDINATE:Get2DDistance( TargetCoordinate ) local TargetVec3 = TargetCoordinate:GetVec3() local SourceVec3 = self:GetVec3() @@ -617,7 +617,7 @@ do -- COORDINATE --- Return the 3D distance in meters between the target COORDINATE and the COORDINATE. -- @param #COORDINATE self -- @param #COORDINATE TargetCoordinate The target COORDINATE. - -- @return Dcs.DCSTypes#Distance Distance The distance in meters. + -- @return DCS#Distance Distance The distance in meters. function COORDINATE:Get3DDistance( TargetCoordinate ) local TargetVec3 = TargetCoordinate:GetVec3() local SourceVec3 = self:GetVec3() @@ -753,8 +753,8 @@ do -- COORDINATE --- Add a Distance in meters from the COORDINATE horizontal plane, with the given angle, and calculate the new COORDINATE. -- @param #COORDINATE self - -- @param Dcs.DCSTypes#Distance Distance The Distance to be added in meters. - -- @param Dcs.DCSTypes#Angle Angle The Angle in degrees. + -- @param DCS#Distance Distance The Distance to be added in meters. + -- @param DCS#Angle Angle The Angle in degrees. -- @return #COORDINATE The new calculated COORDINATE. function COORDINATE:Translate( Distance, Angle ) local SX = self.x @@ -773,7 +773,7 @@ do -- COORDINATE -- @param #COORDINATE.WaypointAltType AltType The altitude type. -- @param #COORDINATE.WaypointType Type The route point type. -- @param #COORDINATE.WaypointAction Action The route point action. - -- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h. Default is 500 km/h. + -- @param DCS#Speed Speed Airspeed in km/h. Default is 500 km/h. -- @param #boolean SpeedLocked true means the speed is locked. -- @return #table The route point. function COORDINATE:WaypointAir( AltType, Type, Action, Speed, SpeedLocked ) @@ -816,7 +816,7 @@ do -- COORDINATE --- Build a Waypoint Air "Turning Point". -- @param #COORDINATE self -- @param #COORDINATE.WaypointAltType AltType The altitude type. - -- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h. + -- @param DCS#Speed Speed Airspeed in km/h. -- @return #table The route point. function COORDINATE:WaypointAirTurningPoint( AltType, Speed ) return self:WaypointAir( AltType, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, Speed ) @@ -826,7 +826,7 @@ do -- COORDINATE --- Build a Waypoint Air "Fly Over Point". -- @param #COORDINATE self -- @param #COORDINATE.WaypointAltType AltType The altitude type. - -- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h. + -- @param DCS#Speed Speed Airspeed in km/h. -- @return #table The route point. function COORDINATE:WaypointAirFlyOverPoint( AltType, Speed ) return self:WaypointAir( AltType, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.FlyoverPoint, Speed ) @@ -836,7 +836,7 @@ do -- COORDINATE --- Build a Waypoint Air "Take Off Parking Hot". -- @param #COORDINATE self -- @param #COORDINATE.WaypointAltType AltType The altitude type. - -- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h. + -- @param DCS#Speed Speed Airspeed in km/h. -- @return #table The route point. function COORDINATE:WaypointAirTakeOffParkingHot( AltType, Speed ) return self:WaypointAir( AltType, COORDINATE.WaypointType.TakeOffParkingHot, COORDINATE.WaypointAction.FromParkingAreaHot, Speed ) @@ -846,7 +846,7 @@ do -- COORDINATE --- Build a Waypoint Air "Take Off Parking". -- @param #COORDINATE self -- @param #COORDINATE.WaypointAltType AltType The altitude type. - -- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h. + -- @param DCS#Speed Speed Airspeed in km/h. -- @return #table The route point. function COORDINATE:WaypointAirTakeOffParking( AltType, Speed ) return self:WaypointAir( AltType, COORDINATE.WaypointType.TakeOffParking, COORDINATE.WaypointAction.FromParkingArea, Speed ) @@ -856,7 +856,7 @@ do -- COORDINATE --- Build a Waypoint Air "Take Off Runway". -- @param #COORDINATE self -- @param #COORDINATE.WaypointAltType AltType The altitude type. - -- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h. + -- @param DCS#Speed Speed Airspeed in km/h. -- @return #table The route point. function COORDINATE:WaypointAirTakeOffRunway( AltType, Speed ) return self:WaypointAir( AltType, COORDINATE.WaypointType.TakeOff, COORDINATE.WaypointAction.FromRunway, Speed ) @@ -865,7 +865,7 @@ do -- COORDINATE --- Build a Waypoint Air "Landing". -- @param #COORDINATE self - -- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h. + -- @param DCS#Speed Speed Airspeed in km/h. -- @return #table The route point. -- @usage -- @@ -960,7 +960,7 @@ do -- COORDINATE --- Gets the surface type at the coordinate. -- @param #COORDINATE self - -- @return Dcs.DCSland#SurfaceType Surface type. + -- @return DCS#SurfaceType Surface type. function COORDINATE:GetSurfaceType() local vec2=self:GetVec2() local surface=land.getSurfaceType(vec2) @@ -1111,7 +1111,7 @@ do -- COORDINATE --- Flares the point in a color. -- @param #COORDINATE self -- @param Utilities.Utils#FLARECOLOR FlareColor - -- @param Dcs.DCSTypes#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. + -- @param DCS#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. function COORDINATE:Flare( FlareColor, Azimuth ) self:F2( { FlareColor } ) trigger.action.signalFlare( self:GetVec3(), FlareColor, Azimuth and Azimuth or 0 ) @@ -1119,7 +1119,7 @@ do -- COORDINATE --- Flare the COORDINATE White. -- @param #COORDINATE self - -- @param Dcs.DCSTypes#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. + -- @param DCS#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. function COORDINATE:FlareWhite( Azimuth ) self:F2( Azimuth ) self:Flare( FLARECOLOR.White, Azimuth ) @@ -1127,7 +1127,7 @@ do -- COORDINATE --- Flare the COORDINATE Yellow. -- @param #COORDINATE self - -- @param Dcs.DCSTypes#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. + -- @param DCS#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. function COORDINATE:FlareYellow( Azimuth ) self:F2( Azimuth ) self:Flare( FLARECOLOR.Yellow, Azimuth ) @@ -1135,7 +1135,7 @@ do -- COORDINATE --- Flare the COORDINATE Green. -- @param #COORDINATE self - -- @param Dcs.DCSTypes#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. + -- @param DCS#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. function COORDINATE:FlareGreen( Azimuth ) self:F2( Azimuth ) self:Flare( FLARECOLOR.Green, Azimuth ) @@ -1307,7 +1307,7 @@ do -- COORDINATE --- Return a BULLS string out of the BULLS of the coalition to the COORDINATE. -- @param #COORDINATE self - -- @param Dcs.DCSCoalition#coalition.side Coalition The coalition. + -- @param DCS#coalition.side Coalition The coalition. -- @return #string The BR text. function COORDINATE:ToStringBULLS( Coalition, Settings ) local BullsCoordinate = COORDINATE:NewFromVec3( coalition.getMainRefPoint( Coalition ) ) @@ -1674,9 +1674,9 @@ do -- POINT_VEC3 --- Create a new POINT_VEC3 object. -- @param #POINT_VEC3 self - -- @param Dcs.DCSTypes#Distance x The x coordinate of the Vec3 point, pointing to the North. - -- @param Dcs.DCSTypes#Distance y The y coordinate of the Vec3 point, pointing Upwards. - -- @param Dcs.DCSTypes#Distance z The z coordinate of the Vec3 point, pointing to the Right. + -- @param DCS#Distance x The x coordinate of the Vec3 point, pointing to the North. + -- @param DCS#Distance y The y coordinate of the Vec3 point, pointing Upwards. + -- @param DCS#Distance z The z coordinate of the Vec3 point, pointing to the Right. -- @return Core.Point#POINT_VEC3 function POINT_VEC3:New( x, y, z ) @@ -1688,8 +1688,8 @@ do -- POINT_VEC3 --- Create a new POINT_VEC3 object from Vec2 coordinates. -- @param #POINT_VEC3 self - -- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 point. - -- @param Dcs.DCSTypes#Distance LandHeightAdd (optional) Add a landheight. + -- @param DCS#Vec2 Vec2 The Vec2 point. + -- @param DCS#Distance LandHeightAdd (optional) Add a landheight. -- @return Core.Point#POINT_VEC3 self function POINT_VEC3:NewFromVec2( Vec2, LandHeightAdd ) @@ -1702,7 +1702,7 @@ do -- POINT_VEC3 --- Create a new POINT_VEC3 object from Vec3 coordinates. -- @param #POINT_VEC3 self - -- @param Dcs.DCSTypes#Vec3 Vec3 The Vec3 point. + -- @param DCS#Vec3 Vec3 The Vec3 point. -- @return Core.Point#POINT_VEC3 self function POINT_VEC3:NewFromVec3( Vec3 ) @@ -1791,8 +1791,8 @@ do -- POINT_VEC3 --- Return a random POINT_VEC3 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC3. -- @param #POINT_VEC3 self - -- @param Dcs.DCSTypes#Distance OuterRadius - -- @param Dcs.DCSTypes#Distance InnerRadius + -- @param DCS#Distance OuterRadius + -- @param DCS#Distance InnerRadius -- @return #POINT_VEC3 function POINT_VEC3:GetRandomPointVec3InRadius( OuterRadius, InnerRadius ) @@ -1804,8 +1804,8 @@ end do -- POINT_VEC2 --- @type POINT_VEC2 - -- @field Dcs.DCSTypes#Distance x The x coordinate in meters. - -- @field Dcs.DCSTypes#Distance y the y coordinate in meters. + -- @field DCS#Distance x The x coordinate in meters. + -- @field DCS#Distance y the y coordinate in meters. -- @extends Core.Point#COORDINATE --- The @{Point#POINT_VEC2} class defines a 2D point in the simulator. The height coordinate (if needed) will be the land height + an optional added height specified. @@ -1840,9 +1840,9 @@ do -- POINT_VEC2 --- POINT_VEC2 constructor. -- @param #POINT_VEC2 self - -- @param Dcs.DCSTypes#Distance x The x coordinate of the Vec3 point, pointing to the North. - -- @param Dcs.DCSTypes#Distance y The y coordinate of the Vec3 point, pointing to the Right. - -- @param Dcs.DCSTypes#Distance LandHeightAdd (optional) The default height if required to be evaluated will be the land height of the x, y coordinate. You can specify an extra height to be added to the land height. + -- @param DCS#Distance x The x coordinate of the Vec3 point, pointing to the North. + -- @param DCS#Distance y The y coordinate of the Vec3 point, pointing to the Right. + -- @param DCS#Distance LandHeightAdd (optional) The default height if required to be evaluated will be the land height of the x, y coordinate. You can specify an extra height to be added to the land height. -- @return Core.Point#POINT_VEC2 function POINT_VEC2:New( x, y, LandHeightAdd ) @@ -1859,7 +1859,7 @@ do -- POINT_VEC2 --- Create a new POINT_VEC2 object from Vec2 coordinates. -- @param #POINT_VEC2 self - -- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 point. + -- @param DCS#Vec2 Vec2 The Vec2 point. -- @return Core.Point#POINT_VEC2 self function POINT_VEC2:NewFromVec2( Vec2, LandHeightAdd ) @@ -1876,7 +1876,7 @@ do -- POINT_VEC2 --- Create a new POINT_VEC2 object from Vec3 coordinates. -- @param #POINT_VEC2 self - -- @param Dcs.DCSTypes#Vec3 Vec3 The Vec3 point. + -- @param DCS#Vec3 Vec3 The Vec3 point. -- @return Core.Point#POINT_VEC2 self function POINT_VEC2:NewFromVec3( Vec3 ) @@ -1996,8 +1996,8 @@ do -- POINT_VEC2 --- Return a random POINT_VEC2 within an Outer Radius and optionally NOT within an Inner Radius of the POINT_VEC2. -- @param #POINT_VEC2 self - -- @param Dcs.DCSTypes#Distance OuterRadius - -- @param Dcs.DCSTypes#Distance InnerRadius + -- @param DCS#Distance OuterRadius + -- @param DCS#Distance InnerRadius -- @return #POINT_VEC2 function POINT_VEC2:GetRandomPointVec2InRadius( OuterRadius, InnerRadius ) self:F2( { OuterRadius, InnerRadius } ) @@ -2009,7 +2009,7 @@ do -- POINT_VEC2 --- Calculate the distance from a reference @{#POINT_VEC2}. -- @param #POINT_VEC2 self -- @param #POINT_VEC2 PointVec2Reference The reference @{#POINT_VEC2}. - -- @return Dcs.DCSTypes#Distance The distance from the reference @{#POINT_VEC2} in meters. + -- @return DCS#Distance The distance from the reference @{#POINT_VEC2} in meters. function POINT_VEC2:DistanceFromPointVec2( PointVec2Reference ) self:F2( PointVec2Reference ) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index fbaed636e..2209dcad1 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -2186,7 +2186,7 @@ do -- SET_UNIT --- Returns if the @{Set} has targets having a radar (of a given type). -- @param #SET_UNIT self - -- @param Dcs.DCSWrapper.Unit#Unit.RadarType RadarType + -- @param DCS#Unit.RadarType RadarType -- @return #number The amount of radars in the Set with the given type function SET_UNIT:HasRadar( RadarType ) self:F2( RadarType ) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 919623334..94c3b5564 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -547,8 +547,8 @@ end --- Randomizes the position of @{Wrapper.Group}s that are spawned within a **radius band**, given an Outer and Inner radius, from the point that the spawn happens. -- @param #SPAWN self -- @param #boolean RandomizePosition If true, SPAWN will perform the randomization of the @{Wrapper.Group}s position between a given outer and inner radius. --- @param Dcs.DCSTypes#Distance OuterRadius (optional) The outer radius in meters where the new group will be spawned. --- @param Dcs.DCSTypes#Distance InnerRadius (optional) The inner radius in meters where the new group will NOT be spawned. +-- @param DCS#Distance OuterRadius (optional) The outer radius in meters where the new group will be spawned. +-- @param DCS#Distance InnerRadius (optional) The inner radius in meters where the new group will NOT be spawned. -- @return #SPAWN function SPAWN:InitRandomizePosition( RandomizePosition, OuterRadius, InnerRadius ) self:F( { self.SpawnTemplatePrefix, RandomizePosition, OuterRadius, InnerRadius } ) @@ -568,8 +568,8 @@ end --- Randomizes the UNITs that are spawned within a radius band given an Outer and Inner radius. -- @param #SPAWN self -- @param #boolean RandomizeUnits If true, SPAWN will perform the randomization of the @{UNIT}s position within the group between a given outer and inner radius. --- @param Dcs.DCSTypes#Distance OuterRadius (optional) The outer radius in meters where the new group will be spawned. --- @param Dcs.DCSTypes#Distance InnerRadius (optional) The inner radius in meters where the new group will NOT be spawned. +-- @param DCS#Distance OuterRadius (optional) The outer radius in meters where the new group will be spawned. +-- @param DCS#Distance InnerRadius (optional) The inner radius in meters where the new group will NOT be spawned. -- @return #SPAWN -- @usage -- -- NATO helicopters engaging in the battle field. @@ -1344,7 +1344,7 @@ end -- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. -- You can use the returned group to further define the route to be followed. -- @param #SPAWN self --- @param Dcs.DCSTypes#Vec3 Vec3 The Vec3 coordinates where to spawn the group. +-- @param DCS#Vec3 Vec3 The Vec3 coordinates where to spawn the group. -- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. -- @return Wrapper.Group#GROUP that was spawned. -- @return #nil Nothing was spawned. @@ -1454,7 +1454,7 @@ end -- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. -- You can use the returned group to further define the route to be followed. -- @param #SPAWN self --- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 coordinates where to spawn the group. +-- @param DCS#Vec2 Vec2 The Vec2 coordinates where to spawn the group. -- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone. -- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone. -- @param #number SpawnIndex (optional) The index which group to spawn within the given zone. @@ -1778,7 +1778,7 @@ end -- The method will search for a #-mark, and will return the text before the #-mark. -- It will return nil of no prefix was found. -- @param #SPAWN self --- @param Dcs.DCSWrapper.Unit#UNIT DCSUnit The @{DCSUnit} to be searched. +-- @param DCS#UNIT DCSUnit The @{DCSUnit} to be searched. -- @return #string The prefix -- @return #nil Nothing found function SPAWN:_GetPrefixFromGroup( SpawnGroup ) diff --git a/Moose Development/Moose/Core/UserSound.lua b/Moose Development/Moose/Core/UserSound.lua index 9b7204b2f..a8a6328d7 100644 --- a/Moose Development/Moose/Core/UserSound.lua +++ b/Moose Development/Moose/Core/UserSound.lua @@ -79,7 +79,7 @@ do -- UserSound --- Play the usersound to the given coalition. -- @param #USERSOUND self - -- @param Dcs.DCScoalition#coalition Coalition The coalition to play the usersound to. + -- @param DCS#coalition Coalition The coalition to play the usersound to. -- @return #USERSOUND The usersound instance. -- @usage -- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" ) @@ -95,7 +95,7 @@ do -- UserSound --- Play the usersound to the given country. -- @param #USERSOUND self - -- @param Dcs.DCScountry#country Country The country to play the usersound to. + -- @param DCS#country Country The country to play the usersound to. -- @return #USERSOUND The usersound instance. -- @usage -- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" ) diff --git a/Moose Development/Moose/Core/Velocity.lua b/Moose Development/Moose/Core/Velocity.lua index c8ef325f3..04e4675bb 100644 --- a/Moose Development/Moose/Core/Velocity.lua +++ b/Moose Development/Moose/Core/Velocity.lua @@ -8,7 +8,7 @@ -- === -- -- @module Core.Velocity --- @image Core_Velocity.JPG +-- @image MOOSE.JPG do -- Velocity diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 56e6a6765..3697c6de2 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -92,10 +92,10 @@ ZONE_BASE = { --- The ZONE_BASE.BoundingSquare -- @type ZONE_BASE.BoundingSquare --- @field Dcs.DCSTypes#Distance x1 The lower x coordinate (left down) --- @field Dcs.DCSTypes#Distance y1 The lower y coordinate (left down) --- @field Dcs.DCSTypes#Distance x2 The higher x coordinate (right up) --- @field Dcs.DCSTypes#Distance y2 The higher y coordinate (right up) +-- @field DCS#Distance x1 The lower x coordinate (left down) +-- @field DCS#Distance y1 The lower y coordinate (left down) +-- @field DCS#Distance x2 The higher x coordinate (right up) +-- @field DCS#Distance y2 The higher y coordinate (right up) --- ZONE_BASE constructor @@ -135,7 +135,7 @@ end --- Returns if a Vec2 is within the zone. -- @param #ZONE_BASE self --- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 to test. +-- @param DCS#Vec2 Vec2 The Vec2 to test. -- @return #boolean true if the Vec2 is within the zone. function ZONE_BASE:IsVec2InZone( Vec2 ) self:F2( Vec2 ) @@ -145,7 +145,7 @@ end --- Returns if a Vec3 is within the zone. -- @param #ZONE_BASE self --- @param Dcs.DCSTypes#Vec3 Vec3 The point to test. +-- @param DCS#Vec3 Vec3 The point to test. -- @return #boolean true if the Vec3 is within the zone. function ZONE_BASE:IsVec3InZone( Vec3 ) local InZone = self:IsVec2InZone( { x = Vec3.x, y = Vec3.z } ) @@ -189,7 +189,7 @@ end --- Returns a @{Point#POINT_VEC2} of the zone. -- @param #ZONE_BASE self --- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. +-- @param DCS#Distance Height The height to add to the land height where the center of the zone is located. -- @return Core.Point#POINT_VEC2 The PointVec2 of the zone. function ZONE_BASE:GetPointVec2() self:F2( self.ZoneName ) @@ -222,8 +222,8 @@ end --- Returns the @{DCSTypes#Vec3} of the zone. -- @param #ZONE_BASE self --- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. --- @return Dcs.DCSTypes#Vec3 The Vec3 of the zone. +-- @param DCS#Distance Height The height to add to the land height where the center of the zone is located. +-- @return DCS#Vec3 The Vec3 of the zone. function ZONE_BASE:GetVec3( Height ) self:F2( self.ZoneName ) @@ -240,7 +240,7 @@ end --- Returns a @{Point#POINT_VEC3} of the zone. -- @param #ZONE_BASE self --- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. +-- @param DCS#Distance Height The height to add to the land height where the center of the zone is located. -- @return Core.Point#POINT_VEC3 The PointVec3 of the zone. function ZONE_BASE:GetPointVec3( Height ) self:F2( self.ZoneName ) @@ -256,7 +256,7 @@ end --- Returns a @{Point#COORDINATE} of the zone. -- @param #ZONE_BASE self --- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. +-- @param DCS#Distance Height The height to add to the land height where the center of the zone is located. -- @return Core.Point#COORDINATE The Coordinate of the zone. function ZONE_BASE:GetCoordinate( Height ) --R2.1 self:F2( self.ZoneName ) @@ -273,7 +273,7 @@ end --- Define a random @{DCSTypes#Vec2} within the zone. -- @param #ZONE_BASE self --- @return Dcs.DCSTypes#Vec2 The Vec2 coordinates. +-- @return DCS#Vec2 The Vec2 coordinates. function ZONE_BASE:GetRandomVec2() return nil end @@ -373,8 +373,8 @@ end --- The ZONE_RADIUS class, defined by a zone name, a location and a radius. -- @type ZONE_RADIUS --- @field Dcs.DCSTypes#Vec2 Vec2 The current location of the zone. --- @field Dcs.DCSTypes#Distance Radius The radius of the zone. +-- @field DCS#Vec2 Vec2 The current location of the zone. +-- @field DCS#Distance Radius The radius of the zone. -- @extends #ZONE_BASE --- The ZONE_RADIUS class defined by a zone name, a location and a radius. @@ -411,8 +411,8 @@ ZONE_RADIUS = { --- Constructor of @{#ZONE_RADIUS}, taking the zone name, the zone location and a radius. -- @param #ZONE_RADIUS self -- @param #string ZoneName Name of the zone. --- @param Dcs.DCSTypes#Vec2 Vec2 The location of the zone. --- @param Dcs.DCSTypes#Distance Radius The radius of the zone. +-- @param DCS#Vec2 Vec2 The location of the zone. +-- @param DCS#Distance Radius The radius of the zone. -- @return #ZONE_RADIUS self function ZONE_RADIUS:New( ZoneName, Vec2, Radius ) local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) ) -- #ZONE_RADIUS @@ -427,7 +427,7 @@ end --- Bounds the zone with tires. -- @param #ZONE_RADIUS self -- @param #number Points (optional) The amount of points in the circle. Default 360. --- @param Dcs.DCScountry#country.id CountryID The country id of the tire objects, e.g. country.id.USA for blue or country.id.RUSSIA for red. +-- @param DCS#country.id CountryID The country id of the tire objects, e.g. country.id.USA for blue or country.id.RUSSIA for red. -- @param #boolean UnBound (Optional) If true the tyres will be destroyed. -- @return #ZONE_RADIUS self function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound ) @@ -507,7 +507,7 @@ end -- @param #ZONE_RADIUS self -- @param Utilities.Utils#FLARECOLOR FlareColor The flare color. -- @param #number Points (optional) The amount of points in the circle. --- @param Dcs.DCSTypes#Azimuth Azimuth (optional) Azimuth The azimuth of the flare. +-- @param DCS#Azimuth Azimuth (optional) Azimuth The azimuth of the flare. -- @param #number AddHeight (optional) The height to be added for the smoke. -- @return #ZONE_RADIUS self function ZONE_RADIUS:FlareZone( FlareColor, Points, Azimuth, AddHeight ) @@ -535,7 +535,7 @@ end --- Returns the radius of the zone. -- @param #ZONE_RADIUS self --- @return Dcs.DCSTypes#Distance The radius of the zone. +-- @return DCS#Distance The radius of the zone. function ZONE_RADIUS:GetRadius() self:F2( self.ZoneName ) @@ -546,8 +546,8 @@ end --- Sets the radius of the zone. -- @param #ZONE_RADIUS self --- @param Dcs.DCSTypes#Distance Radius The radius of the zone. --- @return Dcs.DCSTypes#Distance The radius of the zone. +-- @param DCS#Distance Radius The radius of the zone. +-- @return DCS#Distance The radius of the zone. function ZONE_RADIUS:SetRadius( Radius ) self:F2( self.ZoneName ) @@ -559,7 +559,7 @@ end --- Returns the @{DCSTypes#Vec2} of the zone. -- @param #ZONE_RADIUS self --- @return Dcs.DCSTypes#Vec2 The location of the zone. +-- @return DCS#Vec2 The location of the zone. function ZONE_RADIUS:GetVec2() self:F2( self.ZoneName ) @@ -570,8 +570,8 @@ end --- Sets the @{DCSTypes#Vec2} of the zone. -- @param #ZONE_RADIUS self --- @param Dcs.DCSTypes#Vec2 Vec2 The new location of the zone. --- @return Dcs.DCSTypes#Vec2 The new location of the zone. +-- @param DCS#Vec2 Vec2 The new location of the zone. +-- @return DCS#Vec2 The new location of the zone. function ZONE_RADIUS:SetVec2( Vec2 ) self:F2( self.ZoneName ) @@ -584,8 +584,8 @@ end --- Returns the @{DCSTypes#Vec3} of the ZONE_RADIUS. -- @param #ZONE_RADIUS self --- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. --- @return Dcs.DCSTypes#Vec3 The point of the zone. +-- @param DCS#Distance Height The height to add to the land height where the center of the zone is located. +-- @return DCS#Vec3 The point of the zone. function ZONE_RADIUS:GetVec3( Height ) self:F2( { self.ZoneName, Height } ) @@ -829,7 +829,7 @@ end --- Returns if a location is within the zone. -- @param #ZONE_RADIUS self --- @param Dcs.DCSTypes#Vec2 Vec2 The location to test. +-- @param DCS#Vec2 Vec2 The location to test. -- @return #boolean true if the location is within the zone. function ZONE_RADIUS:IsVec2InZone( Vec2 ) self:F2( Vec2 ) @@ -847,7 +847,7 @@ end --- Returns if a point is within the zone. -- @param #ZONE_RADIUS self --- @param Dcs.DCSTypes#Vec3 Vec3 The point to test. +-- @param DCS#Vec3 Vec3 The point to test. -- @return #boolean true if the point is within the zone. function ZONE_RADIUS:IsVec3InZone( Vec3 ) self:F2( Vec3 ) @@ -861,7 +861,7 @@ end -- @param #ZONE_RADIUS self -- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. --- @return Dcs.DCSTypes#Vec2 The random location within the zone. +-- @return DCS#Vec2 The random location within the zone. function ZONE_RADIUS:GetRandomVec2( inner, outer ) self:F( self.ZoneName, inner, outer ) @@ -898,7 +898,7 @@ end -- @param #ZONE_RADIUS self -- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. --- @return Dcs.DCSTypes#Vec3 The random location within the zone. +-- @return DCS#Vec3 The random location within the zone. function ZONE_RADIUS:GetRandomVec3( inner, outer ) self:F( self.ZoneName, inner, outer ) @@ -1029,7 +1029,7 @@ ZONE_UNIT = { -- @param #ZONE_UNIT self -- @param #string ZoneName Name of the zone. -- @param Wrapper.Unit#UNIT ZoneUNIT The unit as the center of the zone. --- @param Dcs.DCSTypes#Distance Radius The radius of the zone. +-- @param DCS#Distance Radius The radius of the zone. -- @return #ZONE_UNIT self function ZONE_UNIT:New( ZoneName, ZoneUNIT, Radius ) local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, ZoneUNIT:GetVec2(), Radius ) ) @@ -1047,7 +1047,7 @@ end --- Returns the current location of the @{Unit#UNIT}. -- @param #ZONE_UNIT self --- @return Dcs.DCSTypes#Vec2 The location of the zone based on the @{Unit#UNIT}location. +-- @return DCS#Vec2 The location of the zone based on the @{Unit#UNIT}location. function ZONE_UNIT:GetVec2() self:F2( self.ZoneName ) @@ -1066,7 +1066,7 @@ end --- Returns a random location within the zone. -- @param #ZONE_UNIT self --- @return Dcs.DCSTypes#Vec2 The random location within the zone. +-- @return DCS#Vec2 The random location within the zone. function ZONE_UNIT:GetRandomVec2() self:F( self.ZoneName ) @@ -1088,8 +1088,8 @@ end --- Returns the @{DCSTypes#Vec3} of the ZONE_UNIT. -- @param #ZONE_UNIT self --- @param Dcs.DCSTypes#Distance Height The height to add to the land height where the center of the zone is located. --- @return Dcs.DCSTypes#Vec3 The point of the zone. +-- @param DCS#Distance Height The height to add to the land height where the center of the zone is located. +-- @return DCS#Vec3 The point of the zone. function ZONE_UNIT:GetVec3( Height ) self:F2( self.ZoneName ) @@ -1120,7 +1120,7 @@ ZONE_GROUP = { -- @param #ZONE_GROUP self -- @param #string ZoneName Name of the zone. -- @param Wrapper.Group#GROUP ZoneGROUP The @{Wrapper.Group} as the center of the zone. --- @param Dcs.DCSTypes#Distance Radius The radius of the zone. +-- @param DCS#Distance Radius The radius of the zone. -- @return #ZONE_GROUP self function ZONE_GROUP:New( ZoneName, ZoneGROUP, Radius ) local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, ZoneGROUP:GetVec2(), Radius ) ) @@ -1137,7 +1137,7 @@ end --- Returns the current location of the @{Wrapper.Group}. -- @param #ZONE_GROUP self --- @return Dcs.DCSTypes#Vec2 The location of the zone based on the @{Wrapper.Group} location. +-- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Group} location. function ZONE_GROUP:GetVec2() self:F( self.ZoneName ) @@ -1150,7 +1150,7 @@ end --- Returns a random location within the zone of the @{Wrapper.Group}. -- @param #ZONE_GROUP self --- @return Dcs.DCSTypes#Vec2 The random location of the zone based on the @{Wrapper.Group} location. +-- @return DCS#Vec2 The random location of the zone based on the @{Wrapper.Group} location. function ZONE_GROUP:GetRandomVec2() self:F( self.ZoneName ) @@ -1206,7 +1206,7 @@ ZONE_POLYGON_BASE = { --- A points array. -- @type ZONE_POLYGON_BASE.ListVec2 --- @list +-- @list --- Constructor to create a ZONE_POLYGON_BASE instance, taking the zone name and an array of @{DCSTypes#Vec2}, forming a polygon. -- The @{Wrapper.Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected. @@ -1233,7 +1233,7 @@ end --- Returns the center location of the polygon. -- @param #ZONE_GROUP self --- @return Dcs.DCSTypes#Vec2 The location of the zone based on the @{Wrapper.Group} location. +-- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Group} location. function ZONE_POLYGON_BASE:GetVec2() self:F( self.ZoneName ) @@ -1340,7 +1340,7 @@ end --- Returns if a location is within the zone. -- Source learned and taken from: https://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html -- @param #ZONE_POLYGON_BASE self --- @param Dcs.DCSTypes#Vec2 Vec2 The location to test. +-- @param DCS#Vec2 Vec2 The location to test. -- @return #boolean true if the location is within the zone. function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 ) self:F2( Vec2 ) @@ -1370,7 +1370,7 @@ end --- Define a random @{DCSTypes#Vec2} within the zone. -- @param #ZONE_POLYGON_BASE self --- @return Dcs.DCSTypes#Vec2 The Vec2 coordinate. +-- @return DCS#Vec2 The Vec2 coordinate. function ZONE_POLYGON_BASE:GetRandomVec2() self:F2() diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua new file mode 100644 index 000000000..19bb8e47c --- /dev/null +++ b/Moose Development/Moose/DCS.lua @@ -0,0 +1,1072 @@ +--- DCS API prototypes +-- @module DCS +-- @image MOOSE.JPG + +do -- world + + --- @type world + -- @field #world.event event + + --- @type world.event + -- @field S_EVENT_INVALID + -- @field S_EVENT_SHOT + -- @field S_EVENT_HIT + -- @field S_EVENT_TAKEOFF + -- @field S_EVENT_LAND + -- @field S_EVENT_CRASH + -- @field S_EVENT_EJECTION + -- @field S_EVENT_REFUELING + -- @field S_EVENT_DEAD + -- @field S_EVENT_PILOT_DEAD + -- @field S_EVENT_BASE_CAPTURED + -- @field S_EVENT_MISSION_START + -- @field S_EVENT_MISSION_END + -- @field S_EVENT_TOOK_CONTROL + -- @field S_EVENT_REFUELING_STOP + -- @field S_EVENT_BIRTH + -- @field S_EVENT_HUMAN_FAILURE + -- @field S_EVENT_ENGINE_STARTUP + -- @field S_EVENT_ENGINE_SHUTDOWN + -- @field S_EVENT_PLAYER_ENTER_UNIT + -- @field S_EVENT_PLAYER_LEAVE_UNIT + -- @field S_EVENT_PLAYER_COMMENT + -- @field S_EVENT_SHOOTING_START + -- @field S_EVENT_SHOOTING_END + -- @field S_EVENT_MAX + + world = {} --#world + +end -- world + + +do -- env + + --- @type env + + --- Add message to simulator log with caption "INFO". Message box is optional. + -- @function [parent=#env] info + -- @field #string message message string to add to log. + -- @field #boolean showMessageBox If the parameter is true Message Box will appear. Optional. + + --- Add message to simulator log with caption "WARNING". Message box is optional. + -- @function [parent=#env] warning + -- @field #string message message string to add to log. + -- @field #boolean showMessageBox If the parameter is true Message Box will appear. Optional. + + --- Add message to simulator log with caption "ERROR". Message box is optional. + -- @function [parent=#env] error + -- @field #string message message string to add to log. + -- @field #boolean showMessageBox If the parameter is true Message Box will appear. Optional. + + --- Enables/disables appearance of message box each time lua error occurs. + -- @function [parent=#env] setErrorMessageBoxEnabled + -- @field #boolean on if true message box appearance is enabled. + + env = {} --#env + +end -- env + + +do -- timer + + --- @type timer + + + --- Returns model time in seconds. + -- @function [parent=#timer] getTime + -- @return #Time + + --- Returns mission time in seconds. + -- @function [parent=#timer] getAbsTime + -- @return #Time + + --- Returns mission start time in seconds. + -- @function [parent=#timer] getTime0 + -- @return #Time + + --- Schedules function to call at desired model time. + -- Time function FunctionToCall(any argument, Time time) + -- + -- ... + -- + -- return ... + -- + -- end + -- + -- Must return model time of next call or nil. Note that the DCS scheduler calls the function in protected mode and any Lua errors in the called function will be trapped and not reported. If the function triggers a Lua error then it will be terminated and not scheduled to run again. + -- @function [parent=#timer] scheduleFunction + -- @param #FunctionToCall functionToCall Lua-function to call. Must have prototype of FunctionToCall. + -- @param functionArgument Function argument of any type to pass to functionToCall. + -- @param #Time time Model time of the function call. + -- @return functionId + + --- Re-schedules function to call at another model time. + -- @function [parent=#timer] setFunctionTime + -- @param functionId Lua-function to call. Must have prototype of FunctionToCall. + -- @param #Time time Model time of the function call. + + + --- Removes the function from schedule. + -- @function [parent=#timer] removeFunction + -- @param functionId Function identifier to remove from schedule + + timer = {} --#timer + +end + + +do -- land + + --- @type land + -- @field #land.SurfaceType SurfaceType + + + --- @type land.SurfaceType + -- @field LAND + -- @field SHALLOW_WATER + -- @field WATER + -- @field ROAD + -- @field RUNWAY + + --- Returns altitude MSL of the point. + -- @function [parent=#land] getHeight + -- @param #Vec2 point point on the ground. + -- @return #Distance + + --- returns surface type at the given point. + -- @function [parent=#land] getSurfaceType + -- @param #Vec2 point Point on the land. + -- @return #land.SurfaceType + + land = {} --#land + +end -- land + +do -- country + + --- @type country + -- @field #country.id id + + --- @type country.id + -- @field RUSSIA + -- @field UKRAINE + -- @field USA + -- @field TURKEY + -- @field UK + -- @field FRANCE + -- @field GERMANY + -- @field CANADA + -- @field SPAIN + -- @field THE_NETHERLANDS + -- @field BELGIUM + -- @field NORWAY + -- @field DENMARK + -- @field ISRAEL + -- @field GEORGIA + -- @field INSURGENTS + -- @field ABKHAZIA + -- @field SOUTH_OSETIA + -- @field ITALY + + country = {} -- #country + +end -- country + +do -- Command + + --- @type Command + -- @field #string id + -- @field #Command.params params + + --- @type Command.params + +end -- Command + +do -- coalition + + --- @type coalition + -- @field #coalition.side side + + --- @type coalition.side + -- @field NEUTRAL + -- @field RED + -- @field BLUE + + --- @function [parent=#coalition] getCountryCoalition + -- @param #number countryId + -- @return #number coalitionId + + coalition = {} -- #coalition + +end -- coalition + + +do -- Types + + --- @type Desc + -- @field #TypeName typeName type name + -- @field #string displayName localized display name + -- @field #table attributes object type attributes + + --- A distance type + -- @type Distance + + --- An angle type + -- @type Angle + + --- Time is given in seconds. + -- @type Time + -- @extends #number + + --- Model time is the time that drives the simulation. Model time may be stopped, accelerated and decelerated relative real time. + -- @type ModelTime + -- @extends #number + + --- Mission time is a model time plus time of the mission start. + -- @type MissionTime + -- @extends #number + + + --- Distance is given in meters. + -- @type Distance + -- @extends #number + + --- Angle is given in radians. + -- @type Angle + -- @extends #number + + --- Azimuth is an angle of rotation around world axis y counter-clockwise. + -- @type Azimuth + -- @extends #number + + --- Mass is given in kilograms. + -- @type Mass + -- @extends #number + + --- Vec3 type is a 3D-vector. + -- DCS world has 3-dimensional coordinate system. DCS ground is an infinite plain. + -- @type Vec3 + -- @field #Distance x is directed to the north + -- @field #Distance z is directed to the east + -- @field #Distance y is directed up + + --- Vec2 is a 2D-vector for the ground plane as a reference plane. + -- @type Vec2 + -- @field #Distance x Vec2.x = Vec3.x + -- @field #Distance y Vec2.y = Vec3.z + + --- Position is a composite structure. It consists of both coordinate vector and orientation matrix. Position3 (also known as "Pos3" for short) is a table that has following format: + -- @type Position3 + -- @field #Vec3 p + -- @field #Vec3 x + -- @field #Vec3 y + -- @field #Vec3 z + + --- 3-dimensional box. + -- @type Box3 + -- @field #Vec3 min + -- @field #Vec3 max + + --- Each object belongs to a type. Object type is a named couple of properties those independent of mission and common for all units of the same type. Name of unit type is a string. Samples of unit type: "Su-27", "KAMAZ" and "M2 Bradley". + -- @type TypeName + -- @extends #string + + --- AttributeName = string + -- Each object type may have attributes. + -- Attributes are enlisted in ./Scripts/Database/db_attributes.Lua. + -- To know what attributes the object type has, look for the unit type script in sub-directories planes/, helicopter/s, vehicles, navy/ of ./Scripts/Database/ directory. + -- @type AttributeName + -- @extends #string + + --- List of @{#AttributeName} + -- @type AttributeNameArray + -- @list <#AttributeName> + + --- @type Zone + -- @field DCSVec3#Vec3 point + -- @field #number radius + + Zone = {} + + --- @type ModelTime + -- @extends #number + + --- @type Time + -- @extends #number + + --- A task descriptor (internal structure for DCS World) + -- @type Task + -- @field #string id + -- @field #Task.param param + + --- @type Task.param + + --- List of @{#Task} + -- @type TaskArray + -- @list <#Task> + + +end -- + +do -- Object + + --- @type Object + -- @field #Object.Category Category + -- @field #Object.Desc Desc + + --- @type Object.Category + -- @field UNIT + -- @field WEAPON + -- @field STATIC + -- @field SCENERY + -- @field BASE + + --- @type Object.Desc + -- @extends #Desc + -- @field #number life initial life level + -- @field #Box3 box bounding box of collision geometry + + --- @function [parent=#Object] isExist + -- @param #Object self + -- @return #boolean + + --- @function [parent=#Object] destroy + -- @param #Object self + + --- @function [parent=#Object] getCategory + -- @param #Object self + -- @return #Object.Category + + --- Returns type name of the Object. + -- @function [parent=#Object] getTypeName + -- @param #Object self + -- @return #string + + --- Returns object descriptor. + -- @function [parent=#Object] getDesc + -- @param #Object self + -- @return #Object.Desc + + --- Returns true if the object belongs to the category. + -- @function [parent=#Object] hasAttribute + -- @param #Object self + -- @param #AttributeName attributeName Attribute name to check. + -- @return #boolean + + --- Returns name of the object. This is the name that is assigned to the object in the Mission Editor. + -- @function [parent=#Object] getName + -- @param #Object self + -- @return #string + + --- Returns object coordinates for current time. + -- @function [parent=#Object] getPoint + -- @param #Object self + -- @return #Vec3 + + --- Returns object position for current time. + -- @function [parent=#Object] getPosition + -- @param #Object self + -- @return #Position3 + + --- Returns the unit's velocity vector. + -- @function [parent=#Object] getVelocity + -- @param #Object self + -- @return #Vec3 + + --- Returns true if the unit is in air. + -- @function [parent=#Object] inAir + -- @param #Object self + -- @return #boolean + + Object = {} --#Object + +end -- Object + +do -- CoalitionObject + + --- @type CoalitionObject + -- @extends #Object + + + --- Returns coalition of the object. + -- @function [parent=#CoalitionObject] getCoalition + -- @param #CoalitionObject self + -- @return #coalition.side + + --- Returns object country. + -- @function [parent=#CoalitionObject] getCountry + -- @param #CoalitionObject self + -- @return #country.id + +CoalitionObject = {} --#CoalitionObject + +end -- CoalitionObject + + +do -- Airbase + + --- Represents airbases: airdromes, helipads and ships with flying decks or landing pads. + -- @type Airbase + -- @extends #CoalitionObject + -- @field #Airbase.ID ID Identifier of an airbase. It assigned to an airbase by the Mission Editor automatically. This identifier is used in AI tasks to refer an airbase that exists (spawned and not dead) or not. + -- @field #Airbase.Category Category enum contains identifiers of airbase categories. + -- @field #Airbase.Desc Desc Airbase descriptor. Airdromes are unique and their types are unique, but helipads and ships are not always unique and may have the same type. + + --- Enum contains identifiers of airbase categories. + -- @type Airbase.Category + -- @field AIRDROME + -- @field HELIPAD + -- @field SHIP + + --- Airbase descriptor. Airdromes are unique and their types are unique, but helipads and ships are not always unique and may have the same type. + -- @type Airbase.Desc + -- @extends #Desc + -- @field #Airbase.Category category Category of the airbase type. + + --- Returns airbase by its name. If no airbase found the function will return nil. + -- @function [parent=#Airbase] getByName + -- @param #string name + -- @return #Airbase + + --- Returns airbase descriptor by type name. If no descriptor is found the function will return nil. + -- @function [parent=#Airbase] getDescByName + -- @param #TypeName typeName Airbase type name. + -- @return #Airbase.Desc + + --- Returns Unit that is corresponded to the airbase. Works only for ships. + -- @function [parent=#Airbase] getUnit + -- @param self + -- @return #Unit + + --- Returns identifier of the airbase. + -- @function [parent=#Airbase] getID + -- @param self + -- @return #Airbase.ID + + --- Returns the airbase's callsign - the localized string. + -- @function [parent=#Airbase] getCallsign + -- @param self + -- @return #string + + --- Returns descriptor of the airbase. + -- @function [parent=#Airbase] getDesc + -- @param self + -- @return #Airbase.Desc + + Airbase = {} --#Airbase + +end -- Airbase + + + +do -- Controller + --- Controller is an object that performs A.I.-routines. Other words controller is an instance of A.I.. Controller stores current main task, active enroute tasks and behavior options. Controller performs commands. Please, read DCS A-10C GUI Manual EN.pdf chapter "Task Planning for Unit Groups", page 91 to understand A.I. system of DCS:A-10C. + -- + -- This class has 2 types of functions: + -- + -- * Tasks + -- * Commands: Commands are instant actions those required zero time to perform. Commands may be used both for control unit/group behavior and control game mechanics. + -- @type Controller + -- @field #Controller.Detection Detection Enum contains identifiers of surface types. + + --- Enables and disables the controller. + -- Note: Now it works only for ground / naval groups! + -- @function [parent=#Controller] setOnOff + -- @param self + -- @param #boolean value Enable / Disable. + + -- Tasks + + --- Resets current task and then sets the task to the controller. Task is a table that contains task identifier and task parameters. + -- @function [parent=#Controller] setTask + -- @param self + -- @param #Task task + + --- Resets current task of the controller. + -- @function [parent=#Controller] resetTask + -- @param self + + --- Pushes the task to the front of the queue and makes the task active. Further call of function Controller.setTask() function will stop current task, clear the queue and set the new task active. If the task queue is empty the function will work like function Controller.setTask() function. + -- @function [parent=#Controller] pushTask + -- @param self + -- @param #Task task + + --- Pops current (front) task from the queue and makes active next task in the queue (if exists). If no more tasks in the queue the function works like function Controller.resetTask() function. Does nothing if the queue is empty. + -- @function [parent=#Controller] popTask + -- @param self + + --- Returns true if the controller has a task. + -- @function [parent=#Controller] hasTask + -- @param self + -- @return #boolean + + -- Commands + + --TODO: describe #Command structure + --- Sets the command to perform by controller. + -- @function [parent=#Controller] setCommand + -- @param self + -- @param #Command command Table that contains command identifier and command parameters. + + + -- Behaviours + + --- Sets the option to the controller. + -- Option is a pair of identifier and value. Behavior options are global parameters those affect controller behavior in all tasks it performs. + -- Option identifiers and values are stored in table AI.Option in subtables Air, Ground and Naval. + -- + -- OptionId = @{#AI.Option.Air.id} or @{#AI.Option.Ground.id} or @{#AI.Option.Naval.id} + -- OptionValue = AI.Option.Air.val[optionName] or AI.Option.Ground.val[optionName] or AI.Option.Naval.val[optionName] + -- + -- @function [parent=#Controller] setOption + -- @param self + -- @param #OptionId optionId Option identifier. + -- @param #OptionValue optionValue Value of the option. + + + -- Detection + + --- Enum contains identifiers of surface types. + -- @type Controller.Detection + -- @field VISUAL + -- @field OPTIC + -- @field RADAR + -- @field IRST + -- @field RWR + -- @field DLINK + + --- Detected target. + -- @type DetectedTarget + -- @field Wrapper.Object#Object object The target + -- @field #boolean visible The target is visible + -- @field #boolean type The target type is known + -- @field #boolean distance Distance to the target is known + + + --- Checks if the target is detected or not. If one or more detection method is specified the function will return true if the target is detected by at least one of these methods. If no detection methods are specified the function will return true if the target is detected by any method. + -- @function [parent=#Controller] isTargetDetected + -- @param self + -- @param Wrapper.Object#Object target Target to check + -- @param #Controller.Detection detection Controller.Detection detection1, Controller.Detection detection2, ... Controller.Detection detectionN + -- @return #boolean detected True if the target is detected. + -- @return #boolean visible Has effect only if detected is true. True if the target is visible now. + -- @return #ModelTime lastTime Has effect only if visible is false. Last time when target was seen. + -- @return #boolean type Has effect only if detected is true. True if the target type is known. + -- @return #boolean distance Has effect only if detected is true. True if the distance to the target is known. + -- @return #Vec3 lastPos Has effect only if visible is false. Last position of the target when it was seen. + -- @return #Vec3 lastVel Has effect only if visible is false. Last velocity of the target when it was seen. + + + --- Returns list of detected targets. If one or more detection method is specified the function will return targets which were detected by at least one of these methods. If no detection methods are specified the function will return targets which were detected by any method. + -- @function [parent=#Controller] getDetectedTargets + -- @param self + -- @param #Controller.Detection detection Controller.Detection detection1, Controller.Detection detection2, ... Controller.Detection detectionN + -- @return #list<#DetectedTarget> array of DetectedTarget + + --- Know a target. + -- @function [parent=#Controller] knowTarget + -- @param self + -- @param Wrapper.Object#Object object The target. + -- @param #boolean type Target type is known. + -- @param #boolean distance Distance to target is known. + + + Controller = {} --#Controller + +end -- Controller + + +do -- Unit + + --- @type Unit + -- @extends #CoalitionObject + -- @field ID Identifier of an unit. It assigned to an unit by the Mission Editor automatically. + -- @field #Unit.Category Category + -- @field #Unit.RefuelingSystem RefuelingSystem + -- @field #Unit.SensorType SensorType + -- @field #Unit.OpticType OpticType + -- @field #Unit.RadarType RadarType + -- @field #Unit.Desc Desc + -- @field #Unit.DescAircraft DescAircraft + -- @field #Unit.DescAirplane DescAirplane + -- @field #Unit.DescHelicopter DescHelicopter + -- @field #Unit.DescVehicle DescVehicle + -- @field #Unit.DescShip DescShip + -- @field #Unit.AmmoItem AmmoItem + -- @field #list<#Unit.AmmoItem> Ammo + -- @field #Unit.Sensor Sensor + -- @field #Unit.Optic Optic + -- @field #Unit.Radar Radar + -- @field #Unit.IRST IRST + + + --- Enum that stores unit categories. + -- @type Unit.Category + -- @field AIRPLANE + -- @field HELICOPTER + -- @field GROUND_UNIT + -- @field SHIP + -- @field STRUCTURE + + --- Enum that stores aircraft refueling system types. + -- @type Unit.RefuelingSystem + -- @field BOOM_AND_RECEPTACLE + -- @field PROBE_AND_DROGUE + + --- Enum that stores sensor types. + -- @type Unit.SensorType + -- @field OPTIC + -- @field RADAR + -- @field IRST + -- @field RWR + + --- Enum that stores types of optic sensors. + -- @type Unit.OpticType + -- @field TV TV-sensor + -- @field LLTV Low-level TV-sensor + -- @field IR Infra-Red optic sensor + + --- Enum that stores radar types. + -- @type Unit.RadarType + -- @field AS air search radar + -- @field SS surface/land search radar + + + --- A unit descriptor. + -- @type Unit.Desc + -- @extends #Object.Desc + -- @field #Unit.Category category Unit Category + -- @field #Mass massEmpty mass of empty unit + -- @field #number speedMax istance / Time, --maximal velocity + + --- An aircraft descriptor. + -- @type Unit.DescAircraft + -- @extends #Unit.Desc + -- @field #Mass fuelMassMax maximal inner fuel mass + -- @field #Distance range Operational range + -- @field #Distance Hmax Ceiling + -- @field #number VyMax #Distance / #Time, --maximal climb rate + -- @field #number NyMin minimal safe acceleration + -- @field #number NyMax maximal safe acceleration + -- @field #Unit.RefuelingSystem tankerType refueling system type + + --- An airplane descriptor. + -- @type Unit.DescAirplane + -- @extends #Unit.DescAircraft + -- @field #number speedMax0 Distance / Time maximal TAS at ground level + -- @field #number speedMax10K Distance / Time maximal TAS at altitude of 10 km + + --- A helicopter descriptor. + -- @type Unit.DescHelicopter + -- @extends #Unit.DescAircraft + -- @field #Distance HmaxStat static ceiling + + --- A vehicle descriptor. + -- @type Unit.DescVehicle + -- @extends #Unit.Desc + -- @field #Angle maxSlopeAngle maximal slope angle + -- @field #boolean riverCrossing can the vehicle cross a rivers + + --- A ship descriptor. + -- @type Unit.DescShip + -- @extends #Unit.Desc + + --- ammunition item: "type-count" pair. + -- @type Unit.AmmoItem + -- @field #Weapon.Desc desc ammunition descriptor + -- @field #number count ammunition count + + --- A unit sensor. + -- @type Unit.Sensor + -- @field #TypeName typeName + -- @field #Unit.SensorType type + + --- An optic sensor. + -- @type Unit.Optic + -- @extends #Unit.Sensor + -- @field #Unit.OpticType opticType + + --- A radar. + -- @type Unit.Radar + -- @extends #Unit.Sensor + -- @field #Distance detectionDistanceRBM detection distance for RCS=1m^2 in real-beam mapping mode, nil if radar doesn't support surface/land search + -- @field #Distance detectionDistanceHRM detection distance for RCS=1m^2 in high-resolution mapping mode, nil if radar has no HRM + -- @field #Unit.Radar.detectionDistanceAir detectionDistanceAir detection distance for RCS=1m^2 airborne target, nil if radar doesn't support air search + + --- @type Unit.Radar.detectionDistanceAir + -- @field #Unit.Radar.detectionDistanceAir.upperHemisphere upperHemisphere + -- @field #Unit.Radar.detectionDistanceAir.lowerHemisphere lowerHemisphere + + --- @type Unit.Radar.detectionDistanceAir.upperHemisphere + -- @field #Distance headOn + -- @field #Distance tailOn + + --- @type Unit.Radar.detectionDistanceAir.lowerHemisphere + -- @field #Distance headOn + -- @field #Distance tailOn + + --- An IRST. + -- @type Unit.IRST + -- @extends #Unit.Sensor + -- @field #Distance detectionDistanceIdle detection of tail-on target with heat signature = 1 in upper hemisphere, engines are in idle + -- @field #Distance detectionDistanceMaximal ..., engines are in maximal mode + -- @field #Distance detectionDistanceAfterburner ..., engines are in afterburner mode + + --- An RWR. + -- @type Unit.RWR + -- @extends #Unit.Sensor + + --- table that stores all unit sensors. + -- TODO @type Sensors + -- + + + --- Returns unit object by the name assigned to the unit in Mission Editor. If there is unit with such name or the unit is destroyed the function will return nil. The function provides access to non-activated units too. + -- @function [parent=#Unit] getByName + -- @param #string name + -- @return #Unit + + --- Returns if the unit is activated. + -- @function [parent=#Unit] isActive + -- @param #Unit self + -- @return #boolean + + --- Returns name of the player that control the unit or nil if the unit is controlled by A.I. + -- @function [parent=#Unit] getPlayerName + -- @param #Unit self + -- @return #string + + --- returns the unit's unique identifier. + -- @function [parent=#Unit] getID + -- @param #Unit self + -- @return #Unit.ID + + + --- Returns the unit's number in the group. The number is the same number the unit has in ME. It may not be changed during the mission. If any unit in the group is destroyed, the numbers of another units will not be changed. + -- @function [parent=#Unit] getNumber + -- @param #Unit self + -- @return #number + + --- Returns controller of the unit if it exist and nil otherwise + -- @function [parent=#Unit] getController + -- @param #Unit self + -- @return #Controller + + --- Returns the unit's group if it exist and nil otherwise + -- @function [parent=#Unit] getGroup + -- @param #Unit self + -- @return #Group + + --- Returns the unit's callsign - the localized string. + -- @function [parent=#Unit] getCallsign + -- @param #Unit self + -- @return #string + + --- Returns the unit's health. Dead units has health <= 1.0 + -- @function [parent=#Unit] getLife + -- @param #Unit self + -- @return #number + + --- returns the unit's initial health. + -- @function [parent=#Unit] getLife0 + -- @param #Unit self + -- @return #number + + --- Returns relative amount of fuel (from 0.0 to 1.0) the unit has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0. + -- @function [parent=#Unit] getFuel + -- @param #Unit self + -- @return #number + + --- Returns the unit ammunition. + -- @function [parent=#Unit] getAmmo + -- @param #Unit self + -- @return #Unit.Ammo + + --- Returns the unit sensors. + -- @function [parent=#Unit] getSensors + -- @param #Unit self + -- @return #Unit.Sensors + + --- Returns true if the unit has specified types of sensors. This function is more preferable than Unit.getSensors() if you don't want to get information about all the unit's sensors, and just want to check if the unit has specified types of sensors. + -- @function [parent=#Unit] hasSensors + -- @param #Unit self + -- @param #Unit.SensorType sensorType (= nil) Sensor type. + -- @param ... Additional parameters. + -- @return #boolean + -- @usage + -- If sensorType is Unit.SensorType.OPTIC, additional parameters are optic sensor types. Following example checks if the unit has LLTV or IR optics: + -- unit:hasSensors(Unit.SensorType.OPTIC, Unit.OpticType.LLTV, Unit.OpticType.IR) + -- If sensorType is Unit.SensorType.RADAR, additional parameters are radar types. Following example checks if the unit has air search radars: + -- unit:hasSensors(Unit.SensorType.RADAR, Unit.RadarType.AS) + -- If no additional parameters are specified the function returns true if the unit has at least one sensor of specified type. + -- If sensor type is not specified the function returns true if the unit has at least one sensor of any type. + -- + + --- returns two values: + -- First value indicates if at least one of the unit's radar(s) is on. + -- Second value is the object of the radar's interest. Not nil only if at least one radar of the unit is tracking a target. + -- @function [parent=#Unit] getRadar + -- @param #Unit self + -- @return #boolean, Wrapper.Object#Object + + --- Returns unit descriptor. Descriptor type depends on unit category. + -- @function [parent=#Unit] getDesc + -- @param #Unit self + -- @return #Unit.Desc + + + Unit = {} --#Unit + +end -- Unit + + +do -- Group + + --- Represents group of Units. + -- @type Group + -- @field #ID ID Identifier of a group. It is assigned to a group by Mission Editor automatically. + -- @field #Group.Category Category Enum contains identifiers of group types. + + --- Enum contains identifiers of group types. + -- @type Group.Category + -- @field AIRPLANE + -- @field HELICOPTER + -- @field GROUND + -- @field SHIP + + -- Static Functions + + --- Returns group by the name assigned to the group in Mission Editor. + -- @function [parent=#Group] getByName + -- @param #string name + -- @return #Group + + -- Member Functions + + --- returns true if the group exist or false otherwise. + -- @function [parent=#Group] isExist + -- @param #Group self + -- @return #boolean + + --- Destroys the group and all of its units. + -- @function [parent=#Group] destroy + -- @param #Group self + + --- Returns category of the group. + -- @function [parent=#Group] getCategory + -- @param #Group self + -- @return #Group.Category + + --TODO check coalition.side + + --- Returns the coalition of the group. + -- @function [parent=#Group] getCoalition + -- @param #Group self + -- @return #coalition.side + + --- Returns the group's name. This is the same name assigned to the group in Mission Editor. + -- @function [parent=#Group] getName + -- @param #Group self + -- @return #string + + --- Returns the group identifier. + -- @function [parent=#Group] getID + -- @param #Group self + -- @return #ID + + --- Returns the unit with number unitNumber. If the unit is not exists the function will return nil. + -- @function [parent=#Group] getUnit + -- @param #Group self + -- @param #number unitNumber + -- @return #Unit + + --- Returns current size of the group. If some of the units will be destroyed, As units are destroyed the size of the group will be changed. + -- @function [parent=#Group] getSize + -- @param #Group self + -- @return #number + + --- Returns initial size of the group. If some of the units will be destroyed, initial size of the group will not be changed. Initial size limits the unitNumber parameter for Group.getUnit() function. + -- @function [parent=#Group] getInitialSize + -- @param #Group self + -- @return #number + + --- Returns array of the units present in the group now. Destroyed units will not be enlisted at all. + -- @function [parent=#Group] getUnits + -- @param #Group self + -- @return #list<#Unit> array of Units + + --- Returns controller of the group. + -- @function [parent=#Group] getController + -- @param #Group self + -- @return #Controller + + Group = {} --#Group + +end -- Group + + +do -- AI + + --- @type AI + -- @field #AI.Skill Skill + -- @field #AI.Task Task + -- @field #AI.Option Option + + --- @type AI.Skill + -- @field AVERAGE + -- @field GOOD + -- @field HIGH + -- @field EXCELLENT + -- @field PLAYER + -- @field CLIENT + + --- @type AI.Task + -- @field #AI.Task.WeaponExpend WeaponExpend + -- @field #AI.Task.OrbitPattern OrbitPattern + -- @field #AI.Task.Designation Designation + -- @field #AI.Task.WaypointType WaypointType + -- @field #AI.Task.TurnMethod TurnMethod + -- @field #AI.Task.AltitudeType AltitudeType + -- @field #AI.Task.VehicleFormation VehicleFormation + + --- @type AI.Task.WeaponExpend + -- @field ONE + -- @field TWO + -- @field FOUR + -- @field QUARTER + -- @field HALF + -- @field ALL + + --- @type AI.Task.OrbitPattern + -- @field CIRCLE + -- @field RACE_TRACK + + --- @type AI.Task.Designation + -- @field NO + -- @field AUTO + -- @field WP + -- @field IR_POINTER + -- @field LASER + + --- @type AI.Task.WaypointType + -- @field TAKEOFF + -- @field TAKEOFF_PARKING + -- @field TURNING_POINT + -- @field LAND + + --- @type AI.Task.TurnMethod + -- @field FLY_OVER_POINT + -- @field FIN_POINT + + --- @type AI.Task.AltitudeType + -- @field BARO + -- @field RADIO + + --- @type AI.Task.VehicleFormation + -- @field OFF_ROAD + -- @field ON_ROAD + -- @field RANK + -- @field CONE + -- @field DIAMOND + -- @field VEE + -- @field ECHELON_LEFT + -- @field ECHELON_RIGHT + + --- @type AI.Option + -- @field #AI.Option.Air Air + -- @field #AI.Option.Ground Ground + -- @field #AI.Option.Naval Naval + + --- @type AI.Option.Air + -- @field #AI.Option.Air.id id + -- @field #AI.Option.Air.val val + + --- @type AI.Option.Ground + -- @field #AI.Option.Ground.id id + -- @field #AI.Option.Ground.val val + + --- @type AI.Option.Naval + -- @field #AI.Option.Naval.id id + -- @field #AI.Option.Naval.val val + + --TODO: work on formation + --- @type AI.Option.Air.id + -- @field NO_OPTION + -- @field ROE + -- @field REACTION_ON_THREAT + -- @field RADAR_USING + -- @field FLARE_USING + -- @field FORMATION + -- @field RTB_ON_BINGO + -- @field SILENCE + + --- @type AI.Option.Air.val + -- @field #AI.Option.Air.val.ROE ROE + -- @field #AI.Option.Air.val.REACTION_ON_THREAT REACTION_ON_THREAT + -- @field #AI.Option.Air.val.RADAR_USING RADAR_USING + -- @field #AI.Option.Air.val.FLARE_USING FLARE_USING + + --- @type AI.Option.Air.val.ROE + -- @field WEAPON_FREE + -- @field OPEN_FIRE_WEAPON_FREE + -- @field OPEN_FIRE + -- @field RETURN_FIRE + -- @field WEAPON_HOLD + + --- @type AI.Option.Air.val.REACTION_ON_THREAT + -- @field NO_REACTION + -- @field PASSIVE_DEFENCE + -- @field EVADE_FIRE + -- @field BYPASS_AND_ESCAPE + -- @field ALLOW_ABORT_MISSION + + --- @type AI.Option.Air.val.RADAR_USING + -- @field NEVER + -- @field FOR_ATTACK_ONLY + -- @field FOR_SEARCH_IF_REQUIRED + -- @field FOR_CONTINUOUS_SEARCH + + --- @type AI.Option.Air.val.FLARE_USING + -- @field NEVER + -- @field AGAINST_FIRED_MISSILE + -- @field WHEN_FLYING_IN_SAM_WEZ + -- @field WHEN_FLYING_NEAR_ENEMIES + + --- @type AI.Option.Ground.id + -- @field NO_OPTION + -- @field ROE @{#AI.Option.Ground.val.ROE} + -- @field DISPERSE_ON_ATTACK true or false + -- @field ALARM_STATE @{#AI.Option.Ground.val.ALARM_STATE} + + --- @type AI.Option.Ground.val + -- @field #AI.Option.Ground.val.ROE ROE + -- @field #AI.Option.Ground.val.ALARM_STATE ALARM_STATE + + --- @type AI.Option.Ground.val.ROE + -- @field OPEN_FIRE + -- @field RETURN_FIRE + -- @field WEAPON_HOLD + + --- @type AI.Option.Ground.val.ALARM_STATE + -- @field AUTO + -- @field GREEN + -- @field RED + + --- @type AI.Option.Naval.id + -- @field NO_OPTION + -- @field ROE + + --- @type AI.Option.Naval.val + -- @field #AI.Option.Naval.val.ROE ROE + + --- @type AI.Option.Naval.val.ROE + -- @field OPEN_FIRE + -- @field RETURN_FIRE + -- @field WEAPON_HOLD + + AI = {} --#AI + +end -- AI + + + diff --git a/Moose Development/Moose/Dcs/DCSAirbase.lua b/Moose Development/Moose/Dcs/DCSAirbase.lua deleted file mode 100644 index 192444a06..000000000 --- a/Moose Development/Moose/Dcs/DCSAirbase.lua +++ /dev/null @@ -1,54 +0,0 @@ -------------------------------------------------------------------------------- --- @module DCS.DCSAirbase - - ---- Represents airbases: airdromes, helipads and ships with flying decks or landing pads. --- @type Airbase --- @extends Dcs.DCSCoalitionWrapper.Object#CoalitionObject --- @field #Airbase.ID ID Identifier of an airbase. It assigned to an airbase by the Mission Editor automatically. This identifier is used in AI tasks to refer an airbase that exists (spawned and not dead) or not. --- @field #Airbase.Category Category enum contains identifiers of airbase categories. --- @field #Airbase.Desc Desc Airbase descriptor. Airdromes are unique and their types are unique, but helipads and ships are not always unique and may have the same type. - ---- Enum contains identifiers of airbase categories. --- @type Airbase.Category --- @field AIRDROME --- @field HELIPAD --- @field SHIP - ---- Airbase descriptor. Airdromes are unique and their types are unique, but helipads and ships are not always unique and may have the same type. --- @type Airbase.Desc --- @extends #Desc --- @field #Airbase.Category category Category of the airbase type. - ---- Returns airbase by its name. If no airbase found the function will return nil. --- @function [parent=#Airbase] getByName --- @param #string name --- @return #Airbase - ---- Returns airbase descriptor by type name. If no descriptor is found the function will return nil. --- @function [parent=#Airbase] getDescByName --- @param #TypeName typeName Airbase type name. --- @return #Airbase.Desc - ---- Returns Unit that is corresponded to the airbase. Works only for ships. --- @function [parent=#Airbase] getUnit --- @param self --- @return Wrapper.Unit#Unit - ---- Returns identifier of the airbase. --- @function [parent=#Airbase] getID --- @param self --- @return #Airbase.ID - ---- Returns the airbase's callsign - the localized string. --- @function [parent=#Airbase] getCallsign --- @param self --- @return #string - ---- Returns descriptor of the airbase. --- @function [parent=#Airbase] getDesc --- @param self --- @return #Airbase.Desc - - -Airbase = {} --#Airbase diff --git a/Moose Development/Moose/Dcs/DCSCoalitionObject.lua b/Moose Development/Moose/Dcs/DCSCoalitionObject.lua deleted file mode 100644 index b16ca63c2..000000000 --- a/Moose Development/Moose/Dcs/DCSCoalitionObject.lua +++ /dev/null @@ -1,20 +0,0 @@ -------------------------------------------------------------------------------- --- @module DCS.DCSCoalitionObject - ---- @type CoalitionObject --- @extends Dcs.DCSWrapper.Object#Object - - - ---- Returns coalition of the object. --- @function [parent=#CoalitionObject] getCoalition --- @param #CoalitionObject self --- @return Dcs.DCSTypes#coalition.side - ---- Returns object country. --- @function [parent=#CoalitionObject] getCountry --- @param #CoalitionObject self --- @return #country.id - - -CoalitionObject = {} --#CoalitionObject diff --git a/Moose Development/Moose/Dcs/DCSCommand.lua b/Moose Development/Moose/Dcs/DCSCommand.lua deleted file mode 100644 index 89b2b6f9e..000000000 --- a/Moose Development/Moose/Dcs/DCSCommand.lua +++ /dev/null @@ -1,10 +0,0 @@ ---- @module DCS.DCSCommand - - ---- @type Command --- @field #string id --- @field #Command.params params - ---- @type Command.params - -env.info( "Command defined" ) \ No newline at end of file diff --git a/Moose Development/Moose/Dcs/DCSController.lua b/Moose Development/Moose/Dcs/DCSController.lua deleted file mode 100644 index 99666ef97..000000000 --- a/Moose Development/Moose/Dcs/DCSController.lua +++ /dev/null @@ -1,115 +0,0 @@ -------------------------------------------------------------------------------- --- @module DCS.DCSController - ---- Controller is an object that performs A.I.-routines. Other words controller is an instance of A.I.. Controller stores current main task, active enroute tasks and behavior options. Controller performs commands. Please, read DCS A-10C GUI Manual EN.pdf chapter "Task Planning for Unit Groups", page 91 to understand A.I. system of DCS:A-10C. --- --- This class has 2 types of functions: --- --- * Tasks --- * Commands: Commands are instant actions those required zero time to perform. Commands may be used both for control unit/group behavior and control game mechanics. --- @type Controller --- @field #Controller.Detection Detection Enum contains identifiers of surface types. - ---- Enables and disables the controller. --- Note: Now it works only for ground / naval groups! --- @function [parent=#Controller] setOnOff --- @param self --- @param #boolean value Enable / Disable. - --- Tasks - ---- Resets current task and then sets the task to the controller. Task is a table that contains task identifier and task parameters. --- @function [parent=#Controller] setTask --- @param self --- @param #Task task - ---- Resets current task of the controller. --- @function [parent=#Controller] resetTask --- @param self - ---- Pushes the task to the front of the queue and makes the task active. Further call of function Controller.setTask() function will stop current task, clear the queue and set the new task active. If the task queue is empty the function will work like function Controller.setTask() function. --- @function [parent=#Controller] pushTask --- @param self --- @param #Task task - ---- Pops current (front) task from the queue and makes active next task in the queue (if exists). If no more tasks in the queue the function works like function Controller.resetTask() function. Does nothing if the queue is empty. --- @function [parent=#Controller] popTask --- @param self - ---- Returns true if the controller has a task. --- @function [parent=#Controller] hasTask --- @param self --- @return #boolean - --- Commands - ---TODO: describe #Command structure ---- Sets the command to perform by controller. --- @function [parent=#Controller] setCommand --- @param self --- @param #Command command Table that contains command identifier and command parameters. - - --- Behaviours - ---- Sets the option to the controller. --- Option is a pair of identifier and value. Behavior options are global parameters those affect controller behavior in all tasks it performs. --- Option identifiers and values are stored in table AI.Option in subtables Air, Ground and Naval. --- --- OptionId = @{#AI.Option.Air.id} or @{#AI.Option.Ground.id} or @{#AI.Option.Naval.id} --- OptionValue = AI.Option.Air.val[optionName] or AI.Option.Ground.val[optionName] or AI.Option.Naval.val[optionName] --- --- @function [parent=#Controller] setOption --- @param self --- @param #OptionId optionId Option identifier. --- @param #OptionValue optionValue Value of the option. - - --- Detection - ---- Enum contains identifiers of surface types. --- @type Controller.Detection --- @field VISUAL --- @field OPTIC --- @field RADAR --- @field IRST --- @field RWR --- @field DLINK - ---- Detected target. --- @type DetectedTarget --- @field Wrapper.Object#Object object The target --- @field #boolean visible The target is visible --- @field #boolean type The target type is known --- @field #boolean distance Distance to the target is known - - ---- Checks if the target is detected or not. If one or more detection method is specified the function will return true if the target is detected by at least one of these methods. If no detection methods are specified the function will return true if the target is detected by any method. --- @function [parent=#Controller] isTargetDetected --- @param self --- @param Wrapper.Object#Object target Target to check --- @param #Controller.Detection detection Controller.Detection detection1, Controller.Detection detection2, ... Controller.Detection detectionN --- @return #boolean detected True if the target is detected. --- @return #boolean visible Has effect only if detected is true. True if the target is visible now. --- @return #ModelTime lastTime Has effect only if visible is false. Last time when target was seen. --- @return #boolean type Has effect only if detected is true. True if the target type is known. --- @return #boolean distance Has effect only if detected is true. True if the distance to the target is known. --- @return #Vec3 lastPos Has effect only if visible is false. Last position of the target when it was seen. --- @return #Vec3 lastVel Has effect only if visible is false. Last velocity of the target when it was seen. - - ---- Returns list of detected targets. If one or more detection method is specified the function will return targets which were detected by at least one of these methods. If no detection methods are specified the function will return targets which were detected by any method. --- @function [parent=#Controller] getDetectedTargets --- @param self --- @param #Controller.Detection detection Controller.Detection detection1, Controller.Detection detection2, ... Controller.Detection detectionN --- @return #list<#DetectedTarget> array of DetectedTarget - ---- Know a target. --- @function [parent=#Controller] knowTarget --- @param self --- @param Wrapper.Object#Object object The target. --- @param #boolean type Target type is known. --- @param #boolean distance Distance to target is known. - - -Controller = {} --#Controller \ No newline at end of file diff --git a/Moose Development/Moose/Dcs/DCSGroup.lua b/Moose Development/Moose/Dcs/DCSGroup.lua deleted file mode 100644 index 0e1798e07..000000000 --- a/Moose Development/Moose/Dcs/DCSGroup.lua +++ /dev/null @@ -1,83 +0,0 @@ -------------------------------------------------------------------------------- --- @module DCS.DCSGroup - ---- Represents group of Units. --- @type Group --- @field #ID ID Identifier of a group. It is assigned to a group by Mission Editor automatically. --- @field #Group.Category Category Enum contains identifiers of group types. - ---- Enum contains identifiers of group types. --- @type Group.Category --- @field AIRPLANE --- @field HELICOPTER --- @field GROUND --- @field SHIP - --- Static Functions - ---- Returns group by the name assigned to the group in Mission Editor. --- @function [parent=#Group] getByName --- @param #string name --- @return #Group - --- Member Functions - ---- returns true if the group exist or false otherwise. --- @function [parent=#Group] isExist --- @param #Group self --- @return #boolean - ---- Destroys the group and all of its units. --- @function [parent=#Group] destroy --- @param #Group self - ---- Returns category of the group. --- @function [parent=#Group] getCategory --- @param #Group self --- @return #Group.Category - ---TODO check coalition.side - ---- Returns the coalition of the group. --- @function [parent=#Group] getCoalition --- @param #Group self --- @return Dcs.DCSCoalitionWrapper.Object#coalition.side - ---- Returns the group's name. This is the same name assigned to the group in Mission Editor. --- @function [parent=#Group] getName --- @param #Group self --- @return #string - ---- Returns the group identifier. --- @function [parent=#Group] getID --- @param #Group self --- @return #ID - ---- Returns the unit with number unitNumber. If the unit is not exists the function will return nil. --- @function [parent=#Group] getUnit --- @param #Group self --- @param #number unitNumber --- @return Dcs.DCSWrapper.Unit#Unit - ---- Returns current size of the group. If some of the units will be destroyed, As units are destroyed the size of the group will be changed. --- @function [parent=#Group] getSize --- @param #Group self --- @return #number - ---- Returns initial size of the group. If some of the units will be destroyed, initial size of the group will not be changed. Initial size limits the unitNumber parameter for Group.getUnit() function. --- @function [parent=#Group] getInitialSize --- @param #Group self --- @return #number - ---- Returns array of the units present in the group now. Destroyed units will not be enlisted at all. --- @function [parent=#Group] getUnits --- @param #Group self --- @return #list array of Units - ---- Returns controller of the group. --- @function [parent=#Group] getController --- @param #Group self --- @return Controller#Controller - -Group = {} --#Group - diff --git a/Moose Development/Moose/Dcs/DCSObject.lua b/Moose Development/Moose/Dcs/DCSObject.lua deleted file mode 100644 index fb09234d4..000000000 --- a/Moose Development/Moose/Dcs/DCSObject.lua +++ /dev/null @@ -1,73 +0,0 @@ -------------------------------------------------------------------------------- --- @module DCS.DCSObject - ---- @type Object --- @field #Object.Category Category --- @field #Object.Desc Desc - ---- @type Object.Category --- @field UNIT --- @field WEAPON --- @field STATIC --- @field SCENERY --- @field BASE - ---- @type Object.Desc --- @extends #Desc --- @field #number life initial life level --- @field #Box3 box bounding box of collision geometry - ---- @function [parent=#Object] isExist --- @param #Object self --- @return #boolean - ---- @function [parent=#Object] destroy --- @param #Object self - ---- @function [parent=#Object] getCategory --- @param #Object self --- @return #Object.Category - ---- Returns type name of the Object. --- @function [parent=#Object] getTypeName --- @param #Object self --- @return #string - ---- Returns object descriptor. --- @function [parent=#Object] getDesc --- @param #Object self --- @return #Object.Desc - ---- Returns true if the object belongs to the category. --- @function [parent=#Object] hasAttribute --- @param #Object self --- @param #AttributeName attributeName Attribute name to check. --- @return #boolean - ---- Returns name of the object. This is the name that is assigned to the object in the Mission Editor. --- @function [parent=#Object] getName --- @param #Object self --- @return #string - ---- Returns object coordinates for current time. --- @function [parent=#Object] getPoint --- @param #Object self --- @return #Vec3 - ---- Returns object position for current time. --- @function [parent=#Object] getPosition --- @param #Object self --- @return #Position3 - ---- Returns the unit's velocity vector. --- @function [parent=#Object] getVelocity --- @param #Object self --- @return #Vec3 - ---- Returns true if the unit is in air. --- @function [parent=#Object] inAir --- @param #Object self --- @return #boolean - -Object = {} --#Object - diff --git a/Moose Development/Moose/Dcs/DCSStaticObject.lua b/Moose Development/Moose/Dcs/DCSStaticObject.lua deleted file mode 100644 index e01f017da..000000000 --- a/Moose Development/Moose/Dcs/DCSStaticObject.lua +++ /dev/null @@ -1,34 +0,0 @@ -------------------------------------------------------------------------------- --- @module DCS.DCSStaticObject - - -------------------------------------------------------------------------------- --- @module StaticObject --- @extends CoalitionWrapper.Object#CoalitionObject - ---- Represents static object added in the Mission Editor. --- @type StaticObject --- @field #StaticObject.ID ID Identifier of a StaticObject. It assigned to an StaticObject by the Mission Editor automatically. --- @field #StaticObject.Desc Desc Descriptor of StaticObject and Unit are equal. StaticObject is just a passive variant of Unit. - ---- StaticObject descriptor. Airdromes are unique and their types are unique, but helipads and ships are not always unique and may have the same type. --- @type StaticObject.Desc --- @extends Wrapper.Unit#Unit.Desc - ---- Returns static object by its name. If no static object found nil will be returned. --- @function [parent=#StaticObject] getByName --- @param #string name Name of static object to find. --- @return #StaticObject - ---- returns identifier of the static object. --- @function [parent=#StaticObject] getID --- @param #StaticObject self --- @return #StaticObject.ID - ---- Returns descriptor of the StaticObject. --- @function [parent=#StaticObject] getDesc --- @param #StaticObject self --- @return #StaticObject.Desc - - -StaticObject = {} --#StaticObject diff --git a/Moose Development/Moose/Dcs/DCSTask.lua b/Moose Development/Moose/Dcs/DCSTask.lua deleted file mode 100644 index 825b9f0bc..000000000 --- a/Moose Development/Moose/Dcs/DCSTask.lua +++ /dev/null @@ -1,15 +0,0 @@ ---- @module DCS.DCSTask - - ---- A task descriptor (internal structure for DCS World) --- @type Task --- @field #string id --- @field #Task.param param - ---- @type Task.param - ---- List of @{#Task} --- @type TaskArray --- @list <#Task> - -env.info( "Task defined" ) diff --git a/Moose Development/Moose/Dcs/DCSTime.lua b/Moose Development/Moose/Dcs/DCSTime.lua deleted file mode 100644 index 766f53317..000000000 --- a/Moose Development/Moose/Dcs/DCSTime.lua +++ /dev/null @@ -1,8 +0,0 @@ -------------------------------------------------------------------------------- --- @module DCS.DCSTime - ---- @type ModelTime --- @extends #number - ---- @type Time --- @extends #number \ No newline at end of file diff --git a/Moose Development/Moose/Dcs/DCSTypes.lua b/Moose Development/Moose/Dcs/DCSTypes.lua deleted file mode 100644 index bb00369e4..000000000 --- a/Moose Development/Moose/Dcs/DCSTypes.lua +++ /dev/null @@ -1,246 +0,0 @@ -------------------------------------------------------------------------------- --- @module DCS.DCSTypes - - - ---- Time is given in seconds. --- @type Time --- @extends #number - ---- Model time is the time that drives the simulation. Model time may be stopped, accelerated and decelerated relative real time. --- @type ModelTime --- @extends #number - ---- Mission time is a model time plus time of the mission start. --- @type MissionTime --- @extends #number - - ---- Distance is given in meters. --- @type Distance --- @extends #number - ---- Angle is given in radians. --- @type Angle --- @extends #number - ---- Azimuth is an angle of rotation around world axis y counter-clockwise. --- @type Azimuth --- @extends #number - ---- Mass is given in kilograms. --- @type Mass --- @extends #number - ---- Vec3 type is a 3D-vector. --- DCS world has 3-dimensional coordinate system. DCS ground is an infinite plain. --- @type Vec3 --- @field #Distance x is directed to the north --- @field #Distance z is directed to the east --- @field #Distance y is directed up - ---- Vec2 is a 2D-vector for the ground plane as a reference plane. --- @type Vec2 --- @field #Distance x Vec2.x = Vec3.x --- @field #Distance y Vec2.y = Vec3.z - ---- Position is a composite structure. It consists of both coordinate vector and orientation matrix. Position3 (also known as "Pos3" for short) is a table that has following format: --- @type Position3 --- @field #Vec3 p --- @field #Vec3 x --- @field #Vec3 y --- @field #Vec3 z - ---- 3-dimensional box. --- @type Box3 --- @field #Vec3 min --- @field #Vec3 max - ---- Each object belongs to a type. Object type is a named couple of properties those independent of mission and common for all units of the same type. Name of unit type is a string. Samples of unit type: "Su-27", "KAMAZ" and "M2 Bradley". --- @type TypeName --- @extends #string - ---- AttributeName = string --- Each object type may have attributes. --- Attributes are enlisted in ./Scripts/Database/db_attributes.Lua. --- To know what attributes the object type has, look for the unit type script in sub-directories planes/, helicopter/s, vehicles, navy/ of ./Scripts/Database/ directory. --- @type AttributeName --- @extends #string - ---- List of @{#AttributeName} --- @type AttributeNameArray --- @list <#AttributeName> - ---- @type AI --- @field #AI.Skill Skill --- @field #AI.Task Task --- @field #AI.Option Option - ---- @type AI.Skill --- @field AVERAGE --- @field GOOD --- @field HIGH --- @field EXCELLENT --- @field PLAYER --- @field CLIENT - ---- @type AI.Task --- @field #AI.Task.WeaponExpend WeaponExpend --- @field #AI.Task.OrbitPattern OrbitPattern --- @field #AI.Task.Designation Designation --- @field #AI.Task.WaypointType WaypointType --- @field #AI.Task.TurnMethod TurnMethod --- @field #AI.Task.AltitudeType AltitudeType --- @field #AI.Task.VehicleFormation VehicleFormation - ---- @type AI.Task.WeaponExpend --- @field ONE --- @field TWO --- @field FOUR --- @field QUARTER --- @field HALF --- @field ALL - ---- @type AI.Task.OrbitPattern --- @field CIRCLE --- @field RACE_TRACK - ---- @type AI.Task.Designation --- @field NO --- @field AUTO --- @field WP --- @field IR_POINTER --- @field LASER - ---- @type AI.Task.WaypointType --- @field TAKEOFF --- @field TAKEOFF_PARKING --- @field TURNING_POINT --- @field LAND - ---- @type AI.Task.TurnMethod --- @field FLY_OVER_POINT --- @field FIN_POINT - ---- @type AI.Task.AltitudeType --- @field BARO --- @field RADIO - ---- @type AI.Task.VehicleFormation --- @field OFF_ROAD --- @field ON_ROAD --- @field RANK --- @field CONE --- @field DIAMOND --- @field VEE --- @field ECHELON_LEFT --- @field ECHELON_RIGHT - ---- @type AI.Option --- @field #AI.Option.Air Air --- @field #AI.Option.Ground Ground --- @field #AI.Option.Naval Naval - ---- @type AI.Option.Air --- @field #AI.Option.Air.id id --- @field #AI.Option.Air.val val - ---- @type AI.Option.Ground --- @field #AI.Option.Ground.id id --- @field #AI.Option.Ground.val val - ---- @type AI.Option.Naval --- @field #AI.Option.Naval.id id --- @field #AI.Option.Naval.val val - ---TODO: work on formation ---- @type AI.Option.Air.id --- @field NO_OPTION --- @field ROE --- @field REACTION_ON_THREAT --- @field RADAR_USING --- @field FLARE_USING --- @field FORMATION --- @field RTB_ON_BINGO --- @field SILENCE - ---- @type AI.Option.Air.val --- @field #AI.Option.Air.val.ROE ROE --- @field #AI.Option.Air.val.REACTION_ON_THREAT REACTION_ON_THREAT --- @field #AI.Option.Air.val.RADAR_USING RADAR_USING --- @field #AI.Option.Air.val.FLARE_USING FLARE_USING - ---- @type AI.Option.Air.val.ROE --- @field WEAPON_FREE --- @field OPEN_FIRE_WEAPON_FREE --- @field OPEN_FIRE --- @field RETURN_FIRE --- @field WEAPON_HOLD - ---- @type AI.Option.Air.val.REACTION_ON_THREAT --- @field NO_REACTION --- @field PASSIVE_DEFENCE --- @field EVADE_FIRE --- @field BYPASS_AND_ESCAPE --- @field ALLOW_ABORT_MISSION - ---- @type AI.Option.Air.val.RADAR_USING --- @field NEVER --- @field FOR_ATTACK_ONLY --- @field FOR_SEARCH_IF_REQUIRED --- @field FOR_CONTINUOUS_SEARCH - ---- @type AI.Option.Air.val.FLARE_USING --- @field NEVER --- @field AGAINST_FIRED_MISSILE --- @field WHEN_FLYING_IN_SAM_WEZ --- @field WHEN_FLYING_NEAR_ENEMIES - ---- @type AI.Option.Ground.id --- @field NO_OPTION --- @field ROE @{#AI.Option.Ground.val.ROE} --- @field DISPERSE_ON_ATTACK true or false --- @field ALARM_STATE @{#AI.Option.Ground.val.ALARM_STATE} - ---- @type AI.Option.Ground.val --- @field #AI.Option.Ground.val.ROE ROE --- @field #AI.Option.Ground.val.ALARM_STATE ALARM_STATE - ---- @type AI.Option.Ground.val.ROE --- @field OPEN_FIRE --- @field RETURN_FIRE --- @field WEAPON_HOLD - ---- @type AI.Option.Ground.val.ALARM_STATE --- @field AUTO --- @field GREEN --- @field RED - ---- @type AI.Option.Naval.id --- @field NO_OPTION --- @field ROE - ---- @type AI.Option.Naval.val --- @field #AI.Option.Naval.val.ROE ROE - ---- @type AI.Option.Naval.val.ROE --- @field OPEN_FIRE --- @field RETURN_FIRE --- @field WEAPON_HOLD - -AI = {} --#AI - - ---- @type Desc --- @field #TypeName typeName type name --- @field #string displayName localized display name --- @field #table attributes object type attributes - ---- A distance type --- @type Distance - ---- An angle type --- @type Angle - -env.info( 'AI types created' ) - diff --git a/Moose Development/Moose/Dcs/DCSUnit.lua b/Moose Development/Moose/Dcs/DCSUnit.lua deleted file mode 100644 index 723b653d1..000000000 --- a/Moose Development/Moose/Dcs/DCSUnit.lua +++ /dev/null @@ -1,241 +0,0 @@ -------------------------------------------------------------------------------- --- @module DCS.DCSUnit - ---- @type Unit --- @extends Dcs.DCSCoalitionWrapper.Object#CoalitionObject --- @field ID Identifier of an unit. It assigned to an unit by the Mission Editor automatically. --- @field #Unit.Category Category --- @field #Unit.RefuelingSystem RefuelingSystem --- @field #Unit.SensorType SensorType --- @field #Unit.OpticType OpticType --- @field #Unit.RadarType RadarType --- @field #Unit.Desc Desc --- @field #Unit.DescAircraft DescAircraft --- @field #Unit.DescAirplane DescAirplane --- @field #Unit.DescHelicopter DescHelicopter --- @field #Unit.DescVehicle DescVehicle --- @field #Unit.DescShip DescShip --- @field #Unit.AmmoItem AmmoItem --- @field #list<#Unit.AmmoItem> Ammo --- @field #Unit.Sensor Sensor --- @field #Unit.Optic Optic --- @field #Unit.Radar Radar --- @field #Unit.IRST IRST - - ---- Enum that stores unit categories. --- @type Unit.Category --- @field AIRPLANE --- @field HELICOPTER --- @field GROUND_UNIT --- @field SHIP --- @field STRUCTURE - ---- Enum that stores aircraft refueling system types. --- @type Unit.RefuelingSystem --- @field BOOM_AND_RECEPTACLE --- @field PROBE_AND_DROGUE - ---- Enum that stores sensor types. --- @type Unit.SensorType --- @field OPTIC --- @field RADAR --- @field IRST --- @field RWR - ---- Enum that stores types of optic sensors. --- @type Unit.OpticType --- @field TV TV-sensor --- @field LLTV Low-level TV-sensor --- @field IR Infra-Red optic sensor - ---- Enum that stores radar types. --- @type Unit.RadarType --- @field AS air search radar --- @field SS surface/land search radar - - ---- A unit descriptor. --- @type Unit.Desc --- @extends Wrapper.Object#Object.Desc --- @field #Unit.Category category Unit Category --- @field #Mass massEmpty mass of empty unit --- @field #number speedMax istance / Time, --maximal velocity - ---- An aircraft descriptor. --- @type Unit.DescAircraft --- @extends Wrapper.Unit#Unit.Desc --- @field #Mass fuelMassMax maximal inner fuel mass --- @field #Distance range Operational range --- @field #Distance Hmax Ceiling --- @field #number VyMax #Distance / #Time, --maximal climb rate --- @field #number NyMin minimal safe acceleration --- @field #number NyMax maximal safe acceleration --- @field #Unit.RefuelingSystem tankerType refueling system type - ---- An airplane descriptor. --- @type Unit.DescAirplane --- @extends Wrapper.Unit#Unit.DescAircraft --- @field #number speedMax0 Distance / Time maximal TAS at ground level --- @field #number speedMax10K Distance / Time maximal TAS at altitude of 10 km - ---- A helicopter descriptor. --- @type Unit.DescHelicopter --- @extends Wrapper.Unit#Unit.DescAircraft --- @field #Distance HmaxStat static ceiling - ---- A vehicle descriptor. --- @type Unit.DescVehicle --- @extends Wrapper.Unit#Unit.Desc --- @field #Angle maxSlopeAngle maximal slope angle --- @field #boolean riverCrossing can the vehicle cross a rivers - ---- A ship descriptor. --- @type Unit.DescShip --- @extends #Unit.Desc - ---- ammunition item: "type-count" pair. --- @type Unit.AmmoItem --- @field #Weapon.Desc desc ammunition descriptor --- @field #number count ammunition count - ---- A unit sensor. --- @type Unit.Sensor --- @field #TypeName typeName --- @field #Unit.SensorType type - ---- An optic sensor. --- @type Unit.Optic --- @extends Wrapper.Unit#Unit.Sensor --- @field #Unit.OpticType opticType - ---- A radar. --- @type Unit.Radar --- @extends Wrapper.Unit#Unit.Sensor --- @field #Distance detectionDistanceRBM detection distance for RCS=1m^2 in real-beam mapping mode, nil if radar doesn't support surface/land search --- @field #Distance detectionDistanceHRM detection distance for RCS=1m^2 in high-resolution mapping mode, nil if radar has no HRM --- @field #Unit.Radar.detectionDistanceAir detectionDistanceAir detection distance for RCS=1m^2 airborne target, nil if radar doesn't support air search - ---- @type Unit.Radar.detectionDistanceAir --- @field #Unit.Radar.detectionDistanceAir.upperHemisphere upperHemisphere --- @field #Unit.Radar.detectionDistanceAir.lowerHemisphere lowerHemisphere - ---- @type Unit.Radar.detectionDistanceAir.upperHemisphere --- @field #Distance headOn --- @field #Distance tailOn - ---- @type Unit.Radar.detectionDistanceAir.lowerHemisphere --- @field #Distance headOn --- @field #Distance tailOn - ---- An IRST. --- @type Wrapper.Unit#Unit.IRST --- @extends Unit.Sensor --- @field #Distance detectionDistanceIdle detection of tail-on target with heat signature = 1 in upper hemisphere, engines are in idle --- @field #Distance detectionDistanceMaximal ..., engines are in maximal mode --- @field #Distance detectionDistanceAfterburner ..., engines are in afterburner mode - ---- An RWR. --- @type Unit.RWR --- @extends Wrapper.Unit#Unit.Sensor - ---- table that stores all unit sensors. --- TODO @type Sensors --- - - ---- Returns unit object by the name assigned to the unit in Mission Editor. If there is unit with such name or the unit is destroyed the function will return nil. The function provides access to non-activated units too. --- @function [parent=#Unit] getByName --- @param #string name --- @return #Unit - ---- Returns if the unit is activated. --- @function [parent=#Unit] isActive --- @param #Unit self --- @return #boolean - ---- Returns name of the player that control the unit or nil if the unit is controlled by A.I. --- @function [parent=#Unit] getPlayerName --- @param #Unit self --- @return #string - ---- returns the unit's unique identifier. --- @function [parent=#Unit] getID --- @param #Unit self --- @return #Unit.ID - - ---- Returns the unit's number in the group. The number is the same number the unit has in ME. It may not be changed during the mission. If any unit in the group is destroyed, the numbers of another units will not be changed. --- @function [parent=#Unit] getNumber --- @param #Unit self --- @return #number - ---- Returns controller of the unit if it exist and nil otherwise --- @function [parent=#Unit] getController --- @param #Unit self --- @return #Controller - ---- Returns the unit's group if it exist and nil otherwise --- @function [parent=#Unit] getGroup --- @param #Unit self --- @return Dcs.DCSWrapper.Group#Group - ---- Returns the unit's callsign - the localized string. --- @function [parent=#Unit] getCallsign --- @param #Unit self --- @return #string - ---- Returns the unit's health. Dead units has health <= 1.0 --- @function [parent=#Unit] getLife --- @param #Unit self --- @return #number - ---- returns the unit's initial health. --- @function [parent=#Unit] getLife0 --- @param #Unit self --- @return #number - ---- Returns relative amount of fuel (from 0.0 to 1.0) the unit has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0. --- @function [parent=#Unit] getFuel --- @param #Unit self --- @return #number - ---- Returns the unit ammunition. --- @function [parent=#Unit] getAmmo --- @param #Unit self --- @return #Unit.Ammo - ---- Returns the unit sensors. --- @function [parent=#Unit] getSensors --- @param #Unit self --- @return #Unit.Sensors - ---- Returns true if the unit has specified types of sensors. This function is more preferable than Unit.getSensors() if you don't want to get information about all the unit's sensors, and just want to check if the unit has specified types of sensors. --- @function [parent=#Unit] hasSensors --- @param #Unit self --- @param #Unit.SensorType sensorType (= nil) Sensor type. --- @param ... Additional parameters. --- @return #boolean --- @usage --- If sensorType is Unit.SensorType.OPTIC, additional parameters are optic sensor types. Following example checks if the unit has LLTV or IR optics: --- unit:hasSensors(Unit.SensorType.OPTIC, Unit.OpticType.LLTV, Unit.OpticType.IR) --- If sensorType is Unit.SensorType.RADAR, additional parameters are radar types. Following example checks if the unit has air search radars: --- unit:hasSensors(Unit.SensorType.RADAR, Unit.RadarType.AS) --- If no additional parameters are specified the function returns true if the unit has at least one sensor of specified type. --- If sensor type is not specified the function returns true if the unit has at least one sensor of any type. --- - ---- returns two values: --- First value indicates if at least one of the unit's radar(s) is on. --- Second value is the object of the radar's interest. Not nil only if at least one radar of the unit is tracking a target. --- @function [parent=#Unit] getRadar --- @param #Unit self --- @return #boolean, Wrapper.Object#Object - ---- Returns unit descriptor. Descriptor type depends on unit category. --- @function [parent=#Unit] getDesc --- @param #Unit self --- @return #Unit.Desc - - -Unit = {} --#Unit diff --git a/Moose Development/Moose/Dcs/DCSVec3.lua b/Moose Development/Moose/Dcs/DCSVec3.lua deleted file mode 100644 index 0128e1a7a..000000000 --- a/Moose Development/Moose/Dcs/DCSVec3.lua +++ /dev/null @@ -1,11 +0,0 @@ -------------------------------------------------------------------------------- --- @module DCS.DCSVec3 - - - ---- --- @type Vec3 --- @field #number x --- @field #number y --- @field #number z -Vec3 = {} \ No newline at end of file diff --git a/Moose Development/Moose/Dcs/DCSZone.lua b/Moose Development/Moose/Dcs/DCSZone.lua deleted file mode 100644 index 6070d8cc9..000000000 --- a/Moose Development/Moose/Dcs/DCSZone.lua +++ /dev/null @@ -1,10 +0,0 @@ -------------------------------------------------------------------------------- --- @module DCS.DCSZone - - - ---- --- @type Zone --- @field DCSVec3#Vec3 point --- @field #number radius -Zone = {} diff --git a/Moose Development/Moose/Dcs/DCScoalition.lua b/Moose Development/Moose/Dcs/DCScoalition.lua deleted file mode 100644 index 309492fd0..000000000 --- a/Moose Development/Moose/Dcs/DCScoalition.lua +++ /dev/null @@ -1,16 +0,0 @@ -------------------------------------------------------------------------------- --- @module DCS.DCScoalition - ---- @type coalition --- @field #coalition.side side - ---- @type coalition.side --- @field NEUTRAL --- @field RED --- @field BLUE - ---- @function [parent=#coalition] getCountryCoalition --- @param #number countryId --- @return #number coalitionId - -coalition = coalition -- #coalition diff --git a/Moose Development/Moose/Dcs/DCScountry.lua b/Moose Development/Moose/Dcs/DCScountry.lua deleted file mode 100644 index 32ff4c34e..000000000 --- a/Moose Development/Moose/Dcs/DCScountry.lua +++ /dev/null @@ -1,27 +0,0 @@ -------------------------------------------------------------------------------- --- @module DCS.DCScountry - ---- @type country --- @field #country.id id -country = country -- #country - ---- @type country.id --- @field RUSSIA --- @field UKRAINE --- @field USA --- @field TURKEY --- @field UK --- @field FRANCE --- @field GERMANY --- @field CANADA --- @field SPAIN --- @field THE_NETHERLANDS --- @field BELGIUM --- @field NORWAY --- @field DENMARK --- @field ISRAEL --- @field GEORGIA --- @field INSURGENTS --- @field ABKHAZIA --- @field SOUTH_OSETIA --- @field ITALY diff --git a/Moose Development/Moose/Dcs/DCSenv.lua b/Moose Development/Moose/Dcs/DCSenv.lua deleted file mode 100644 index 8b71f0f9e..000000000 --- a/Moose Development/Moose/Dcs/DCSenv.lua +++ /dev/null @@ -1,27 +0,0 @@ -------------------------------------------------------------------------------- --- @module DCS.DCSenv - ---- @type env - ---- Add message to simulator log with caption "INFO". Message box is optional. --- @function [parent=#env] info --- @field #string message message string to add to log. --- @field #boolean showMessageBox If the parameter is true Message Box will appear. Optional. - ---- Add message to simulator log with caption "WARNING". Message box is optional. --- @function [parent=#env] warning --- @field #string message message string to add to log. --- @field #boolean showMessageBox If the parameter is true Message Box will appear. Optional. - ---- Add message to simulator log with caption "ERROR". Message box is optional. --- @function [parent=#env] error --- @field #string message message string to add to log. --- @field #boolean showMessageBox If the parameter is true Message Box will appear. Optional. - ---- Enables/disables appearance of message box each time lua error occurs. --- @function [parent=#env] setErrorMessageBoxEnabled --- @field #boolean on if true message box appearance is enabled. - - - -env = {} --#env diff --git a/Moose Development/Moose/Dcs/DCSland.lua b/Moose Development/Moose/Dcs/DCSland.lua deleted file mode 100644 index 5e4d2c77c..000000000 --- a/Moose Development/Moose/Dcs/DCSland.lua +++ /dev/null @@ -1,26 +0,0 @@ -------------------------------------------------------------------------------- --- @module DCS.land - ---- @type land --- @field #land.SurfaceType SurfaceType - - ---- @type land.SurfaceType --- @field LAND --- @field SHALLOW_WATER --- @field WATER --- @field ROAD --- @field RUNWAY - ---- Returns altitude MSL of the point. --- @function [parent=#land] getHeight --- @param #Vec2 point point on the ground. --- @return Dcs.DCSTypes#Distance - ---- returns surface type at the given point. --- @function [parent=#land] getSurfaceType --- @param #Vec2 point Point on the land. --- @return #land.SurfaceType - - -land = {} --#land \ No newline at end of file diff --git a/Moose Development/Moose/Dcs/DCStimer.lua b/Moose Development/Moose/Dcs/DCStimer.lua deleted file mode 100644 index 91fb3b93d..000000000 --- a/Moose Development/Moose/Dcs/DCStimer.lua +++ /dev/null @@ -1,45 +0,0 @@ -------------------------------------------------------------------------------- --- @module DCS.DCStimer - ---- @type timer - - ---- Returns model time in seconds. --- @function [parent=#timer] getTime --- @return #Time - ---- Returns mission time in seconds. --- @function [parent=#timer] getAbsTime --- @return #Time - ---- Returns mission start time in seconds. --- @function [parent=#timer] getTime0 --- @return #Time - ---- Schedules function to call at desired model time. --- Time function FunctionToCall(any argument, Time time) --- --- ... --- --- return ... --- --- end --- --- Must return model time of next call or nil. Note that the DCS scheduler calls the function in protected mode and any Lua errors in the called function will be trapped and not reported. If the function triggers a Lua error then it will be terminated and not scheduled to run again. --- @function [parent=#timer] scheduleFunction --- @param #FunctionToCall functionToCall Lua-function to call. Must have prototype of FunctionToCall. --- @param functionArgument Function argument of any type to pass to functionToCall. --- @param #Time time Model time of the function call. --- @return functionId - ---- Re-schedules function to call at another model time. --- @function [parent=#timer] setFunctionTime --- @param functionId Lua-function to call. Must have prototype of FunctionToCall. --- @param #Time time Model time of the function call. - - ---- Removes the function from schedule. --- @function [parent=#timer] removeFunction --- @param functionId Function identifier to remove from schedule - -timer = {} --#timer diff --git a/Moose Development/Moose/Dcs/DCStrigger.lua b/Moose Development/Moose/Dcs/DCStrigger.lua deleted file mode 100644 index c1638c5bb..000000000 --- a/Moose Development/Moose/Dcs/DCStrigger.lua +++ /dev/null @@ -1,8 +0,0 @@ -------------------------------------------------------------------------------- --- @module DCS.DCStrigger - - -trigger = {} --#timer - - - \ No newline at end of file diff --git a/Moose Development/Moose/Dcs/DCSworld.lua b/Moose Development/Moose/Dcs/DCSworld.lua deleted file mode 100644 index 80fed64a2..000000000 --- a/Moose Development/Moose/Dcs/DCSworld.lua +++ /dev/null @@ -1,35 +0,0 @@ -------------------------------------------------------------------------------- --- @module DCS.DCSWorld - ---- @type world --- @field #world.event event - - ---- @type world.event --- @field S_EVENT_INVALID --- @field S_EVENT_SHOT --- @field S_EVENT_HIT --- @field S_EVENT_TAKEOFF --- @field S_EVENT_LAND --- @field S_EVENT_CRASH --- @field S_EVENT_EJECTION --- @field S_EVENT_REFUELING --- @field S_EVENT_DEAD --- @field S_EVENT_PILOT_DEAD --- @field S_EVENT_BASE_CAPTURED --- @field S_EVENT_MISSION_START --- @field S_EVENT_MISSION_END --- @field S_EVENT_TOOK_CONTROL --- @field S_EVENT_REFUELING_STOP --- @field S_EVENT_BIRTH --- @field S_EVENT_HUMAN_FAILURE --- @field S_EVENT_ENGINE_STARTUP --- @field S_EVENT_ENGINE_SHUTDOWN --- @field S_EVENT_PLAYER_ENTER_UNIT --- @field S_EVENT_PLAYER_LEAVE_UNIT --- @field S_EVENT_PLAYER_COMMENT --- @field S_EVENT_SHOOTING_START --- @field S_EVENT_SHOOTING_END --- @field S_EVENT_MAX - -world = {} --#world \ No newline at end of file diff --git a/Moose Development/Moose/Functional/ATC_Ground.lua b/Moose Development/Moose/Functional/ATC_Ground.lua index 707a8ff24..af4ac91bf 100644 --- a/Moose Development/Moose/Functional/ATC_Ground.lua +++ b/Moose Development/Moose/Functional/ATC_Ground.lua @@ -12,7 +12,7 @@ -- === -- -- @module Functional.ATC_Ground - +-- @image Air_Traffic_Control_Ground_Operations.JPG --- @type ATC_GROUND -- @field Core.Set#SET_CLIENT SetClient diff --git a/Moose Development/Moose/Functional/CleanUp.lua b/Moose Development/Moose/Functional/CleanUp.lua index 04f4be5b6..63bda13d6 100644 --- a/Moose Development/Moose/Functional/CleanUp.lua +++ b/Moose Development/Moose/Functional/CleanUp.lua @@ -198,7 +198,7 @@ end --- Destroys a missile from the simulator, but checks first if it is still existing! -- @param #CLEANUP_AIRBASE self --- @param Dcs.DCSTypes#Weapon MissileObject +-- @param DCS#Weapon MissileObject function CLEANUP_AIRBASE.__:DestroyMissile( MissileObject ) self:F( { MissileObject } ) @@ -288,9 +288,9 @@ function CLEANUP_AIRBASE.__:OnEventHit( Event ) end end ---- Add the @{DCSWrapper.Unit#Unit} to the CleanUpList for CleanUp. +--- Add the @{DCS#Unit} to the CleanUpList for CleanUp. -- @param #CLEANUP_AIRBASE self --- @param Wrapper.Unit#UNIT CleanUpUnit +-- @param DCS#UNIT CleanUpUnit -- @oaram #string CleanUpUnitName function CLEANUP_AIRBASE.__:AddForCleanUp( CleanUpUnit, CleanUpUnitName ) self:F( { CleanUpUnit, CleanUpUnitName } ) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 30de4a170..9e38a5b45 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -37,7 +37,7 @@ do -- DETECTION_BASE --- @type DETECTION_BASE -- @field Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. - -- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are accepted to be detected. + -- @field DCS#Distance DetectionRange The range till which targets are accepted to be detected. -- @field #DETECTION_BASE.DetectedObjects DetectedObjects The list of detected objects. -- @field #table DetectedObjectsIdentified Map of the DetectedObjects identified. -- @field #number DetectionRun @@ -553,7 +553,7 @@ do -- DETECTION_BASE --self:F( DetectedTargets ) for DetectionObjectID, Detection in pairs( DetectedTargets ) do - local DetectedObject = Detection.object -- Dcs.DCSWrapper.Object#Object + local DetectedObject = Detection.object -- DCS#Object if DetectedObject and DetectedObject:isExist() and DetectedObject.id_ < 50000000 then -- and ( DetectedObject:getCategory() == Object.Category.UNIT or DetectedObject:getCategory() == Object.Category.STATIC ) then @@ -891,7 +891,7 @@ do -- DETECTION_BASE -- DetectionObject:FilterCategories( { Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } ) -- -- @param #DETECTION_BASE self - -- @param #list FilterCategories The Categories entries + -- @param #list FilterCategories The Categories entries -- @return #DETECTION_BASE self function DETECTION_BASE:FilterCategories( FilterCategories ) self:F2() @@ -1165,7 +1165,7 @@ do -- DETECTION_BASE --- Returns if there are friendlies nearby the FAC units ... -- @param #DETECTION_BASE self -- @param DetectedItem - -- @param Dcs.DCSUnit#Unit.Category Category The category of the unit. + -- @param DCS#Unit.Category Category The category of the unit. -- @return #boolean true if there are friendlies nearby function DETECTION_BASE:IsFriendliesNearBy( DetectedItem, Category ) --self:F( { "FriendliesNearBy Test", DetectedItem.FriendliesNearBy } ) @@ -1175,7 +1175,7 @@ do -- DETECTION_BASE --- Returns friendly units nearby the FAC units ... -- @param #DETECTION_BASE self -- @param DetectedItem - -- @param Dcs.DCSUnit#Unit.Category Category The category of the unit. + -- @param DCS#Unit.Category Category The category of the unit. -- @return #map<#string,Wrapper.Unit#UNIT> The map of Friendly UNITs. function DETECTION_BASE:GetFriendliesNearBy( DetectedItem, Category ) @@ -1248,7 +1248,7 @@ do -- DETECTION_BASE } - --- @param Dcs.DCSWrapper.Unit#Unit FoundDCSUnit + --- @param DCS#Unit FoundDCSUnit -- @param Wrapper.Group#GROUP ReportGroup -- @param Core.Set#SET_GROUP ReportSetGroup local FindNearByFriendlies = function( FoundDCSUnit, ReportGroupData ) @@ -1809,7 +1809,7 @@ do -- DETECTION_UNITS -- Beware that when the amount of units detected is large, the DetectedItems list will be large also. -- -- @type DETECTION_UNITS - -- @field Dcs.DCSTypes#Distance DetectionRange The range till which targets are detected. + -- @field DCS#Distance DetectionRange The range till which targets are detected. -- @extends #DETECTION_BASE DETECTION_UNITS = { ClassName = "DETECTION_UNITS", @@ -2294,7 +2294,7 @@ do -- DETECTION_AREAS -- the detected zones when a new detection has taken place. -- -- @type DETECTION_AREAS - -- @field Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. + -- @field DCS#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. -- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Wrapper.Unit}s, @{Zone}s, the center @{Wrapper.Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. -- @extends #DETECTION_BASE DETECTION_AREAS = { @@ -2306,7 +2306,7 @@ do -- DETECTION_AREAS --- DETECTION_AREAS constructor. -- @param #DETECTION_AREAS self -- @param Core.Set#SET_GROUP DetectionSetGroup The @{Set} of GROUPs in the Forward Air Controller role. - -- @param Dcs.DCSTypes#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. + -- @param DCS#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. -- @return #DETECTION_AREAS function DETECTION_AREAS:New( DetectionSetGroup, DetectionZoneRange ) diff --git a/Moose Development/Moose/Functional/Escort.lua b/Moose Development/Moose/Functional/Escort.lua index b2c27bdc9..c76742736 100644 --- a/Moose Development/Moose/Functional/Escort.lua +++ b/Moose Development/Moose/Functional/Escort.lua @@ -126,8 +126,8 @@ -- @field Core.Scheduler#SCHEDULER FollowScheduler The instance of the SCHEDULER class. -- @field #number FollowDistance The current follow distance. -- @field #boolean ReportTargets If true, nearby targets are reported. --- @Field Dcs.DCSTypes#AI.Option.Air.val.ROE OptionROE Which ROE is set to the EscortGroup. --- @field Dcs.DCSTypes#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the EscortGroup. +-- @Field DCS#AI.Option.Air.val.ROE OptionROE Which ROE is set to the EscortGroup. +-- @field DCS#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the EscortGroup. -- @field FunctionalMENU_GROUPDETECTION_BASE Detection ESCORT = { ClassName = "ESCORT", @@ -294,7 +294,7 @@ end --- Defines a menu slot to let the escort Join and Follow you at a certain distance. -- This menu will appear under **Navigation**. -- @param #ESCORT self --- @param Dcs.DCSTypes#Distance Distance The distance in meters that the escort needs to follow the client. +-- @param DCS#Distance Distance The distance in meters that the escort needs to follow the client. -- @return #ESCORT function ESCORT:MenuFollowAt( Distance ) self:F(Distance) @@ -319,8 +319,8 @@ end --- Defines a menu slot to let the escort hold at their current position and stay low with a specified height during a specified time in seconds. -- This menu will appear under **Hold position**. -- @param #ESCORT self --- @param Dcs.DCSTypes#Distance Height Optional parameter that sets the height in meters to let the escort orbit at the current location. The default value is 30 meters. --- @param Dcs.DCSTypes#Time Seconds Optional parameter that lets the escort orbit at the current position for a specified time. (not implemented yet). The default value is 0 seconds, meaning, that the escort will orbit forever until a sequent command is given. +-- @param DCS#Distance Height Optional parameter that sets the height in meters to let the escort orbit at the current location. The default value is 30 meters. +-- @param DCS#Time Seconds Optional parameter that lets the escort orbit at the current position for a specified time. (not implemented yet). The default value is 0 seconds, meaning, that the escort will orbit forever until a sequent command is given. -- @param #string MenuTextFormat Optional parameter that shows the menu option text. The text string is formatted, and should contain two %d tokens in the string. The first for the Height, the second for the Time (if given). If no text is given, the default text will be displayed. -- @return #ESCORT -- TODO: Implement Seconds parameter. Challenge is to first develop the "continue from last activity" function. @@ -380,8 +380,8 @@ end --- Defines a menu slot to let the escort hold at the client position and stay low with a specified height during a specified time in seconds. -- This menu will appear under **Navigation**. -- @param #ESCORT self --- @param Dcs.DCSTypes#Distance Height Optional parameter that sets the height in meters to let the escort orbit at the current location. The default value is 30 meters. --- @param Dcs.DCSTypes#Time Seconds Optional parameter that lets the escort orbit at the current position for a specified time. (not implemented yet). The default value is 0 seconds, meaning, that the escort will orbit forever until a sequent command is given. +-- @param DCS#Distance Height Optional parameter that sets the height in meters to let the escort orbit at the current location. The default value is 30 meters. +-- @param DCS#Time Seconds Optional parameter that lets the escort orbit at the current position for a specified time. (not implemented yet). The default value is 0 seconds, meaning, that the escort will orbit forever until a sequent command is given. -- @param #string MenuTextFormat Optional parameter that shows the menu option text. The text string is formatted, and should contain one or two %d tokens in the string. The first for the Height, the second for the Time (if given). If no text is given, the default text will be displayed. -- @return #ESCORT -- TODO: Implement Seconds parameter. Challenge is to first develop the "continue from last activity" function. @@ -441,8 +441,8 @@ end --- Defines a menu slot to let the escort scan for targets at a certain height for a certain time in seconds. -- This menu will appear under **Scan targets**. -- @param #ESCORT self --- @param Dcs.DCSTypes#Distance Height Optional parameter that sets the height in meters to let the escort orbit at the current location. The default value is 30 meters. --- @param Dcs.DCSTypes#Time Seconds Optional parameter that lets the escort orbit at the current position for a specified time. (not implemented yet). The default value is 0 seconds, meaning, that the escort will orbit forever until a sequent command is given. +-- @param DCS#Distance Height Optional parameter that sets the height in meters to let the escort orbit at the current location. The default value is 30 meters. +-- @param DCS#Time Seconds Optional parameter that lets the escort orbit at the current position for a specified time. (not implemented yet). The default value is 0 seconds, meaning, that the escort will orbit forever until a sequent command is given. -- @param #string MenuTextFormat Optional parameter that shows the menu option text. The text string is formatted, and should contain one or two %d tokens in the string. The first for the Height, the second for the Time (if given). If no text is given, the default text will be displayed. -- @return #ESCORT function ESCORT:MenuScanForTargets( Height, Seconds, MenuTextFormat ) @@ -566,7 +566,7 @@ end -- This menu will appear under **Report targets**. -- Note that if a report targets menu is not specified, no targets will be detected by the escort, and the attack and assisted attack menus will not be displayed. -- @param #ESCORT self --- @param Dcs.DCSTypes#Time Seconds Optional parameter that lets the escort report their current detected targets after specified time interval in seconds. The default time is 30 seconds. +-- @param DCS#Time Seconds Optional parameter that lets the escort report their current detected targets after specified time interval in seconds. The default time is 30 seconds. -- @return #ESCORT function ESCORT:MenuReportTargets( Seconds ) self:F( { Seconds } ) @@ -736,7 +736,7 @@ end -- @param Functional.Escort#ESCORT self -- @param Wrapper.Group#GROUP EscortGroup -- @param Wrapper.Client#CLIENT EscortClient --- @param Dcs.DCSTypes#Distance Distance +-- @param DCS#Distance Distance function ESCORT:JoinUpAndFollow( EscortGroup, EscortClient, Distance ) self:F( { EscortGroup, EscortClient, Distance } ) diff --git a/Moose Development/Moose/Functional/Movement.lua b/Moose Development/Moose/Functional/Movement.lua index 8aabf0130..f01dad139 100644 --- a/Moose Development/Moose/Functional/Movement.lua +++ b/Moose Development/Moose/Functional/Movement.lua @@ -8,6 +8,7 @@ -- the main DCS execution core of your CPU is fully utilized. So, this class will limit the amount of simultaneous moving GROUND units -- on defined intervals (currently every minute). -- @module Functional.Movement +-- @image MOOSE.JPG --- the MOVEMENT class -- @type MOVEMENT diff --git a/Moose Development/Moose/Functional/Protect.lua b/Moose Development/Moose/Functional/Protect.lua deleted file mode 100644 index 00645edbe..000000000 --- a/Moose Development/Moose/Functional/Protect.lua +++ /dev/null @@ -1,305 +0,0 @@ ---- **Functional** -- The PROTECT class handles the protection of objects, which can be zones, units, scenery. --- --- === --- --- ### Author: **FlightControl** --- ### Contributions: **MillerTime** --- --- === --- --- @module Functional.Protect - ---- @type PROTECT.__ Methods which are not intended for mission designers, but which are used interally by the moose designer :-) --- @extends Core.Fsm#FSM - ---- @type PROTECT --- @extends #PROTECT.__ - ---- # PROTECT, extends @{Core.Base#BASE} --- --- @field #PROTECT -PROTECT = { - ClassName = "PROTECT", -} - ---- Get the ProtectZone --- @param #PROTECT self --- @return Core.Zone#ZONE_BASE -function PROTECT:GetProtectZone() - return self.ProtectZone -end - - ---- Get the name of the ProtectZone --- @param #PROTECT self --- @return #string -function PROTECT:GetProtectZoneName() - return self.ProtectZone:GetName() -end - - ---- Set the owning coalition of the zone. --- @param #PROTECT self --- @param DCSCoalition.DCSCoalition#coalition Coalition -function PROTECT:SetCoalition( Coalition ) - self.Coalition = Coalition -end - - ---- Get the owning coalition of the zone. --- @param #PROTECT self --- @return DCSCoalition.DCSCoalition#coalition Coalition. -function PROTECT:GetCoalition() - return self.Coalition -end - - ---- Get the owning coalition name of the zone. --- @param #PROTECT self --- @return #string Coalition name. -function PROTECT:GetCoalitionName() - - if self.Coalition == coalition.side.BLUE then - return "Blue" - end - - if self.Coalition == coalition.side.RED then - return "Red" - end - - if self.Coalition == coalition.side.NEUTRAL then - return "Neutral" - end - - return "" -end - - -function PROTECT:IsGuarded() - - local IsGuarded = self.ProtectZone:IsAllInZoneOfCoalition( self.Coalition ) - self:F( { IsGuarded = IsGuarded } ) - return IsGuarded -end - -function PROTECT:IsCaptured() - - local IsCaptured = self.ProtectZone:IsAllInZoneOfOtherCoalition( self.Coalition ) - self:F( { IsCaptured = IsCaptured } ) - return IsCaptured -end - - -function PROTECT:IsAttacked() - - local IsAttacked = self.ProtectZone:IsSomeInZoneOfCoalition( self.Coalition ) - self:F( { IsAttacked = IsAttacked } ) - return IsAttacked -end - - -function PROTECT:IsEmpty() - - local IsEmpty = self.ProtectZone:IsNoneInZone() - self:F( { IsEmpty = IsEmpty } ) - return IsEmpty -end - - ---- Check if the units are still alive. --- @param #PROTECT self -function PROTECT:AreProtectUnitsAlive() - - local IsAlive = false - - local UnitSet = self.ProtectUnitSet - UnitSet:Flush( self ) - local UnitList = UnitSet:GetSet() - - for UnitID, ProtectUnit in pairs( UnitList ) do - local IsUnitAlive = ProtectUnit:IsAlive() - if IsUnitAlive == true then - IsAlive = true - break - end - end - - return IsAlive -end - ---- Check if the statics are still alive. --- @param #PROTECT self -function PROTECT:AreProtectStaticsAlive() - - local IsAlive = false - - local StaticSet = self.ProtectStaticSet - StaticSet:Flush( self ) - local StaticList = StaticSet:GetSet() - - for UnitID, ProtectStatic in pairs( StaticList ) do - local IsStaticAlive = ProtectStatic:IsAlive() - if IsStaticAlive == true then - IsAlive = true - break - end - end - - return IsAlive -end - - ---- Check if there is a capture unit in the zone. --- @param #PROTECT self -function PROTECT:IsCaptureUnitInZone() - - local CaptureUnitSet = self.CaptureUnitSet - CaptureUnitSet:Flush( self ) - - local IsInZone = self.CaptureUnitSet:IsPartiallyInZone( self.ProtectZone ) - - self:F({IsInZone = IsInZone}) - - return IsInZone -end - ---- Smoke. --- @param #PROTECT self --- @param #SMOKECOLOR.Color SmokeColor -function PROTECT:Smoke( SmokeColor ) - - self.SmokeColor = SmokeColor -end - - ---- Flare. --- @param #PROTECT self --- @param #SMOKECOLOR.Color FlareColor -function PROTECT:Flare( FlareColor ) - self.ProtectZone:FlareZone( FlareColor, math.random( 1, 360 ) ) -end - - ---- Mark. --- @param #PROTECT self -function PROTECT:Mark() - - local Coord = self.ProtectZone:GetCoordinate() - local ZoneName = self:GetProtectZoneName() - local State = self:GetState() - - if self.MarkRed and self.MarkBlue then - self:F( { MarkRed = self.MarkRed, MarkBlue = self.MarkBlue } ) - Coord:RemoveMark( self.MarkRed ) - Coord:RemoveMark( self.MarkBlue ) - end - - if self.Coalition == coalition.side.BLUE then - self.MarkBlue = Coord:MarkToCoalitionBlue( "Guard Zone: " .. ZoneName .. "\nStatus: " .. State ) - self.MarkRed = Coord:MarkToCoalitionRed( "Capture Zone: " .. ZoneName .. "\nStatus: " .. State ) - else - self.MarkRed = Coord:MarkToCoalitionRed( "Guard Zone: " .. ZoneName .. "\nStatus: " .. State ) - self.MarkBlue = Coord:MarkToCoalitionBlue( "Capture Zone: " .. ZoneName .. "\nStatus: " .. State ) - end -end - - ---- Bound. --- @param #PROTECT self -function PROTECT:onafterStart() - - self:ScheduleRepeat( 5, 15, 0.1, nil, self.StatusCoalition, self ) - self:ScheduleRepeat( 5, 15, 0.1, nil, self.StatusZone, self ) - self:ScheduleRepeat( 10, 15, 0, nil, self.StatusSmoke, self ) -end - ---- Bound. --- @param #PROTECT self -function PROTECT:onenterGuarded() - - - if self.Coalition == coalition.side.BLUE then - --elf.ProtectZone:BoundZone( 12, country.id.USA ) - else - --self.ProtectZone:BoundZone( 12, country.id.RUSSIA ) - end - - self:Mark() - -end - -function PROTECT:onenterCaptured() - - local NewCoalition = self.ProtectZone:GetCoalition() - self:F( { NewCoalition = NewCoalition } ) - self:SetCoalition( NewCoalition ) - - self:Mark() -end - - -function PROTECT:onenterEmpty() - - self:Mark() -end - - -function PROTECT:onenterAttacked() - - self:Mark() -end - - ---- Check status Coalition ownership. --- @param #PROTECT self -function PROTECT:StatusCoalition() - - self:F( { State = self:GetState() } ) - - self.ProtectZone:Scan() - - if self:IsGuarded() then - self:Guard() - else - if self:IsCaptured() then - self:Capture() - end - end -end - ---- Check status Zone. --- @param #PROTECT self -function PROTECT:StatusZone() - - self:F( { State = self:GetState() } ) - - self.ProtectZone:Scan() - - if self:IsAttacked() then - self:Attack() - else - if self:IsEmpty() then - self:Empty() - end - end -end - ---- Check status Smoke. --- @param #PROTECT self -function PROTECT:StatusSmoke() - - local CurrentTime = timer.getTime() - - if self.SmokeTime == nil or self.SmokeTime + 300 <= CurrentTime then - if self.SmokeColor then - self.ProtectZone:GetCoordinate():Smoke( self.SmokeColor ) - --self.SmokeColor = nil - self.SmokeTime = CurrentTime - end - end -end - - - - - diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 9e192a795..610c67b17 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -55,7 +55,7 @@ -- -- === -- @module Functional.Rat --- @module RAT.JPG +-- @image RAT.JPG ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- RAT class @@ -1663,7 +1663,7 @@ end --- Initialize basic parameters of the aircraft based on its (template) group in the mission editor. -- @param #RAT self --- @param Dcs.DCSWrapper.Group#Group DCSgroup Group of the aircraft in the mission editor. +-- @param DCS#Group DCSgroup Group of the aircraft in the mission editor. function RAT:_InitAircraft(DCSgroup) self:F2(DCSgroup) @@ -3762,11 +3762,11 @@ end function RAT:_Destroy(group) self:F2(group) - local DCSGroup = group:GetDCSObject() -- Dcs.DCSGroup#Group + local DCSGroup = group:GetDCSObject() -- DCS#Group if DCSGroup and DCSGroup:isExist() then - --local DCSUnit = DCSGroup:getUnit(1) -- Dcs.DCSUnit#Unit + --local DCSUnit = DCSGroup:getUnit(1) -- DCS#Unit --if DCSUnit then -- self:_CreateEventDead(timer.getTime(), DCSUnit) --end @@ -3797,8 +3797,8 @@ end --- Create a Dead event. -- @param #RAT self --- @param Dcs.DCSTypes#Time EventTime The time stamp of the event. --- @param Dcs.DCSWrapper.Object#Object Initiator The initiating object of the event. +-- @param DCS#Time EventTime The time stamp of the event. +-- @param DCS#Object Initiator The initiating object of the event. function RAT:_CreateEventDead(EventTime, Initiator) self:F( { EventTime, Initiator } ) @@ -4030,11 +4030,11 @@ end --- Orbit at a specified position at a specified alititude with a specified speed. -- @param #RAT self --- @param Dcs.DCSTypes#Vec2 P1 The point to hold the position. +-- @param DCS#Vec2 P1 The point to hold the position. -- @param #number Altitude The altitude ASL at which to hold the position. -- @param #number Speed The speed flying when holding the position in m/s. -- @param #number Duration Duration of holding pattern in seconds. --- @return Dcs.DCSTasking.Task#Task DCSTask +-- @return DCS#Task DCSTask function RAT:_TaskHolding(P1, Altitude, Speed, Duration) --local LandHeight = land.getHeight(P1) diff --git a/Moose Development/Moose/Functional/ZoneGoal.lua b/Moose Development/Moose/Functional/ZoneGoal.lua index 60c2f6d25..fb610e7d9 100644 --- a/Moose Development/Moose/Functional/ZoneGoal.lua +++ b/Moose Development/Moose/Functional/ZoneGoal.lua @@ -12,6 +12,7 @@ -- === -- -- @module Functional.ZoneGoal +-- @image MOOSE.JPG do -- Zone diff --git a/Moose Development/Moose/Functional/ZoneGoalCargo.lua b/Moose Development/Moose/Functional/ZoneGoalCargo.lua index 5fc27acc6..48b492aaa 100644 --- a/Moose Development/Moose/Functional/ZoneGoalCargo.lua +++ b/Moose Development/Moose/Functional/ZoneGoalCargo.lua @@ -12,6 +12,7 @@ -- === -- -- @module Functional.ZoneGoalCargo +-- @image MOOSE.JPG do -- ZoneGoal diff --git a/Moose Development/Moose/Functional/ZoneGoalCoalition.lua b/Moose Development/Moose/Functional/ZoneGoalCoalition.lua index b0b737518..cb66ad3bc 100644 --- a/Moose Development/Moose/Functional/ZoneGoalCoalition.lua +++ b/Moose Development/Moose/Functional/ZoneGoalCoalition.lua @@ -12,6 +12,7 @@ -- === -- -- @module Functional.ZoneGoalCoalition +-- @image MOOSE.JPG do -- ZoneGoal diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index 74d50dfb6..131e8e4b9 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -13,13 +13,10 @@ -- @image Task_Command_Center.JPG - - - --- The COMMANDCENTER class -- @type COMMANDCENTER -- @field Wrapper.Group#GROUP HQ --- @field Dcs.DCSCoalitionWrapper.Object#coalition CommandCenterCoalition +-- @field DCS#coalition CommandCenterCoalition -- @list Missions -- @extends Core.Base#BASE diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index 9dd3da5bb..962770e14 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -9,6 +9,7 @@ -- === -- -- @module Tasking.Task +-- @image MOOSE.JPG --- @type TASK -- @field Core.Scheduler#SCHEDULER TaskScheduler diff --git a/Moose Development/Moose/Tasking/TaskInfo.lua b/Moose Development/Moose/Tasking/TaskInfo.lua index 349d090c1..2822615d5 100644 --- a/Moose Development/Moose/Tasking/TaskInfo.lua +++ b/Moose Development/Moose/Tasking/TaskInfo.lua @@ -9,6 +9,7 @@ -- === -- -- @module Tasking.TaskInfo +-- @image MOOSE.JPG --- @type TASKINFO -- @extends Core.Base#BASE diff --git a/Moose Development/Moose/Tasking/TaskZoneCapture.lua b/Moose Development/Moose/Tasking/TaskZoneCapture.lua index ad563c1d5..e32ea58bf 100644 --- a/Moose Development/Moose/Tasking/TaskZoneCapture.lua +++ b/Moose Development/Moose/Tasking/TaskZoneCapture.lua @@ -9,6 +9,7 @@ -- === -- -- @module Tasking.TaskZoneCapture +-- @image MOOSE.JPG do -- TASK_ZONE_GOAL diff --git a/Moose Development/Moose/Tasking/Task_A2A.lua b/Moose Development/Moose/Tasking/Task_A2A.lua index 122356bd5..e3d3ed793 100644 --- a/Moose Development/Moose/Tasking/Task_A2A.lua +++ b/Moose Development/Moose/Tasking/Task_A2A.lua @@ -9,6 +9,7 @@ -- === -- -- @module Tasking.Tasking.Task_A2A +-- @image MOOSE.JPG do -- TASK_A2A diff --git a/Moose Development/Moose/Tasking/Task_A2G.lua b/Moose Development/Moose/Tasking/Task_A2G.lua index 1c6fa1276..319a30fb2 100644 --- a/Moose Development/Moose/Tasking/Task_A2G.lua +++ b/Moose Development/Moose/Tasking/Task_A2G.lua @@ -9,6 +9,7 @@ -- === -- -- @module Tasking.Task_A2G +-- @image MOOSE.JPG do -- TASK_A2G diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 7cf6bd9c1..200877922 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -24,6 +24,7 @@ -- === -- -- @module Tasking.Task_Cargo +-- @image MOOSE.JPG do -- TASK_CARGO diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index 16dc70cf0..1fd738087 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -9,6 +9,7 @@ -- === -- -- @module Tasking.Task_Cargo_Dispatcher +-- @image MOOSE.JPG do -- TASK_CARGO_DISPATCHER diff --git a/Moose Development/Moose/Tasking/Task_Manager.lua b/Moose Development/Moose/Tasking/Task_Manager.lua index c82fa505e..dc7f31c62 100644 --- a/Moose Development/Moose/Tasking/Task_Manager.lua +++ b/Moose Development/Moose/Tasking/Task_Manager.lua @@ -30,6 +30,7 @@ -- ### Author: FlightControl - Framework Design & Programming -- -- @module Tasking.Task_Manager +-- @image MOOSE.JPG do -- TASK_MANAGER diff --git a/Moose Development/Moose/Utilities/Routines.lua b/Moose Development/Moose/Utilities/Routines.lua index 508a35ed5..285f270d7 100644 --- a/Moose Development/Moose/Utilities/Routines.lua +++ b/Moose Development/Moose/Utilities/Routines.lua @@ -1,6 +1,6 @@ --- Various routines -- @module routines --- @author Flightcontrol +-- @image MOOSE.JPG env.setErrorMessageBoxEnabled(false) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 7ffb6dd44..5dab11d52 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -10,6 +10,7 @@ -- * FlightControl : Rework to OO framework -- -- @module Utils +-- @image MOOSE.JPG --- @type SMOKECOLOR diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 50c331293..0a062eb1e 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -238,7 +238,7 @@ end --- Finds a AIRBASE from the _DATABASE using a DCSAirbase object. -- @param #AIRBASE self --- @param Dcs.DCSWrapper.Airbase#Airbase DCSAirbase An existing DCS Airbase object reference. +-- @param DCS#Airbase DCSAirbase An existing DCS Airbase object reference. -- @return Wrapper.Airbase#AIRBASE self function AIRBASE:Find( DCSAirbase ) diff --git a/Moose Development/Moose/Wrapper/Client.lua b/Moose Development/Moose/Wrapper/Client.lua index 0c1eefdaf..09b082339 100644 --- a/Moose Development/Moose/Wrapper/Client.lua +++ b/Moose Development/Moose/Wrapper/Client.lua @@ -273,7 +273,7 @@ end --- Return the DCSGroup of a Client. -- This function is modified to deal with a couple of bugs in DCS 1.5.3 -- @param #CLIENT self --- @return Dcs.DCSWrapper.Group#Group +-- @return DCS#Group The group of the Client. function CLIENT:GetDCSGroup() self:F3() @@ -347,10 +347,10 @@ function CLIENT:GetDCSGroup() end --- TODO: Check Dcs.DCSTypes#Group.ID +-- TODO: Check DCS#Group.ID --- Get the group ID of the client. -- @param #CLIENT self --- @return Dcs.DCSTypes#Group.ID +-- @return DCS#Group.ID function CLIENT:GetClientGroupID() local ClientGroup = self:GetDCSGroup() @@ -389,7 +389,7 @@ end --- Returns the DCSUnit of the CLIENT. -- @param #CLIENT self --- @return Dcs.DCSTypes#Unit +-- @return DCS#Unit function CLIENT:GetClientGroupDCSUnit() self:F2() diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 5e7a15ef9..1665f1b44 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -14,7 +14,7 @@ --- @type CONTROLLABLE -- @extends Wrapper.Positionable#POSITIONABLE --- @field Dcs.DCSWrapper.Controllable#Controllable DCSControllable The DCS controllable class. +-- @field DCS#Controllable DCSControllable The DCS controllable class. -- @field #string ControllableName The name of the controllable. @@ -171,7 +171,7 @@ CONTROLLABLE = { --- Create a new CONTROLLABLE from a DCSControllable -- @param #CONTROLLABLE self --- @param Dcs.DCSWrapper.Controllable#Controllable ControllableName The DCS Controllable name +-- @param #string ControllableName The DCS Controllable name -- @return #CONTROLLABLE self function CONTROLLABLE:New( ControllableName ) local self = BASE:Inherit( self, POSITIONABLE:New( ControllableName ) ) -- #CONTROLLABLE @@ -186,7 +186,7 @@ end --- Get the controller for the CONTROLLABLE. -- @param #CONTROLLABLE self --- @return Dcs.DCSController#Controller +-- @return DCS#Controller function CONTROLLABLE:_GetController() local DCSControllable = self:GetDCSObject() @@ -396,13 +396,13 @@ end --- Return a condition section for a controlled task. -- @param #CONTROLLABLE self --- @param Dcs.DCSTime#Time time +-- @param DCS#Time time -- @param #string userFlag -- @param #boolean userFlagValue -- @param #string condition --- @param Dcs.DCSTime#Time duration +-- @param DCS#Time duration -- @param #number lastWayPoint --- return Dcs.DCSTasking.Task#Task +-- return DCS#Task function CONTROLLABLE:TaskCondition( time, userFlag, userFlagValue, condition, duration, lastWayPoint ) self:F2( { time, userFlag, userFlagValue, condition, duration, lastWayPoint } ) @@ -420,9 +420,9 @@ end --- Return a Controlled Task taking a Task and a TaskCondition. -- @param #CONTROLLABLE self --- @param Dcs.DCSTasking.Task#Task DCSTask +-- @param DCS#Task DCSTask -- @param #DCSStopCondition DCSStopCondition --- @return Dcs.DCSTasking.Task#Task +-- @return DCS#Task function CONTROLLABLE:TaskControlled( DCSTask, DCSStopCondition ) self:F2( { DCSTask, DCSStopCondition } ) @@ -442,8 +442,8 @@ end --- Return a Combo Task taking an array of Tasks. -- @param #CONTROLLABLE self --- @param Dcs.DCSTasking.Task#TaskArray DCSTasks Array of @{DCSTasking.Task#Task} --- @return Dcs.DCSTasking.Task#Task +-- @param DCS#TaskArray DCSTasks Array of @{DCSTasking.Task#Task} +-- @return DCS#Task function CONTROLLABLE:TaskCombo( DCSTasks ) self:F2( { DCSTasks } ) @@ -466,8 +466,8 @@ end --- Return a WrappedAction Task taking a Command. -- @param #CONTROLLABLE self --- @param Dcs.DCSCommand#Command DCSCommand --- @return Dcs.DCSTasking.Task#Task +-- @param DCS#Command DCSCommand +-- @return DCS#Task function CONTROLLABLE:TaskWrappedAction( DCSCommand, Index ) self:F2( { DCSCommand } ) @@ -490,8 +490,8 @@ end --- Set a Task at a Waypoint using a Route list. -- @param #CONTROLLABLE self -- @param #table Waypoint The Waypoint! --- @param Dcs.DCSTasking.Task#Task Task The Task structure to be executed! --- @return Dcs.DCSTasking.Task#Task +-- @param DCS#Task Task The Task structure to be executed! +-- @return DCS#Task function CONTROLLABLE:SetTaskWaypoint( Waypoint, Task ) Waypoint.task = self:TaskCombo( { Task } ) @@ -505,7 +505,7 @@ end --- Executes a command action -- @param #CONTROLLABLE self --- @param Dcs.DCSCommand#Command DCSCommand +-- @param DCS#Command DCSCommand -- @return #CONTROLLABLE self function CONTROLLABLE:SetCommand( DCSCommand ) self:F2( DCSCommand ) @@ -525,7 +525,7 @@ end -- @param #CONTROLLABLE self -- @param #number FromWayPoint -- @param #number ToWayPoint --- @return Dcs.DCSTasking.Task#Task +-- @return DCS#Task -- @usage -- --- This test demonstrates the use(s) of the SwitchWayPoint method of the GROUP class. -- HeliGroup = GROUP:FindByName( "Helicopter" ) @@ -564,7 +564,7 @@ end -- -- @param #CONTROLLABLE self -- @param #boolean StopRoute true if the ground unit needs to stop, false if it needs to continue to move. --- @return Dcs.DCSTasking.Task#Task +-- @return DCS#Task function CONTROLLABLE:CommandStopRoute( StopRoute ) self:F2( { StopRoute } ) @@ -587,12 +587,12 @@ end -- @param #CONTROLLABLE self -- @param Wrapper.Controllable#CONTROLLABLE AttackGroup The Controllable to be attacked. -- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. +-- @param DCS#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param Dcs.DCSTypes#Distance Altitude (optional) Desired attack start altitude. Controllable/aircraft will make its attacks from the altitude. If the altitude is too low or too high to use weapon aircraft/controllable will choose closest altitude to the desired attack start altitude. If the desired altitude is defined controllable/aircraft will not attack from safe altitude. +-- @param DCS#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. +-- @param DCS#Distance Altitude (optional) Desired attack start altitude. Controllable/aircraft will make its attacks from the altitude. If the altitude is too low or too high to use weapon aircraft/controllable will choose closest altitude to the desired attack start altitude. If the desired altitude is defined controllable/aircraft will not attack from safe altitude. -- @param #boolean AttackQtyLimit (optional) The flag determines how to interpret attackQty parameter. If the flag is true then attackQty is a limit on maximal attack quantity for "AttackGroup" and "AttackUnit" tasks. If the flag is false then attackQty is a desired attack quantity for "Bombing" and "BombingRunway" tasks. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. +-- @return DCS#Task The DCS task structure. function CONTROLLABLE:TaskAttackGroup( AttackGroup, WeaponType, WeaponExpend, AttackQty, Direction, Altitude, AttackQtyLimit ) self:F2( { self.ControllableName, AttackGroup, WeaponType, WeaponExpend, AttackQty, Direction, Altitude, AttackQtyLimit } ) @@ -644,13 +644,13 @@ end -- @param #CONTROLLABLE self -- @param Wrapper.Unit#UNIT AttackUnit The UNIT. -- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. +-- @param DCS#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. +-- @param DCS#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. -- @param #number Altitude (optional) The altitude from where to attack. -- @param #boolean Visible (optional) not a clue. -- @param #number WeaponType (optional) The WeaponType. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. +-- @return DCS#Task The DCS task structure. function CONTROLLABLE:TaskAttackUnit( AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, WeaponType ) self:F2( { self.ControllableName, AttackUnit, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, WeaponType } ) @@ -680,14 +680,14 @@ end --- (AIR) Delivering weapon at the point on the ground. -- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Vec2 2D-coordinates of the point to deliver weapon at. +-- @param DCS#Vec2 Vec2 2D-coordinates of the point to deliver weapon at. -- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. +-- @param DCS#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. +-- @param DCS#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. -- @param #number Altitude (optional) The altitude from where to attack. -- @param #number WeaponType (optional) The WeaponType. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. +-- @return DCS#Task The DCS task structure. function CONTROLLABLE:TaskBombing( Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType ) self:F2( { self.ControllableName, Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType } ) @@ -714,14 +714,14 @@ end --- (AIR) Attacking the map object (building, structure, e.t.c). -- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Vec2 2D-coordinates of the point to deliver weapon at. +-- @param DCS#Vec2 Vec2 2D-coordinates of the point to deliver weapon at. -- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. +-- @param DCS#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. +-- @param DCS#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. -- @param #number Altitude (optional) The altitude from where to attack. -- @param #number WeaponType (optional) The WeaponType. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. +-- @return DCS#Task The DCS task structure. function CONTROLLABLE:TaskAttackMapObject( Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType ) self:F2( { self.ControllableName, Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType } ) @@ -749,7 +749,7 @@ end --- (AIR) Orbit at a specified position at a specified alititude during a specified duration with a specified speed. -- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Point The point to hold the position. +-- @param DCS#Vec2 Point The point to hold the position. -- @param #number Altitude The altitude [m] to hold the position. -- @param #number Speed The speed [m/s] flying when holding the position. -- @return #CONTROLLABLE self @@ -832,11 +832,11 @@ end -- @param #CONTROLLABLE self -- @param Wrapper.Airbase#AIRBASE Airbase Airbase to attack. -- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. +-- @param DCS#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. +-- @param DCS#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. -- @param #boolean ControllableAttack (optional) Flag indicates that the target must be engaged by all aircrafts of the controllable. Has effect only if the task is assigned to a controllable, not to a single aircraft. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. +-- @return DCS#Task The DCS task structure. function CONTROLLABLE:TaskBombingRunway( Airbase, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack ) self:F2( { self.ControllableName, Airbase, WeaponType, WeaponExpend, AttackQty, Direction, ControllableAttack } ) @@ -871,7 +871,7 @@ end --- (AIR) Refueling from the nearest tanker. No parameters. -- @param #CONTROLLABLE self --- @return Dcs.DCSTasking.Task#Task The DCS task structure. +-- @return DCS#Task The DCS task structure. function CONTROLLABLE:TaskRefueling() self:F2( { self.ControllableName } ) @@ -893,7 +893,7 @@ end --- (AIR HELICOPTER) Landing at the ground. For helicopters only. -- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Point The point where to land. +-- @param DCS#Vec2 Point The point where to land. -- @param #number Duration The duration in seconds to stay on the ground. -- @return #CONTROLLABLE self function CONTROLLABLE:TaskLandAtVec2( Point, Duration ) @@ -958,9 +958,9 @@ end -- If another controllable is on land the unit / controllable will orbit around. -- @param #CONTROLLABLE self -- @param Wrapper.Controllable#CONTROLLABLE FollowControllable The controllable to be followed. --- @param Dcs.DCSTypes#Vec3 Vec3 Position of the unit / lead unit of the controllable relative lead unit of another controllable in frame reference oriented by course of lead unit of another controllable. If another controllable is on land the unit / controllable will orbit around. +-- @param DCS#Vec3 Vec3 Position of the unit / lead unit of the controllable relative lead unit of another controllable in frame reference oriented by course of lead unit of another controllable. If another controllable is on land the unit / controllable will orbit around. -- @param #number LastWaypointIndex Detach waypoint of another controllable. Once reached the unit / controllable Follow task is finished. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. +-- @return DCS#Task The DCS task structure. function CONTROLLABLE:TaskFollow( FollowControllable, Vec3, LastWaypointIndex ) self:F2( { self.ControllableName, FollowControllable, Vec3, LastWaypointIndex } ) @@ -1000,11 +1000,11 @@ end -- The unit / controllable will also protect that controllable from threats of specified types. -- @param #CONTROLLABLE self -- @param Wrapper.Controllable#CONTROLLABLE EscortControllable The controllable to be escorted. --- @param Dcs.DCSTypes#Vec3 Vec3 Position of the unit / lead unit of the controllable relative lead unit of another controllable in frame reference oriented by course of lead unit of another controllable. If another controllable is on land the unit / controllable will orbit around. +-- @param DCS#Vec3 Vec3 Position of the unit / lead unit of the controllable relative lead unit of another controllable in frame reference oriented by course of lead unit of another controllable. If another controllable is on land the unit / controllable will orbit around. -- @param #number LastWaypointIndex Detach waypoint of another controllable. Once reached the unit / controllable Follow task is finished. -- @param #number EngagementDistanceMax Maximal distance from escorted controllable to threat. If the threat is already engaged by escort escort will disengage if the distance becomes greater than 1.5 * engagementDistMax. --- @param Dcs.DCSTypes#AttributeNameArray TargetTypes Array of AttributeName that is contains threat categories allowed to engage. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. +-- @param DCS#AttributeNameArray TargetTypes Array of AttributeName that is contains threat categories allowed to engage. +-- @return DCS#Task The DCS task structure. function CONTROLLABLE:TaskEscort( FollowControllable, Vec3, LastWaypointIndex, EngagementDistance, TargetTypes ) self:F2( { self.ControllableName, FollowControllable, Vec3, LastWaypointIndex, EngagementDistance, TargetTypes } ) @@ -1046,11 +1046,11 @@ end --- (GROUND) Fire at a VEC2 point until ammunition is finished. -- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Vec2 The point to fire at. --- @param Dcs.DCSTypes#Distance Radius The radius of the zone to deploy the fire at. +-- @param DCS#Vec2 Vec2 The point to fire at. +-- @param DCS#Distance Radius The radius of the zone to deploy the fire at. -- @param #number AmmoCount (optional) Quantity of ammunition to expand (omit to fire until ammunition is depleted). -- @param #number WeaponType (optional) Enum for weapon type ID. This value is only required if you want the group firing to use a specific weapon, for instance using the task on a ship to force it to fire guided missiles at targets within cannon range. See http://wiki.hoggit.us/view/DCS_enum_weapon_flag --- @return Dcs.DCSTasking.Task#Task The DCS task structure. +-- @return DCS#Task The DCS task structure. function CONTROLLABLE:TaskFireAtPoint( Vec2, Radius, AmmoCount, WeaponType ) self:F2( { self.ControllableName, Vec2, Radius, AmmoCount, WeaponType } ) @@ -1089,7 +1089,7 @@ end --- (GROUND) Hold ground controllable from moving. -- @param #CONTROLLABLE self --- @return Dcs.DCSTasking.Task#Task The DCS task structure. +-- @return DCS#Task The DCS task structure. function CONTROLLABLE:TaskHold() self:F2( { self.ControllableName } ) @@ -1118,9 +1118,9 @@ end -- @param #CONTROLLABLE self -- @param Wrapper.Controllable#CONTROLLABLE AttackGroup Target CONTROLLABLE. -- @param #number WeaponType Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. --- @param Dcs.DCSTypes#AI.Task.Designation Designation (optional) Designation type. +-- @param DCS#AI.Task.Designation Designation (optional) Designation type. -- @param #boolean Datalink (optional) Allows to use datalink to send the target information to attack aircraft. Enabled by default. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. +-- @return DCS#Task The DCS task structure. function CONTROLLABLE:TaskFAC_AttackGroup( AttackGroup, WeaponType, Designation, Datalink ) self:F2( { self.ControllableName, AttackGroup, WeaponType, Designation, Datalink } ) @@ -1152,10 +1152,10 @@ end --- (AIR) Engaging targets of defined types. -- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Distance Distance Maximal distance from the target to a route leg. If the target is on a greater distance it will be ignored. --- @param Dcs.DCSTypes#AttributeNameArray TargetTypes Array of target categories allowed to engage. +-- @param DCS#Distance Distance Maximal distance from the target to a route leg. If the target is on a greater distance it will be ignored. +-- @param DCS#AttributeNameArray TargetTypes Array of target categories allowed to engage. -- @param #number Priority All enroute tasks have the priority parameter. This is a number (less value - higher priority) that determines actions related to what task will be performed first. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. +-- @return DCS#Task The DCS task structure. function CONTROLLABLE:EnRouteTaskEngageTargets( Distance, TargetTypes, Priority ) self:F2( { self.ControllableName, Distance, TargetTypes, Priority } ) @@ -1185,11 +1185,11 @@ end --- (AIR) Engaging a targets of defined types at circle-shaped zone. -- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Vec2 2D-coordinates of the zone. --- @param Dcs.DCSTypes#Distance Radius Radius of the zone. --- @param Dcs.DCSTypes#AttributeNameArray TargetTypes Array of target categories allowed to engage. +-- @param DCS#Vec2 Vec2 2D-coordinates of the zone. +-- @param DCS#Distance Radius Radius of the zone. +-- @param DCS#AttributeNameArray TargetTypes Array of target categories allowed to engage. -- @param #number Priority 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. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. +-- @return DCS#Task The DCS task structure. function CONTROLLABLE:EnRouteTaskEngageTargetsInZone( Vec2, Radius, TargetTypes, Priority ) self:F2( { self.ControllableName, Vec2, Radius, TargetTypes, Priority } ) @@ -1223,12 +1223,12 @@ end -- @param Wrapper.Controllable#CONTROLLABLE AttackGroup The Controllable to be attacked. -- @param #number Priority 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. -- @param #number WeaponType (optional) Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. +-- @param DCS#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param Dcs.DCSTypes#Distance Altitude (optional) Desired attack start altitude. Controllable/aircraft will make its attacks from the altitude. If the altitude is too low or too high to use weapon aircraft/controllable will choose closest altitude to the desired attack start altitude. If the desired altitude is defined controllable/aircraft will not attack from safe altitude. +-- @param DCS#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. +-- @param DCS#Distance Altitude (optional) Desired attack start altitude. Controllable/aircraft will make its attacks from the altitude. If the altitude is too low or too high to use weapon aircraft/controllable will choose closest altitude to the desired attack start altitude. If the desired altitude is defined controllable/aircraft will not attack from safe altitude. -- @param #boolean AttackQtyLimit (optional) The flag determines how to interpret attackQty parameter. If the flag is true then attackQty is a limit on maximal attack quantity for "AttackGroup" and "AttackUnit" tasks. If the flag is false then attackQty is a desired attack quantity for "Bombing" and "BombingRunway" tasks. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. +-- @return DCS#Task The DCS task structure. function CONTROLLABLE:EnRouteTaskEngageGroup( AttackGroup, Priority, WeaponType, WeaponExpend, AttackQty, Direction, Altitude, AttackQtyLimit ) self:F2( { self.ControllableName, AttackGroup, Priority, WeaponType, WeaponExpend, AttackQty, Direction, Altitude, AttackQtyLimit } ) @@ -1284,13 +1284,13 @@ end -- @param Wrapper.Unit#UNIT EngageUnit The UNIT. -- @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. -- @param #boolean GroupAttack (optional) If true, all units in the group will attack the Unit when found. --- @param Dcs.DCSTypes#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. +-- @param DCS#AI.Task.WeaponExpend WeaponExpend (optional) Determines how much weapon will be released at each attack. If parameter is not defined the unit / controllable will choose expend on its own discretion. -- @param #number AttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. --- @param Dcs.DCSTypes#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. --- @param Dcs.DCSTypes#Distance Altitude (optional) Desired altitude to perform the unit engagement. +-- @param DCS#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. +-- @param DCS#Distance Altitude (optional) Desired altitude to perform the unit engagement. -- @param #boolean Visible (optional) Unit must be visible. -- @param #boolean ControllableAttack (optional) Flag indicates that the target must be engaged by all aircrafts of the controllable. Has effect only if the task is assigned to a controllable, not to a single aircraft. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. +-- @return DCS#Task The DCS task structure. function CONTROLLABLE:EnRouteTaskEngageUnit( EngageUnit, Priority, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, ControllableAttack ) self:F2( { self.ControllableName, EngageUnit, Priority, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, Visible, ControllableAttack } ) @@ -1334,7 +1334,7 @@ end --- (AIR) Aircraft will act as an AWACS for friendly units (will provide them with information about contacts). No parameters. -- @param #CONTROLLABLE self --- @return Dcs.DCSTasking.Task#Task The DCS task structure. +-- @return DCS#Task The DCS task structure. function CONTROLLABLE:EnRouteTaskAWACS( ) self:F2( { self.ControllableName } ) @@ -1357,7 +1357,7 @@ end --- (AIR) Aircraft will act as a tanker for friendly units. No parameters. -- @param #CONTROLLABLE self --- @return Dcs.DCSTasking.Task#Task The DCS task structure. +-- @return DCS#Task The DCS task structure. function CONTROLLABLE:EnRouteTaskTanker( ) self:F2( { self.ControllableName } ) @@ -1382,7 +1382,7 @@ end --- (GROUND) Ground unit (EW-radar) will act as an EWR for friendly units (will provide them with information about contacts). No parameters. -- @param #CONTROLLABLE self --- @return Dcs.DCSTasking.Task#Task The DCS task structure. +-- @return DCS#Task The DCS task structure. function CONTROLLABLE:EnRouteTaskEWR( ) self:F2( { self.ControllableName } ) @@ -1412,9 +1412,9 @@ end -- @param Wrapper.Controllable#CONTROLLABLE AttackGroup Target CONTROLLABLE. -- @param #number Priority 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. -- @param #number WeaponType Bitmask of weapon types those allowed to use. If parameter is not defined that means no limits on weapon usage. --- @param Dcs.DCSTypes#AI.Task.Designation Designation (optional) Designation type. +-- @param DCS#AI.Task.Designation Designation (optional) Designation type. -- @param #boolean Datalink (optional) Allows to use datalink to send the target information to attack aircraft. Enabled by default. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. +-- @return DCS#Task The DCS task structure. function CONTROLLABLE:EnRouteTaskFAC_EngageGroup( AttackGroup, Priority, WeaponType, Designation, Datalink ) self:F2( { self.ControllableName, AttackGroup, WeaponType, Priority, Designation, Datalink } ) @@ -1449,9 +1449,9 @@ end -- The killer is player-controlled allied CAS-aircraft that is in contact with the FAC. -- If the task is assigned to the controllable lead unit will be a FAC. -- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Distance Radius The maximal distance from the FAC to a target. +-- @param DCS#Distance Radius The maximal distance from the FAC to a target. -- @param #number Priority 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. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. +-- @return DCS#Task The DCS task structure. function CONTROLLABLE:EnRouteTaskFAC( Radius, Priority ) self:F2( { self.ControllableName, Radius, Priority } ) @@ -1480,10 +1480,10 @@ end --- (AIR) Move the controllable to a Vec2 Point, wait for a defined duration and embark a controllable. -- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Point The point where to wait. +-- @param DCS#Vec2 Point The point where to wait. -- @param #number Duration The duration in seconds to wait. -- @param #CONTROLLABLE EmbarkingControllable The controllable to be embarked. --- @return Dcs.DCSTasking.Task#Task The DCS task structure +-- @return DCS#Task The DCS task structure function CONTROLLABLE:TaskEmbarking( Point, Duration, EmbarkingControllable ) self:F2( { self.ControllableName, Point, Duration, EmbarkingControllable.DCSControllable } ) @@ -1507,13 +1507,13 @@ end --- Move to a defined Vec2 Point, and embark to a controllable when arrived within a defined Radius. -- @param #CONTROLLABLE self --- @param Dcs.DCSTypes#Vec2 Point The point where to wait. +-- @param DCS#Vec2 Point The point where to wait. -- @param #number Radius The radius of the embarking zone around the Point. --- @return Dcs.DCSTasking.Task#Task The DCS task structure. +-- @return DCS#Task The DCS task structure. function CONTROLLABLE:TaskEmbarkToTransport( Point, Radius ) self:F2( { self.ControllableName, Point, Radius } ) - local DCSTask --Dcs.DCSTasking.Task#Task + local DCSTask --DCS#Task DCSTask = { id = 'EmbarkToTransport', params = { x = Point.x, y = Point.y, @@ -1608,7 +1608,7 @@ end --- (AIR + GROUND) Return a mission task from a mission template. -- @param #CONTROLLABLE self -- @param #table TaskMission A table containing the mission task. --- @return Dcs.DCSTasking.Task#Task +-- @return DCS#Task function CONTROLLABLE:TaskMission( TaskMission ) self:F2( Points ) @@ -1762,7 +1762,7 @@ end --- Return a Misson task to follow a given route defined by Points. -- @param #CONTROLLABLE self -- @param #table Points A table of route points. --- @return Dcs.DCSTasking.Task#Task +-- @return DCS#Task function CONTROLLABLE:TaskRoute( Points ) self:F2( Points ) @@ -1777,7 +1777,7 @@ do -- Route methods --- (AIR + GROUND) Make the Controllable move to fly to a given point. -- @param #CONTROLLABLE self - -- @param Dcs.DCSTypes#Vec3 Point The destination point in Vec3 format. + -- @param DCS#Vec3 Point The destination point in Vec3 format. -- @param #number Speed The speed [m/s] to travel. -- @return #CONTROLLABLE self function CONTROLLABLE:RouteToVec2( Point, Speed ) @@ -1828,7 +1828,7 @@ do -- Route methods --- (AIR + GROUND) Make the Controllable move to a given point. -- @param #CONTROLLABLE self - -- @param Dcs.DCSTypes#Vec3 Point The destination point in Vec3 format. + -- @param DCS#Vec3 Point The destination point in Vec3 format. -- @param #number Speed The speed [m/s] to travel. -- @return #CONTROLLABLE self function CONTROLLABLE:RouteToVec3( Point, Speed ) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index fe4d5e20b..dc8157cde 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -113,9 +113,9 @@ GROUPTEMPLATE.Takeoff = { -- It is merely added to the @{Core.Database}. -- @param #GROUP self -- @param #table GroupTemplate The GroupTemplate Structure exactly as defined within the mission editor. --- @param Dcs.DCScoalition#coalition.side CoalitionSide The coalition.side of the group. --- @param Dcs.DCSGroup#Group.Category CategoryID The Group.Category of the group. --- @param Dcs.DCScountry#country.id CountryID the country.id of the group. +-- @param DCS#coalition.side CoalitionSide The coalition.side of the group. +-- @param DCS#Group.Category CategoryID The Group.Category of the group. +-- @param DCS#country.id CountryID the country.id of the group. -- @return #GROUP self function GROUP:NewTemplate( GroupTemplate, CoalitionSide, CategoryID, CountryID ) local GroupName = GroupTemplate.name @@ -149,7 +149,7 @@ end --- Find the GROUP wrapper class instance using the DCS Group. -- @param #GROUP self --- @param Dcs.DCSWrapper.Group#Group DCSGroup The DCS Group. +-- @param DCS#Group DCSGroup The DCS Group. -- @return #GROUP The GROUP. function GROUP:Find( DCSGroup ) @@ -172,7 +172,7 @@ end --- Returns the DCS Group. -- @param #GROUP self --- @return Dcs.DCSWrapper.Group#Group The DCS Group. +-- @return DCS#Group The DCS Group. function GROUP:GetDCSObject() local DCSGroup = Group.getByName( self.GroupName ) @@ -185,7 +185,7 @@ end --- Returns the @{DCSTypes#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission. -- @param Wrapper.Positionable#POSITIONABLE self --- @return Dcs.DCSTypes#Position The 3D position vectors of the POSITIONABLE. +-- @return DCS#Position The 3D position vectors of the POSITIONABLE. -- @return #nil The POSITIONABLE is not existing or alive. function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3() self:F2( self.PositionableName ) @@ -217,11 +217,11 @@ end function GROUP:IsAlive() self:F2( self.GroupName ) - local DCSGroup = self:GetDCSObject() -- Dcs.DCSGroup#Group + local DCSGroup = self:GetDCSObject() -- DCS#Group if DCSGroup then if DCSGroup:isExist() then - local DCSUnit = DCSGroup:getUnit(1) -- Dcs.DCSUnit#Unit + local DCSUnit = DCSGroup:getUnit(1) -- DCS#Unit if DCSUnit then local GroupIsAlive = DCSUnit:isActive() self:T3( GroupIsAlive ) @@ -277,7 +277,7 @@ end --- Returns category of the DCS Group. -- @param #GROUP self --- @return Dcs.DCSWrapper.Group#Group.Category The category ID +-- @return DCS#Group.Category The category ID function GROUP:GetCategory() self:F2( self.GroupName ) @@ -317,7 +317,7 @@ end --- Returns the coalition of the DCS Group. -- @param #GROUP self --- @return Dcs.DCSCoalitionWrapper.Object#coalition.side The coalition side of the DCS Group. +-- @return DCS#coalition.side The coalition side of the DCS Group. function GROUP:GetCoalition() self:F2( self.GroupName ) @@ -333,7 +333,7 @@ end --- Returns the country of the DCS Group. -- @param #GROUP self --- @return Dcs.DCScountry#country.id The country identifier. +-- @return DCS#country.id The country identifier. -- @return #nil The DCS Group is not existing or alive. function GROUP:GetCountry() self:F2( self.GroupName ) @@ -448,7 +448,7 @@ end -- If the underlying DCS Unit does not exist, the method will return nil. . -- @param #GROUP self -- @param #number UnitNumber The number of the DCS Unit to be returned. --- @return Dcs.DCSWrapper.Unit#Unit The DCS Unit. +-- @return DCS#Unit The DCS Unit. function GROUP:GetDCSUnit( UnitNumber ) self:F3( { self.GroupName, UnitNumber } ) @@ -488,7 +488,7 @@ end --- Returns the average velocity Vec3 vector. -- @param Wrapper.Group#GROUP self --- @return Dcs.DCSTypes#Vec3 The velocity Vec3 vector +-- @return DCS#Vec3 The velocity Vec3 vector -- @return #nil The GROUP is not existing or alive. function GROUP:GetVelocityVec3() self:F2( self.GroupName ) @@ -524,7 +524,7 @@ end --- Returns the average group height in meters. -- @param Wrapper.Group#GROUP self -- @param #boolean FromGround Measure from the ground or from sea level. Provide **true** for measuring from the ground. **false** or **nil** if you measure from sea level. --- @return Dcs.DCSTypes#Vec3 The height of the group. +-- @return DCS#Vec3 The height of the group. -- @return #nil The GROUP is not existing or alive. function GROUP:GetHeight( FromGround ) self:F2( self.GroupName ) @@ -658,7 +658,7 @@ end --- Returns the current point (Vec2 vector) of the first DCS Unit in the DCS Group. -- @param #GROUP self --- @return Dcs.DCSTypes#Vec2 Current Vec2 point of the first DCS Unit of the DCS Group. +-- @return DCS#Vec2 Current Vec2 point of the first DCS Unit of the DCS Group. function GROUP:GetVec2() self:F2( self.GroupName ) @@ -671,7 +671,7 @@ end --- Returns the current Vec3 vector of the first DCS Unit in the GROUP. -- @param #GROUP self --- @return Dcs.DCSTypes#Vec3 Current Vec3 of the first DCS Unit of the GROUP. +-- @return DCS#Vec3 Current Vec3 of the first DCS Unit of the GROUP. function GROUP:GetVec3() self:F2( self.GroupName ) @@ -723,10 +723,10 @@ end --- Returns a random @{DCSTypes#Vec3} vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP. -- @param #GROUP self -- @param #number Radius --- @return Dcs.DCSTypes#Vec3 The random 3D point vector around the first UNIT of the GROUP. +-- @return DCS#Vec3 The random 3D point vector around the first UNIT of the GROUP. -- @return #nil The GROUP is invalid or empty -- @usage --- -- If Radius is ignored, returns the Dcs.DCSTypes#Vec3 of first UNIT of the GROUP +-- -- If Radius is ignored, returns the DCS#Vec3 of first UNIT of the GROUP function GROUP:GetRandomVec3(Radius) self:F2(self.GroupName) @@ -1004,10 +1004,10 @@ do -- AI methods -- @return #GROUP The GROUP. function GROUP:SetAIOnOff( AIOnOff ) - local DCSGroup = self:GetDCSObject() -- Dcs.DCSGroup#Group + local DCSGroup = self:GetDCSObject() -- DCS#Group if DCSGroup then - local DCSController = DCSGroup:getController() -- Dcs.DCSController#Controller + local DCSController = DCSGroup:getController() -- DCS#Controller if DCSController then DCSController:setOnOff( AIOnOff ) return self @@ -1114,7 +1114,7 @@ end --- Sets the CountryID of the group in a Template. -- @param #GROUP self --- @param Dcs.DCScountry#country.id CountryID The country ID. +-- @param DCS#country.id CountryID The country ID. -- @return #table function GROUP:SetTemplateCountry( Template, CountryID ) Template.CountryID = CountryID @@ -1123,7 +1123,7 @@ end --- Sets the CoalitionID of the group in a Template. -- @param #GROUP self --- @param Dcs.DCSCoalitionWrapper.Object#coalition.side CoalitionID The coalition ID. +-- @param DCS#coalition.side CoalitionID The coalition ID. -- @return #table function GROUP:SetTemplateCoalition( Template, CoalitionID ) Template.CoalitionID = CoalitionID diff --git a/Moose Development/Moose/Wrapper/Identifiable.lua b/Moose Development/Moose/Wrapper/Identifiable.lua index 94d946f3c..671577882 100644 --- a/Moose Development/Moose/Wrapper/Identifiable.lua +++ b/Moose Development/Moose/Wrapper/Identifiable.lua @@ -9,6 +9,7 @@ -- === -- -- @module Wrapper.Identifiable +-- @image MOOSE.JPG --- @type IDENTIFIABLE -- @extends Wrapper.Object#OBJECT @@ -42,7 +43,7 @@ local _CategoryName = { --- Create a new IDENTIFIABLE from a DCSIdentifiable -- @param #IDENTIFIABLE self --- @param Dcs.DCSWrapper.Identifiable#Identifiable IdentifiableName The DCS Identifiable name +-- @param #string IdentifiableName The DCS Identifiable name -- @return #IDENTIFIABLE self function IDENTIFIABLE:New( IdentifiableName ) local self = BASE:Inherit( self, OBJECT:New( IdentifiableName ) ) @@ -60,7 +61,7 @@ end function IDENTIFIABLE:IsAlive() self:F3( self.IdentifiableName ) - local DCSIdentifiable = self:GetDCSObject() -- Dcs.DCSObject#Object + local DCSIdentifiable = self:GetDCSObject() -- DCS#Object if DCSIdentifiable then local IdentifiableIsAlive = DCSIdentifiable:isExist() @@ -108,7 +109,7 @@ end --- Returns category of the DCS Identifiable. -- @param #IDENTIFIABLE self --- @return Dcs.DCSWrapper.Object#Object.Category The category ID +-- @return DCS#Object.Category The category ID function IDENTIFIABLE:GetCategory() self:F2( self.ObjectName ) @@ -140,7 +141,7 @@ end --- Returns coalition of the Identifiable. -- @param #IDENTIFIABLE self --- @return Dcs.DCSCoalitionWrapper.Object#coalition.side The side of the coalition. +-- @return DCS#coalition.side The side of the coalition. -- @return #nil The DCS Identifiable is not existing or alive. function IDENTIFIABLE:GetCoalition() self:F2( self.IdentifiableName ) @@ -189,7 +190,7 @@ end --- Returns country of the Identifiable. -- @param #IDENTIFIABLE self --- @return Dcs.DCScountry#country.id The country identifier. +-- @return DCS#country.id The country identifier. -- @return #nil The DCS Identifiable is not existing or alive. function IDENTIFIABLE:GetCountry() self:F2( self.IdentifiableName ) @@ -210,7 +211,7 @@ end --- Returns Identifiable descriptor. Descriptor type depends on Identifiable category. -- @param #IDENTIFIABLE self --- @return Dcs.DCSWrapper.Identifiable#Identifiable.Desc The Identifiable descriptor. +-- @return DCS#Object.Desc The Identifiable descriptor. -- @return #nil The DCS Identifiable is not existing or alive. function IDENTIFIABLE:GetDesc() self:F2( self.IdentifiableName ) diff --git a/Moose Development/Moose/Wrapper/Object.lua b/Moose Development/Moose/Wrapper/Object.lua index b2b034a30..c218bf4c1 100644 --- a/Moose Development/Moose/Wrapper/Object.lua +++ b/Moose Development/Moose/Wrapper/Object.lua @@ -9,6 +9,7 @@ -- === -- -- @module Wrapper.Object +-- @image MOOSE.JPG --- @type OBJECT @@ -40,7 +41,7 @@ OBJECT = { --- Create a new OBJECT from a DCSObject -- @param #OBJECT self --- @param Dcs.DCSWrapper.Object#Object ObjectName The Object name +-- @param DCS#Object ObjectName The Object name -- @return #OBJECT self function OBJECT:New( ObjectName, Test ) local self = BASE:Inherit( self, BASE:New() ) @@ -53,7 +54,7 @@ end --- Returns the unit's unique identifier. -- @param Wrapper.Object#OBJECT self --- @return Dcs.DCSWrapper.Object#Object.ID ObjectID +-- @return DCS#Object.ID ObjectID -- @return #nil The DCS Object is not existing or alive. function OBJECT:GetID() diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 296cc0119..912467d20 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -60,7 +60,7 @@ POSITIONABLE.__.Cargo = {} --- Create a new POSITIONABLE from a DCSPositionable -- @param #POSITIONABLE self --- @param Dcs.DCSWrapper.Positionable#Positionable PositionableName The POSITIONABLE name +-- @param #string PositionableName The POSITIONABLE name -- @return #POSITIONABLE self function POSITIONABLE:New( PositionableName ) local self = BASE:Inherit( self, IDENTIFIABLE:New( PositionableName ) ) @@ -71,7 +71,7 @@ end --- Returns the @{DCSTypes#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission. -- @param Wrapper.Positionable#POSITIONABLE self --- @return Dcs.DCSTypes#Position The 3D position vectors of the POSITIONABLE. +-- @return DCS#Position The 3D position vectors of the POSITIONABLE. -- @return #nil The POSITIONABLE is not existing or alive. function POSITIONABLE:GetPositionVec3() self:F2( self.PositionableName ) @@ -91,7 +91,7 @@ end --- Returns the @{DCSTypes#Vec2} vector indicating the point in 2D of the POSITIONABLE within the mission. -- @param Wrapper.Positionable#POSITIONABLE self --- @return Dcs.DCSTypes#Vec2 The 2D point vector of the POSITIONABLE. +-- @return DCS#Vec2 The 2D point vector of the POSITIONABLE. -- @return #nil The POSITIONABLE is not existing or alive. function POSITIONABLE:GetVec2() self:F2( self.PositionableName ) @@ -188,10 +188,10 @@ end --- Returns a random @{DCSTypes#Vec3} vector within a range, indicating the point in 3D of the POSITIONABLE within the mission. -- @param Wrapper.Positionable#POSITIONABLE self -- @param #number Radius --- @return Dcs.DCSTypes#Vec3 The 3D point vector of the POSITIONABLE. +-- @return DCS#Vec3 The 3D point vector of the POSITIONABLE. -- @return #nil The POSITIONABLE is not existing or alive. -- @usage --- -- If Radius is ignored, returns the Dcs.DCSTypes#Vec3 of first UNIT of the GROUP +-- -- If Radius is ignored, returns the DCS#Vec3 of first UNIT of the GROUP function POSITIONABLE:GetRandomVec3( Radius ) self:F2( self.PositionableName ) @@ -222,7 +222,7 @@ end --- Returns the @{DCSTypes#Vec3} vector indicating the 3D vector of the POSITIONABLE within the mission. -- @param Wrapper.Positionable#POSITIONABLE self --- @return Dcs.DCSTypes#Vec3 The 3D point vector of the POSITIONABLE. +-- @return DCS#Vec3 The 3D point vector of the POSITIONABLE. -- @return #nil The POSITIONABLE is not existing or alive. function POSITIONABLE:GetVec3() self:F2( self.PositionableName ) @@ -243,7 +243,7 @@ end --- Get the bounding box of the underlying POSITIONABLE DCS Object. -- @param #POSITIONABLE self --- @return Dcs.DCSTypes#Distance The bounding box of the POSITIONABLE. +-- @return DCS#Distance The bounding box of the POSITIONABLE. -- @return #nil The POSITIONABLE is not existing or alive. function POSITIONABLE:GetBoundingBox() --R2.1 self:F2() @@ -251,7 +251,7 @@ function POSITIONABLE:GetBoundingBox() --R2.1 local DCSPositionable = self:GetDCSObject() if DCSPositionable then - local PositionableDesc = DCSPositionable:getDesc() --Dcs.DCSTypes#Desc + local PositionableDesc = DCSPositionable:getDesc() --DCS#Desc if PositionableDesc then local PositionableBox = PositionableDesc.box return PositionableBox @@ -266,7 +266,7 @@ end --- Returns the altitude of the POSITIONABLE. -- @param Wrapper.Positionable#POSITIONABLE self --- @return Dcs.DCSTypes#Distance The altitude of the POSITIONABLE. +-- @return DCS#Distance The altitude of the POSITIONABLE. -- @return #nil The POSITIONABLE is not existing or alive. function POSITIONABLE:GetAltitude() self:F2() @@ -274,7 +274,7 @@ function POSITIONABLE:GetAltitude() local DCSPositionable = self:GetDCSObject() if DCSPositionable then - local PositionablePointVec3 = DCSPositionable:getPoint() --Dcs.DCSTypes#Vec3 + local PositionablePointVec3 = DCSPositionable:getPoint() --DCS#Vec3 return PositionablePointVec3.y end @@ -383,7 +383,7 @@ end --- Returns the POSITIONABLE velocity Vec3 vector. -- @param Wrapper.Positionable#POSITIONABLE self --- @return Dcs.DCSTypes#Vec3 The velocity Vec3 vector +-- @return DCS#Vec3 The velocity Vec3 vector -- @return #nil The POSITIONABLE is not existing or alive. function POSITIONABLE:GetVelocityVec3() self:F2( self.PositionableName ) @@ -404,7 +404,7 @@ end --- Returns the POSITIONABLE height in meters. -- @param Wrapper.Positionable#POSITIONABLE self --- @return Dcs.DCSTypes#Vec3 The height of the positionable. +-- @return DCS#Vec3 The height of the positionable. -- @return #nil The POSITIONABLE is not existing or alive. function POSITIONABLE:GetHeight() --R2.1 self:F2( self.PositionableName ) @@ -483,7 +483,7 @@ end --- Returns a message with the callsign embedded (if there is one). -- @param #POSITIONABLE self -- @param #string Message The message text --- @param Dcs.DCSTypes#Duration Duration The duration of the message. +-- @param DCS#Duration Duration The duration of the message. -- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. -- @return Core.Message#MESSAGE function POSITIONABLE:GetMessage( Message, Duration, Name ) --R2.1 changed callsign and name and using GetMessageText @@ -518,7 +518,7 @@ end -- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. -- @param #POSITIONABLE self -- @param #string Message The message text --- @param Dcs.DCSTypes#Duration Duration The duration of the message. +-- @param DCS#Duration Duration The duration of the message. -- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. function POSITIONABLE:MessageToAll( Message, Duration, Name ) self:F2( { Message, Duration } ) @@ -535,8 +535,8 @@ end -- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. -- @param #POSITIONABLE self -- @param #string Message The message text --- @param Dcs.DCSTYpes#Duration Duration The duration of the message. --- @param Dcs.DCScoalition#coalition MessageCoalition The Coalition receiving the message. +-- @param DCS#Duration Duration The duration of the message. +-- @param DCS#coalition MessageCoalition The Coalition receiving the message. -- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition, Name ) self:F2( { Message, Duration } ) @@ -557,7 +557,7 @@ end -- @param #POSITIONABLE self -- @param #string Message The message text -- @param Core.Message#MESSAGE.Type MessageType The message type that determines the duration. --- @param Dcs.DCScoalition#coalition MessageCoalition The Coalition receiving the message. +-- @param DCS#coalition MessageCoalition The Coalition receiving the message. -- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. function POSITIONABLE:MessageTypeToCoalition( Message, MessageType, MessageCoalition, Name ) self:F2( { Message, MessageType } ) @@ -577,7 +577,7 @@ end -- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. -- @param #POSITIONABLE self -- @param #string Message The message text --- @param Dcs.DCSTYpes#Duration Duration The duration of the message. +-- @param DCS#Duration Duration The duration of the message. -- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. function POSITIONABLE:MessageToRed( Message, Duration, Name ) self:F2( { Message, Duration } ) @@ -594,7 +594,7 @@ end -- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. -- @param #POSITIONABLE self -- @param #string Message The message text --- @param Dcs.DCSTypes#Duration Duration The duration of the message. +-- @param DCS#Duration Duration The duration of the message. -- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. function POSITIONABLE:MessageToBlue( Message, Duration, Name ) self:F2( { Message, Duration } ) @@ -611,7 +611,7 @@ end -- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. -- @param #POSITIONABLE self -- @param #string Message The message text --- @param Dcs.DCSTypes#Duration Duration The duration of the message. +-- @param DCS#Duration Duration The duration of the message. -- @param Wrapper.Client#CLIENT Client The client object receiving the message. -- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. function POSITIONABLE:MessageToClient( Message, Duration, Client, Name ) @@ -629,7 +629,7 @@ end -- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. -- @param #POSITIONABLE self -- @param #string Message The message text --- @param Dcs.DCSTypes#Duration Duration The duration of the message. +-- @param DCS#Duration Duration The duration of the message. -- @param Wrapper.Group#GROUP MessageGroup The GROUP object receiving the message. -- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. function POSITIONABLE:MessageToGroup( Message, Duration, MessageGroup, Name ) @@ -676,7 +676,7 @@ end -- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. -- @param #POSITIONABLE self -- @param #string Message The message text --- @param Dcs.DCSTypes#Duration Duration The duration of the message. +-- @param DCS#Duration Duration The duration of the message. -- @param Core.Set#SET_GROUP MessageSetGroup The SET_GROUP collection receiving the message. -- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. function POSITIONABLE:MessageToSetGroup( Message, Duration, MessageSetGroup, Name ) --R2.1 @@ -700,7 +700,7 @@ end -- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message. -- @param #POSITIONABLE self -- @param #string Message The message text --- @param Dcs.DCSTypes#Duration Duration The duration of the message. +-- @param DCS#Duration Duration The duration of the message. -- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable. function POSITIONABLE:Message( Message, Duration, Name ) self:F2( { Message, Duration } ) diff --git a/Moose Development/Moose/Wrapper/Static.lua b/Moose Development/Moose/Wrapper/Static.lua index 7ca5699b3..a62aa11c1 100644 --- a/Moose Development/Moose/Wrapper/Static.lua +++ b/Moose Development/Moose/Wrapper/Static.lua @@ -56,7 +56,7 @@ end --- Finds a STATIC from the _DATABASE using a DCSStatic object. -- @param #STATIC self --- @param Dcs.DCSWrapper.Static#Static DCSStatic An existing DCS Static object reference. +-- @param DCS#StaticObject DCSStatic An existing DCS Static object reference. -- @return #STATIC self function STATIC:Find( DCSStatic ) diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 03278f9c7..753b842f4 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -45,7 +45,7 @@ -- -- The DCS Unit APIs are used extensively within MOOSE. The UNIT class has for each DCS Unit API a corresponding method. -- To be able to distinguish easily in your code the difference between a UNIT API call and a DCS Unit API call, --- the first letter of the method is also capitalized. So, by example, the DCS Unit method @{DCSWrapper.Unit#Unit.getName}() +-- the first letter of the method is also capitalized. So, by example, the DCS Unit method @{DCS#Unit.getName}() -- is implemented in the UNIT class as @{#UNIT.GetName}(). -- -- ## Smoke, Flare Units @@ -116,7 +116,7 @@ end --- Finds a UNIT from the _DATABASE using a DCSUnit object. -- @param #UNIT self --- @param Dcs.DCSWrapper.Unit#Unit DCSUnit An existing DCS Unit object reference. +-- @param DCS#Unit DCSUnit An existing DCS Unit object reference. -- @return #UNIT self function UNIT:Find( DCSUnit ) @@ -145,7 +145,7 @@ end --- @param #UNIT self --- @return Dcs.DCSWrapper.Unit#Unit +-- @return DCS#Unit function UNIT:GetDCSObject() local DCSUnit = Unit.getByName( self.UnitName ) @@ -311,7 +311,7 @@ end function UNIT:IsAlive() self:F3( self.UnitName ) - local DCSUnit = self:GetDCSObject() -- Dcs.DCSUnit#Unit + local DCSUnit = self:GetDCSObject() -- DCS#Unit if DCSUnit then local UnitIsAlive = DCSUnit:isExist() and DCSUnit:isActive() @@ -352,7 +352,7 @@ end function UNIT:GetPlayerName() self:F2( self.UnitName ) - local DCSUnit = self:GetDCSObject() -- Dcs.DCSUnit#Unit + local DCSUnit = self:GetDCSObject() -- DCS#Unit if DCSUnit then @@ -458,7 +458,7 @@ end --- Returns the Unit's ammunition. -- @param #UNIT self --- @return Dcs.DCSWrapper.Unit#Unit.Ammo +-- @return DCS#Unit.Ammo -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetAmmo() self:F2( self.UnitName ) @@ -475,7 +475,7 @@ end --- Returns the unit sensors. -- @param #UNIT self --- @return Dcs.DCSWrapper.Unit#Unit.Sensors +-- @return DCS#Unit.Sensors -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetSensors() self:F2( self.UnitName ) @@ -539,7 +539,7 @@ end -- * Second value is the object of the radar's interest. Not nil only if at least one radar of the unit is tracking a target. -- @param #UNIT self -- @return #boolean Indicates if at least one of the unit's radar(s) is on. --- @return Dcs.DCSWrapper.Object#Object The object of the radar's interest. Not nil only if at least one radar of the unit is tracking a target. +-- @return DCS#Object The object of the radar's interest. Not nil only if at least one radar of the unit is tracking a target. -- @return #nil The DCS Unit is not existing or alive. function UNIT:GetRadar() self:F2( self.UnitName ) From 18098d402b284e67652e0189b99ab2dea9eb3c80 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Fri, 1 Jun 2018 10:59:53 +0200 Subject: [PATCH 146/420] Documentation updates --- .../Moose/AI/AI_A2A_Dispatcher.lua | 30 ++++++------ .../Moose/Functional/Detection.lua | 46 +++++++++---------- .../Moose/Tasking/DetectionManager.lua | 4 +- .../Moose/Tasking/Task_A2A_Dispatcher.lua | 4 +- .../Moose/Tasking/Task_A2G_Dispatcher.lua | 2 +- 5 files changed, 43 insertions(+), 43 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index 6f0c7045d..92ecfce9b 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -1267,7 +1267,7 @@ do -- AI_A2A_DISPATCHER --- Calculates which AI friendlies are nearby the area -- @param #AI_A2A_DISPATCHER self -- @param DetectedItem - -- @return #number, Core.CommandCenter#REPORT + -- @return #table A list of the friendlies nearby. function AI_A2A_DISPATCHER:GetAIFriendliesNearBy( DetectedItem ) local FriendliesNearBy = self.Detection:GetFriendliesDistance( DetectedItem ) @@ -2881,7 +2881,7 @@ do -- AI_A2A_DISPATCHER --- Creates an ENGAGE task when there are human friendlies airborne near the targets. -- @param #AI_A2A_DISPATCHER self - -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem + -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem The detected item. -- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units. -- @return #nil If there are no targets to be set. function AI_A2A_DISPATCHER:EvaluateENGAGE( DetectedItem ) @@ -2908,7 +2908,7 @@ do -- AI_A2A_DISPATCHER --- Creates an GCI task when there are targets for it. -- @param #AI_A2A_DISPATCHER self - -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem + -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem The detected item. -- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units. -- @return #nil If there are no targets to be set. function AI_A2A_DISPATCHER:EvaluateGCI( DetectedItem ) @@ -2935,7 +2935,7 @@ do -- AI_A2A_DISPATCHER --- Assigns A2A AI Tasks in relation to the detected items. -- @param #AI_A2A_DISPATCHER self - -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Detection#DETECTION_BASE} derived object. + -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object. -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. function AI_A2A_DISPATCHER:ProcessDetected( Detection ) @@ -3060,10 +3060,10 @@ end do - --- Calculates which HUMAN friendlies are nearby the area + --- Calculates which HUMAN friendlies are nearby the area. -- @param #AI_A2A_DISPATCHER self - -- @param DetectedItem - -- @return #number, Core.CommandCenter#REPORT + -- @param DetectedItem The detected item. + -- @return #number, Core.Report#REPORT The amount of friendlies and a text string explaining which friendlies of which type. function AI_A2A_DISPATCHER:GetPlayerFriendliesNearBy( DetectedItem ) local DetectedSet = DetectedItem.Set @@ -3106,14 +3106,14 @@ do return PlayersCount, PlayerTypesReport end - --- Calculates which friendlies are nearby the area + --- Calculates which friendlies are nearby the area. -- @param #AI_A2A_DISPATCHER self - -- @param DetectedItem - -- @return #number, Core.CommandCenter#REPORT - function AI_A2A_DISPATCHER:GetFriendliesNearBy( Target ) + -- @param DetectedItem The detected item. + -- @return #number, Core.Report#REPORT The amount of friendlies and a text string explaining which friendlies of which type. + function AI_A2A_DISPATCHER:GetFriendliesNearBy( DetectedItem ) - local DetectedSet = Target.Set - local FriendlyUnitsNearBy = self.Detection:GetFriendliesNearBy( Target ) + local DetectedSet = DetectedItem.Set + local FriendlyUnitsNearBy = self.Detection:GetFriendliesNearBy( DetectedItem ) local FriendlyTypes = {} local FriendliesCount = 0 @@ -3150,8 +3150,8 @@ do return FriendliesCount, FriendlyTypesReport end - --- - -- @param AI_A2A_DISPATCHER + --- Schedules a new CAP for the given SquadronName. + -- @param #AI_A2A_DISPATCHER self -- @param #string SquadronName The squadron name. function AI_A2A_DISPATCHER:SchedulerCAP( SquadronName ) self:CAP( SquadronName ) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 9e38a5b45..f8cfbf327 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -100,11 +100,11 @@ do -- DETECTION_BASE -- -- Various methods exist how to retrieve the grouped items from a DETECTION_BASE derived class: -- - -- * The method @{Detection#DETECTION_BASE.GetDetectedItems}() retrieves the DetectedItems[] list. - -- * A DetectedItem from the DetectedItems[] list can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedItem}( DetectedItemIndex ). + -- * The method @{Functional.Detection#DETECTION_BASE.GetDetectedItems}() retrieves the DetectedItems[] list. + -- * A DetectedItem from the DetectedItems[] list can be retrieved using the method @{Functional.Detection#DETECTION_BASE.GetDetectedItem}( DetectedItemIndex ). -- Note that this method returns a DetectedItem element from the list, that contains a Set variable and further information -- about the DetectedItem that is set by the DETECTION_BASE derived classes, used to group the DetectedItem. - -- * A DetectedSet from the DetectedItems[] list can be retrieved using the method @{Detection#DETECTION_BASE.GetDetectedSet}( DetectedItemIndex ). + -- * A DetectedSet from the DetectedItems[] list can be retrieved using the method @{Functional.Detection#DETECTION_BASE.GetDetectedSet}( DetectedItemIndex ). -- This method retrieves the Set from a DetectedItem element from the DetectedItem list (DetectedItems[ DetectedItemIndex ].Set ). -- -- ## **Visual filters** to fine-tune the probability of the detected objects @@ -141,7 +141,7 @@ do -- DETECTION_BASE -- -- Note that based on this probability factor, not only the detection but also the **type** of the unit will be applied! -- - -- Use the method @{Detection#DETECTION_BASE.SetDistanceProbability}() to set the probability factor upon a 10 km distance. + -- Use the method @{Functional.Detection#DETECTION_BASE.SetDistanceProbability}() to set the probability factor upon a 10 km distance. -- -- ### Alpha Angle visual detection probability -- @@ -153,7 +153,7 @@ do -- DETECTION_BASE -- For example, if a alpha angle probability factor of 0.7 is given, the extrapolated probabilities of the different angles would look like: -- 0°: 70%, 10°: 75,21%, 20°: 80,26%, 30°: 85%, 40°: 89,28%, 50°: 92,98%, 60°: 95,98%, 70°: 98,19%, 80°: 99,54%, 90°: 100% -- - -- Use the method @{Detection#DETECTION_BASE.SetAlphaAngleProbability}() to set the probability factor if 0°. + -- Use the method @{Functional.Detection#DETECTION_BASE.SetAlphaAngleProbability}() to set the probability factor if 0°. -- -- ### Cloudy Zones detection probability -- @@ -161,7 +161,7 @@ do -- DETECTION_BASE -- The Cloudy Zones work with the ZONE_BASE derived classes. The mission designer can define within the mission -- zones that reflect cloudy areas where detected units may not be so easily visually detected. -- - -- Use the method @{Detection#DETECTION_BASE.SetZoneProbability}() to set for a defined number of zones, the probability factors. + -- Use the method @{Functional.Detection#DETECTION_BASE.SetZoneProbability}() to set for a defined number of zones, the probability factors. -- -- Note however, that the more zones are defined to be "cloudy" within a detection, the more performance it will take -- from the DETECTION_BASE to calculate the presence of the detected unit within each zone. @@ -178,7 +178,7 @@ do -- DETECTION_BASE -- ### Detection acceptance of within range limit -- -- A range can be set that will limit a successful detection for a unit. - -- Use the method @{Detection#DETECTION_BASE.SetAcceptRange}() to apply a range in meters till where detected units will be accepted. + -- Use the method @{Functional.Detection#DETECTION_BASE.SetAcceptRange}() to apply a range in meters till where detected units will be accepted. -- -- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. -- @@ -195,7 +195,7 @@ do -- DETECTION_BASE -- ### Detection acceptance if within zone(s). -- -- Specific ZONE_BASE object(s) can be given as a parameter, which will only accept a detection if the unit is within the specified ZONE_BASE object(s). - -- Use the method @{Detection#DETECTION_BASE.SetAcceptZones}() will accept detected units if they are within the specified zones. + -- Use the method @{Functional.Detection#DETECTION_BASE.SetAcceptZones}() will accept detected units if they are within the specified zones. -- -- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. -- @@ -215,7 +215,7 @@ do -- DETECTION_BASE -- ### Detection rejectance if within zone(s). -- -- Specific ZONE_BASE object(s) can be given as a parameter, which will reject detection if the unit is within the specified ZONE_BASE object(s). - -- Use the method @{Detection#DETECTION_BASE.SetRejectZones}() will reject detected units if they are within the specified zones. + -- Use the method @{Functional.Detection#DETECTION_BASE.SetRejectZones}() will reject detected units if they are within the specified zones. -- An example of how to use the method is shown below. -- -- local SetGroup = SET_GROUP:New():FilterPrefixes( "FAC" ):FilterStart() -- Build a SetGroup of Forward Air Controllers. @@ -235,7 +235,7 @@ do -- DETECTION_BASE -- -- ## Detection of Friendlies Nearby -- - -- Use the method @{Detection#DETECTION_BASE.SetFriendliesRange}() to set the range what will indicate when friendlies are nearby + -- Use the method @{Functional.Detection#DETECTION_BASE.SetFriendliesRange}() to set the range what will indicate when friendlies are nearby -- a DetectedItem. The default range is 6000 meters. For air detections, it is advisory to use about 30.000 meters. -- -- ## DETECTION_BASE is a Finite State Machine @@ -1802,7 +1802,7 @@ end do -- DETECTION_UNITS - --- # DETECTION_UNITS class, extends @{Detection#DETECTION_BASE} + --- # DETECTION_UNITS class, extends @{Functional.Detection#DETECTION_BASE} -- -- The DETECTION_UNITS class will detect units within the battle zone. -- It will build a DetectedItems list filled with DetectedItems. Each DetectedItem will contain a field Set, which contains a @{Core.Set#SET_UNIT} containing ONE @{UNIT} object reference. @@ -2052,7 +2052,7 @@ end do -- DETECTION_TYPES - --- # 3) DETECTION_TYPES class, extends @{Detection#DETECTION_BASE} + --- # 3) DETECTION_TYPES class, extends @{Functional.Detection#DETECTION_BASE} -- -- The DETECTION_TYPES class will detect units within the battle zone. -- It will build a DetectedItems[] list filled with DetectedItems, grouped by the type of units detected. @@ -2261,7 +2261,7 @@ end do -- DETECTION_AREAS - --- # 4) DETECTION_AREAS class, extends @{Detection#DETECTION_BASE} + --- # 4) DETECTION_AREAS class, extends @{Functional.Detection#DETECTION_BASE} -- -- The DETECTION_AREAS class will detect units within the battle zone for a list of @{Wrapper.Group}s detecting targets following (a) detection method(s), -- and will build a list (table) of @{Core.Set#SET_UNIT}s containing the @{Unit#UNIT}s detected. @@ -2270,26 +2270,26 @@ do -- DETECTION_AREAS -- -- ## 4.1) Retrieve the Detected Unit Sets and Detected Zones -- - -- The methods to manage the DetectedItems[].Set(s) are implemented in @{Detection#DECTECTION_BASE} and - -- the methods to manage the DetectedItems[].Zone(s) is implemented in @{Detection#DETECTION_AREAS}. + -- The methods to manage the DetectedItems[].Set(s) are implemented in @{Functional.Detection#DECTECTION_BASE} and + -- the methods to manage the DetectedItems[].Zone(s) is implemented in @{Functional.Detection#DETECTION_AREAS}. -- - -- Retrieve the DetectedItems[].Set with the method @{Detection#DETECTION_BASE.GetDetectedSet}(). A @{Core.Set#SET_UNIT} object will be returned. + -- Retrieve the DetectedItems[].Set with the method @{Functional.Detection#DETECTION_BASE.GetDetectedSet}(). A @{Core.Set#SET_UNIT} object will be returned. -- - -- Retrieve the formed @{Zone@ZONE_UNIT}s as a result of the grouping the detected units within the DetectionZoneRange, use the method @{Detection#DETECTION_BASE.GetDetectionZones}(). - -- To understand the amount of zones created, use the method @{Detection#DETECTION_BASE.GetDetectionZoneCount}(). - -- If you want to obtain a specific zone from the DetectedZones, use the method @{Detection#DETECTION_BASE.GetDetectionZone}() with a given index. + -- Retrieve the formed @{Zone@ZONE_UNIT}s as a result of the grouping the detected units within the DetectionZoneRange, use the method @{Functional.Detection#DETECTION_BASE.GetDetectionZones}(). + -- To understand the amount of zones created, use the method @{Functional.Detection#DETECTION_BASE.GetDetectionZoneCount}(). + -- If you want to obtain a specific zone from the DetectedZones, use the method @{Functional.Detection#DETECTION_BASE.GetDetectionZone}() with a given index. -- -- ## 4.4) Flare or Smoke detected units -- - -- Use the methods @{Detection#DETECTION_AREAS.FlareDetectedUnits}() or @{Detection#DETECTION_AREAS.SmokeDetectedUnits}() to flare or smoke the detected units when a new detection has taken place. + -- Use the methods @{Functional.Detection#DETECTION_AREAS.FlareDetectedUnits}() or @{Functional.Detection#DETECTION_AREAS.SmokeDetectedUnits}() to flare or smoke the detected units when a new detection has taken place. -- -- ## 4.5) Flare or Smoke or Bound detected zones -- -- Use the methods: -- - -- * @{Detection#DETECTION_AREAS.FlareDetectedZones}() to flare in a color - -- * @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to smoke in a color - -- * @{Detection#DETECTION_AREAS.SmokeDetectedZones}() to bound with a tire with a white flag + -- * @{Functional.Detection#DETECTION_AREAS.FlareDetectedZones}() to flare in a color + -- * @{Functional.Detection#DETECTION_AREAS.SmokeDetectedZones}() to smoke in a color + -- * @{Functional.Detection#DETECTION_AREAS.SmokeDetectedZones}() to bound with a tire with a white flag -- -- the detected zones when a new detection has taken place. -- diff --git a/Moose Development/Moose/Tasking/DetectionManager.lua b/Moose Development/Moose/Tasking/DetectionManager.lua index a716c11ff..0c76973dd 100644 --- a/Moose Development/Moose/Tasking/DetectionManager.lua +++ b/Moose Development/Moose/Tasking/DetectionManager.lua @@ -256,7 +256,7 @@ do -- DETECTION_REPORTING --- Creates a string of the detected items in a @{Detection}. -- @param #DETECTION_MANAGER self - -- @param Core.Set#SET_UNIT DetectedSet The detected Set created by the @{Detection#DETECTION_BASE} object. + -- @param Core.Set#SET_UNIT DetectedSet The detected Set created by the @{Functional.Detection#DETECTION_BASE} object. -- @return #DETECTION_MANAGER self function DETECTION_REPORTING:GetDetectedItemsText( DetectedSet ) self:F2() @@ -289,7 +289,7 @@ do -- DETECTION_REPORTING --- Reports the detected items to the @{Core.Set#SET_GROUP}. -- @param #DETECTION_REPORTING self -- @param Wrapper.Group#GROUP Group The @{Wrapper.Group} object to where the report needs to go. - -- @param Functional.Detection#DETECTION_AREAS Detection The detection created by the @{Detection#DETECTION_BASE} object. + -- @param Functional.Detection#DETECTION_AREAS Detection The detection created by the @{Functional.Detection#DETECTION_BASE} object. -- @return #boolean Return true if you want the reporting to continue... false will cancel the reporting loop. function DETECTION_REPORTING:ProcessDetected( Group, Detection ) self:F2( Group ) diff --git a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua index 204eb87a3..9b68fd38a 100644 --- a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua @@ -334,7 +334,7 @@ do -- TASK_A2A_DISPATCHER -- @param #TASK_A2A_DISPATCHER self -- @param Tasking.Mission#MISSION Mission -- @param Tasking.Task#TASK Task - -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Detection#DETECTION_BASE} derived object. + -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object. -- @param #boolean DetectedItemID -- @param #boolean DetectedItemChange -- @return Tasking.Task#TASK @@ -484,7 +484,7 @@ do -- TASK_A2A_DISPATCHER --- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}. -- @param #TASK_A2A_DISPATCHER self - -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Detection#DETECTION_BASE} derived object. + -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object. -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. function TASK_A2A_DISPATCHER:ProcessDetected( Detection ) self:F() diff --git a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua index 27493d738..d9dae1ab4 100644 --- a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua @@ -582,7 +582,7 @@ do -- TASK_A2G_DISPATCHER --- Assigns tasks in relation to the detected items to the @{Core.Set#SET_GROUP}. -- @param #TASK_A2G_DISPATCHER self - -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Detection#DETECTION_BASE} derived object. + -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object. -- @return #boolean Return true if you want the task assigning to continue... false will cancel the loop. function TASK_A2G_DISPATCHER:ProcessDetected( Detection ) self:F() From 43a4052dc8ead23784027fe8a89c6044e0e2330f Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Fri, 1 Jun 2018 16:00:42 +0200 Subject: [PATCH 147/420] Documentation cleanup --- Moose Development/Moose/AI/AI_A2A_Gci.lua | 4 +-- Moose Development/Moose/AI/AI_A2A_Patrol.lua | 4 +-- Moose Development/Moose/AI/AI_BAI.lua | 5 ++- Moose Development/Moose/AI/AI_Balancer.lua | 6 ++-- Moose Development/Moose/AI/AI_CAP.lua | 4 +-- Moose Development/Moose/AI/AI_CAS.lua | 6 +--- Moose Development/Moose/AI/AI_Cargo_APC.lua | 6 +--- .../Moose/AI/AI_Cargo_Airplane.lua | 4 +-- .../Moose/AI/AI_Cargo_Dispatcher_Airplane.lua | 6 +--- Moose Development/Moose/AI/AI_Formation.lua | 6 ++-- Moose Development/Moose/AI/AI_Patrol.lua | 10 +++--- Moose Development/Moose/Cargo/Cargo.lua | 3 +- Moose Development/Moose/Cargo/CargoCrate.lua | 4 +-- Moose Development/Moose/Cargo/CargoGroup.lua | 4 +-- .../Moose/Cargo/CargoSlingload.lua | 4 +-- Moose Development/Moose/Cargo/CargoUnit.lua | 4 +-- Moose Development/Moose/Core/Database.lua | 4 ++- Moose Development/Moose/Core/Event.lua | 5 +-- Moose Development/Moose/Core/Fsm.lua | 4 +-- Moose Development/Moose/Core/Goal.lua | 2 +- Moose Development/Moose/Core/Menu.lua | 36 +++++++++++-------- Moose Development/Moose/Core/Point.lua | 6 ++-- Moose Development/Moose/Core/Radio.lua | 18 +++++----- Moose Development/Moose/Core/Scheduler.lua | 2 +- Moose Development/Moose/Core/Settings.lua | 2 +- Moose Development/Moose/Core/Spawn.lua | 3 +- Moose Development/Moose/Core/SpawnStatic.lua | 2 +- Moose Development/Moose/Core/Spot.lua | 2 +- Moose Development/Moose/Core/Velocity.lua | 2 +- Moose Development/Moose/Core/Zone.lua | 8 ++--- .../Moose/Functional/Detection.lua | 13 +++---- .../Moose/Functional/PseudoATC.lua | 4 +-- Moose Development/Moose/Functional/RAT.lua | 3 +- Moose Development/Moose/Functional/Range.lua | 2 +- .../Moose/Functional/ZoneCaptureCoalition.lua | 4 +-- .../Moose/Functional/ZoneGoal.lua | 4 +-- .../Moose/Functional/ZoneGoalCargo.lua | 4 +-- .../Moose/Functional/ZoneGoalCoalition.lua | 4 +-- Moose Development/Moose/Wrapper/Airbase.lua | 2 +- Moose Development/Moose/Wrapper/Client.lua | 3 +- .../Moose/Wrapper/Controllable.lua | 8 ++--- Moose Development/Moose/Wrapper/Group.lua | 4 ++- .../Moose/Wrapper/Identifiable.lua | 2 +- Moose Development/Moose/Wrapper/Object.lua | 2 +- .../Moose/Wrapper/Positionable.lua | 6 ++-- Moose Development/Moose/Wrapper/Scenery.lua | 3 +- Moose Development/Moose/Wrapper/Static.lua | 9 ++--- 47 files changed, 108 insertions(+), 145 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A_Gci.lua b/Moose Development/Moose/AI/AI_A2A_Gci.lua index e968c7320..c1acb6df7 100644 --- a/Moose Development/Moose/AI/AI_A2A_Gci.lua +++ b/Moose Development/Moose/AI/AI_A2A_Gci.lua @@ -17,9 +17,7 @@ -- @extends AI.AI_A2A#AI_A2A ---- # AI_A2A_GCI class, extends @{AI.AI_A2A#AI_A2A} --- --- The AI_A2A_GCI class implements the core functions to intercept intruders. The Engage function will intercept intruders. +--- Implements the core functions to intercept intruders. Use the Engage trigger to intercept intruders. -- -- ![Process](..\Presentations\AI_GCI\Dia3.JPG) -- diff --git a/Moose Development/Moose/AI/AI_A2A_Patrol.lua b/Moose Development/Moose/AI/AI_A2A_Patrol.lua index e9ea266db..3ff3cfa69 100644 --- a/Moose Development/Moose/AI/AI_A2A_Patrol.lua +++ b/Moose Development/Moose/AI/AI_A2A_Patrol.lua @@ -13,9 +13,7 @@ --- @type AI_A2A_PATROL -- @extends AI.AI_A2A#AI_A2A ---- # AI_A2A_PATROL class, extends @{AI.AI_A2A#AI_A2A} --- --- The AI_A2A_PATROL class implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group}. +--- Implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Group} or @{Wrapper.Group}. -- -- ![Process](..\Presentations\AI_PATROL\Dia3.JPG) -- diff --git a/Moose Development/Moose/AI/AI_BAI.lua b/Moose Development/Moose/AI/AI_BAI.lua index d68e5b006..fa66d3993 100644 --- a/Moose Development/Moose/AI/AI_BAI.lua +++ b/Moose Development/Moose/AI/AI_BAI.lua @@ -27,9 +27,8 @@ -- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. -- @extends AI.AI_Patrol#AI_PATROL_ZONE ---- AI_BAI_ZONE derives from the @{AI.AI_Patrol#AI_PATROL_ZONE}, inheriting its methods and behaviour. --- --- The AI_BAI_ZONE class implements the core functions to provide BattleGround Air Interdiction in an Engage @{Zone} by an AIR @{Wrapper.Controllable} or @{Wrapper.Group}. +--- Implements the core functions to provide BattleGround Air Interdiction in an Engage @{Zone} by an AIR @{Wrapper.Controllable} or @{Wrapper.Group}. +-- -- The AI_BAI_ZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone. -- -- ![HoldAndEngage](..\Presentations\AI_BAI\Dia3.JPG) diff --git a/Moose Development/Moose/AI/AI_Balancer.lua b/Moose Development/Moose/AI/AI_Balancer.lua index 9b704e8a1..f82631dab 100644 --- a/Moose Development/Moose/AI/AI_Balancer.lua +++ b/Moose Development/Moose/AI/AI_Balancer.lua @@ -27,10 +27,8 @@ -- @extends Core.Fsm#FSM_SET ---- # AI_BALANCER class, extends @{Core.Fsm#FSM_SET} --- --- The AI_BALANCER class monitors and manages as many replacement AI groups as there are --- CLIENTS in a SET_CLIENT collection, which are not occupied by human players. +--- Monitors and manages as many replacement AI groups as there are +-- CLIENTS in a SET\_CLIENT collection, which are not occupied by human players. -- In other words, use AI_BALANCER to simulate human behaviour by spawning in replacement AI in multi player missions. -- -- The parent class @{Fsm#FSM_SET} manages the functionality to control the Finite State Machine (FSM). diff --git a/Moose Development/Moose/AI/AI_CAP.lua b/Moose Development/Moose/AI/AI_CAP.lua index 19d51ca81..f6b42f37b 100644 --- a/Moose Development/Moose/AI/AI_CAP.lua +++ b/Moose Development/Moose/AI/AI_CAP.lua @@ -31,9 +31,7 @@ -- @extends AI.AI_Patrol#AI_PATROL_ZONE ---- # AI_CAP_ZONE class, extends @{AI.AI_Patrol#AI_PATROL_ZONE} --- --- The AI_CAP_ZONE class implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Controllable} or @{Wrapper.Group} +-- Implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Controllable} or @{Wrapper.Group} -- and automatically engage any airborne enemies that are within a certain range or within a certain zone. -- -- ![Process](..\Presentations\AI_CAP\Dia3.JPG) diff --git a/Moose Development/Moose/AI/AI_CAS.lua b/Moose Development/Moose/AI/AI_CAS.lua index bb70b61df..2ef978853 100644 --- a/Moose Development/Moose/AI/AI_CAS.lua +++ b/Moose Development/Moose/AI/AI_CAS.lua @@ -28,11 +28,7 @@ -- @field Core.Zone#ZONE_BASE TargetZone The @{Zone} where the patrol needs to be executed. -- @extends AI.AI_Patrol#AI_PATROL_ZONE ---- # AI_CAS_ZONE class, extends @{AI.AI_Patrol#AI_PATROL_ZONE} --- --- AI_CAS_ZONE derives from the @{AI.AI_Patrol#AI_PATROL_ZONE}, inheriting its methods and behaviour. --- --- The AI_CAS_ZONE class implements the core functions to provide Close Air Support in an Engage @{Zone} by an AIR @{Wrapper.Controllable} or @{Wrapper.Group}. +--- Implements the core functions to provide Close Air Support in an Engage @{Zone} by an AIR @{Wrapper.Controllable} or @{Wrapper.Group}. -- The AI_CAS_ZONE runs a process. It holds an AI in a Patrol Zone and when the AI is commanded to engage, it will fly to an Engage Zone. -- -- ![HoldAndEngage](..\Presentations\AI_CAS\Dia3.JPG) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index c4863e159..a2d086683 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -13,11 +13,7 @@ -- @extends Core.Fsm#FSM_CONTROLLABLE ---- # AI\_CARGO\_APC class, extends @{Core.Fsm#FSM_CONTROLLABLE} --- --- === --- --- AI\_CARGO\APC brings a dynamic cargo handling capability for AI groups. +--- Brings a dynamic cargo handling capability for AI groups. -- -- Armoured Personnel Carriers (APC), Trucks, Jeeps and other ground based carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. -- The AI\_CARGO\APC module uses the @{Cargo} capabilities within the MOOSE framework. diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index ed10d2e5d..343823d2f 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -13,9 +13,7 @@ -- @extends Core.Fsm#FSM_CONTROLLABLE ---- # AI\_CARGO\_AIRPLANE class, extends @{Core.Fsm#FSM_CONTROLLABLE} --- --- === +--- Implements the transportation of cargo by airplanes. -- -- @field #AI_CARGO_AIRPLANE AI_CARGO_AIRPLANE = { diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua index 409699733..c208bab9a 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua @@ -13,11 +13,7 @@ -- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER ---- # AI\_CARGO\_DISPATCHER\_AIRPLANE class, extends @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} --- --- === --- --- AI\_CARGO\_DISPATCHER\_AIRPLANE brings a dynamic cargo handling capability for AI groups. +--- Brings a dynamic cargo handling capability for AI groups. -- -- Airplanes can be mobilized to intelligently transport infantry and other cargo within the simulation. -- The AI\_CARGO\_DISPATCHER\_AIRPLANE module uses the @{Cargo} capabilities within the MOOSE framework. diff --git a/Moose Development/Moose/AI/AI_Formation.lua b/Moose Development/Moose/AI/AI_Formation.lua index ebc5d2b6f..be8abc9e9 100644 --- a/Moose Development/Moose/AI/AI_Formation.lua +++ b/Moose Development/Moose/AI/AI_Formation.lua @@ -58,9 +58,7 @@ -- @field DCSTypes#AI.Option.Air.val.REACTION_ON_THREAT OptionReactionOnThreat Which REACTION_ON_THREAT is set to the FollowGroup. ---- # AI_FORMATION class, extends @{Core.Fsm#FSM_SET} --- --- The #AI_FORMATION class allows you to build large formations, make AI follow a @{Wrapper.Client#CLIENT} (player) leader or a @{Unit#UNIT} (AI) leader. +--- Build large formations, make AI follow a @{Wrapper.Client#CLIENT} (player) leader or a @{Wrapper.Unit#UNIT} (AI) leader. -- -- AI_FORMATION makes AI @{GROUP}s fly in formation of various compositions. -- The AI_FORMATION class models formations in a different manner than the internal DCS formation logic!!! @@ -86,7 +84,7 @@ -- -- Create a new SPAWN object with the @{#AI_FORMATION.New} method: -- --- * @{Follow#AI_FORMATION.New}(): Creates a new AI_FORMATION object from a @{Wrapper.Group#GROUP} for a @{Wrapper.Client#CLIENT} or a @{Unit#UNIT}, with an optional briefing text. +-- * @{Follow#AI_FORMATION.New}(): Creates a new AI_FORMATION object from a @{Wrapper.Group#GROUP} for a @{Wrapper.Client#CLIENT} or a @{Wrapper.Unit#UNIT}, with an optional briefing text. -- -- ## Formation methods -- diff --git a/Moose Development/Moose/AI/AI_Patrol.lua b/Moose Development/Moose/AI/AI_Patrol.lua index 1553be007..c89ab36e0 100644 --- a/Moose Development/Moose/AI/AI_Patrol.lua +++ b/Moose Development/Moose/AI/AI_Patrol.lua @@ -40,9 +40,7 @@ -- @field Core.Spawn#SPAWN CoordTest -- @extends Core.Fsm#FSM_CONTROLLABLE ---- # AI_PATROL_ZONE class, extends @{Core.Fsm#FSM_CONTROLLABLE} --- --- The AI_PATROL_ZONE class implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Controllable} or @{Wrapper.Group}. +--- Implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Controllable} or @{Wrapper.Group}. -- -- ![Process](..\Presentations\AI_PATROL\Dia3.JPG) -- @@ -558,18 +556,18 @@ function AI_PATROL_ZONE:SetDetectionZone( DetectionZone ) end end ---- Gets a list of @{Unit#UNIT}s that were detected by the AI. +--- Gets a list of @{Wrapper.Unit#UNIT}s that were detected by the AI. -- No filtering is applied, so, ANY detected UNIT can be in this list. -- It is up to the mission designer to use the @{Wrapper.Unit} class and methods to filter the targets. -- @param #AI_PATROL_ZONE self --- @return #table The list of @{Unit#UNIT}s +-- @return #table The list of @{Wrapper.Unit#UNIT}s function AI_PATROL_ZONE:GetDetectedUnits() self:F2() return self.DetectedUnits end ---- Clears the list of @{Unit#UNIT}s that were detected by the AI. +--- Clears the list of @{Wrapper.Unit#UNIT}s that were detected by the AI. -- @param #AI_PATROL_ZONE self function AI_PATROL_ZONE:ClearDetectedUnits() self:F2() diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 31fa1644e..f5570dd18 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -144,9 +144,8 @@ do -- CARGO -- @field #boolean Representable This flag defines if the cargo can be represented by a DCS Unit. -- @field #boolean Containable This flag defines if the cargo can be contained within a DCS Unit. - --- # (R2.4) CARGO class, extends @{Core.Fsm#FSM_PROCESS} + --- Defines the core functions that defines a cargo object within MOOSE. -- - -- The CARGO class defines the core functions that defines a cargo object within MOOSE. -- A cargo is a **logical object** defined that is available for transport, and has a life status within a simulation. -- -- CARGO is not meant to be used directly by mission designers, but provides a base class for **concrete cargo implementation classes** to handle: diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua index c3e12ddea..399c29ffb 100644 --- a/Moose Development/Moose/Cargo/CargoCrate.lua +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -22,9 +22,7 @@ do -- CARGO_CRATE -- @type CARGO_CRATE -- @extends Cargo.Cargo#CARGO_REPRESENTABLE - --- # CARGO\_CRATE class, extends @{Cargo.Cargo#CARGO_REPRESENTABLE} - -- - -- The CARGO\_CRATE class defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. + --- Defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. -- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO\_CRATE objects to and from carriers. -- -- === diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 98435b25c..de8b08e6d 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -28,9 +28,7 @@ do -- CARGO_GROUP -- @field Core.Set#SET_CARGO CargoSet The collection of derived CARGO objects. -- @field #string GroupName The name of the CargoGroup. - --- # CARGO\_GROUP class, extends @{Cargo.Cargo#CARGO_REPORTABLE} - -- - -- The CARGO\_GROUP class defines a cargo that is represented by a @{Wrapper.Group} object within the simulator. + --- Defines a cargo that is represented by a @{Wrapper.Group} object within the simulator. -- The cargo can be Loaded, UnLoaded, Boarded, UnBoarded to and from Carriers. -- -- The above cargo classes are used by the AI\_CARGO\_ classes to allow AI groups to transport cargo: diff --git a/Moose Development/Moose/Cargo/CargoSlingload.lua b/Moose Development/Moose/Cargo/CargoSlingload.lua index 617cc4770..ff948d4e6 100644 --- a/Moose Development/Moose/Cargo/CargoSlingload.lua +++ b/Moose Development/Moose/Cargo/CargoSlingload.lua @@ -23,9 +23,7 @@ do -- CARGO_SLINGLOAD -- @type CARGO_SLINGLOAD -- @extends Cargo.Cargo#CARGO_REPRESENTABLE - --- # CARGO\_CRATE class, extends @{Cargo.Cargo#CARGO_REPRESENTABLE} - -- - -- The CARGO\_CRATE class defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. + --- Defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. -- -- === -- diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index 09fd9f0ce..2a5f009ac 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -22,9 +22,7 @@ do -- CARGO_UNIT -- @type CARGO_UNIT -- @extends Cargo.Cargo#CARGO_REPRESENTABLE - --- # CARGO\_UNIT class, extends @{Cargo.Cargo#CARGO_REPRESENTABLEE} - -- - -- The CARGO\_UNIT class defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. + --- Defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. -- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO\_UNIT objects to and from carriers. -- -- === diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 37e792279..82e195f82 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -14,7 +14,9 @@ --- @type DATABASE -- @extends Core.Base#BASE ---- Mission designers can use the DATABASE class to refer to: +--- Contains collections of wrapper objects defined within MOOSE that reflect objects within the simulator. +-- +-- Mission designers can use the DATABASE class to refer to: -- -- * STATICS -- * UNITS diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 3f1b1cd57..f61e0ca40 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -167,6 +167,7 @@ --- The EVENT structure +-- -- @type EVENT -- @field #EVENT.Events Events -- @extends Core.Base#BASE @@ -230,7 +231,7 @@ EVENTS = { -- @field DCS#Object.Category IniObjectCategory (UNIT/STATIC/SCENERY) The initiator object category ( Object.Category.UNIT or Object.Category.STATIC ). -- @field DCS#Unit IniDCSUnit (UNIT/STATIC) The initiating @{DCS#Unit} or @{DCSStaticObject#StaticObject}. -- @field #string IniDCSUnitName (UNIT/STATIC) The initiating Unit name. --- @field Wrapper.Unit#UNIT IniUnit (UNIT/STATIC) The initiating MOOSE wrapper @{Unit#UNIT} of the initiator Unit object. +-- @field Wrapper.Unit#UNIT IniUnit (UNIT/STATIC) The initiating MOOSE wrapper @{Wrapper.Unit#UNIT} of the initiator Unit object. -- @field #string IniUnitName (UNIT/STATIC) The initiating UNIT name (same as IniDCSUnitName). -- @field DCS#Group IniDCSGroup (UNIT) The initiating {DCSGroup#Group}. -- @field #string IniDCSGroupName (UNIT) The initiating Group name. @@ -245,7 +246,7 @@ EVENTS = { -- @field DCS#Object.Category TgtObjectCategory (UNIT/STATIC) The target object category ( Object.Category.UNIT or Object.Category.STATIC ). -- @field DCS#Unit TgtDCSUnit (UNIT/STATIC) The target @{DCS#Unit} or @{DCSStaticObject#StaticObject}. -- @field #string TgtDCSUnitName (UNIT/STATIC) The target Unit name. --- @field Wrapper.Unit#UNIT TgtUnit (UNIT/STATIC) The target MOOSE wrapper @{Unit#UNIT} of the target Unit object. +-- @field Wrapper.Unit#UNIT TgtUnit (UNIT/STATIC) The target MOOSE wrapper @{Wrapper.Unit#UNIT} of the target Unit object. -- @field #string TgtUnitName (UNIT/STATIC) The target UNIT name (same as TgtDCSUnitName). -- @field DCS#Group TgtDCSGroup (UNIT) The target {DCSGroup#Group}. -- @field #string TgtDCSGroupName (UNIT) The target Group name. diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index b92beb1c1..a4a833da2 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -801,7 +801,7 @@ do -- FSM_CONTROLLABLE -- @field Wrapper.Controllable#CONTROLLABLE Controllable -- @extends Core.Fsm#FSM - --- FSM_CONTROLLABLE class models Finite State Machines for @{Wrapper.Controllable}s, which are @{Wrapper.Group}s, @{Wrapper.Unit}s, @{Client}s. + --- Models Finite State Machines for @{Wrapper.Controllable}s, which are @{Wrapper.Group}s, @{Wrapper.Unit}s, @{Client}s. -- -- === -- @@ -1177,7 +1177,7 @@ do -- FSM_TASK -- @field Tasking.Task#TASK Task -- @extends #FSM - --- FSM_TASK class models Finite State Machines for @{Task}s. + --- Models Finite State Machines for @{Tasking.Task}s. -- -- === -- diff --git a/Moose Development/Moose/Core/Goal.lua b/Moose Development/Moose/Core/Goal.lua index 608c4ecc0..926e8eca1 100644 --- a/Moose Development/Moose/Core/Goal.lua +++ b/Moose Development/Moose/Core/Goal.lua @@ -19,7 +19,7 @@ do -- Goal -- @extends Core.Fsm#FSM - --- GOAL models processes that have an objective with a defined achievement. Derived classes implement the ways how the achievements can be realized. + --- Models processes that have an objective with a defined achievement. Derived classes implement the ways how the achievements can be realized. -- -- ## 1. GOAL constructor -- diff --git a/Moose Development/Moose/Core/Menu.lua b/Moose Development/Moose/Core/Menu.lua index 03d95c47f..fd677b5c5 100644 --- a/Moose Development/Moose/Core/Menu.lua +++ b/Moose Development/Moose/Core/Menu.lua @@ -13,15 +13,15 @@ -- -- ### To manage **main menus**, the classes begin with **MENU_**: -- --- * @{Menu#MENU_MISSION}: Manages main menus for whole mission file. --- * @{Menu#MENU_COALITION}: Manages main menus for whole coalition. --- * @{Menu#MENU_GROUP}: Manages main menus for GROUPs. +-- * @{Core.Menu#MENU_MISSION}: Manages main menus for whole mission file. +-- * @{Core.Menu#MENU_COALITION}: Manages main menus for whole coalition. +-- * @{Core.Menu#MENU_GROUP}: Manages main menus for GROUPs. -- -- ### To manage **command menus**, which are menus that allow the player to issue **functions**, the classes begin with **MENU_COMMAND_**: -- --- * @{Menu#MENU_MISSION_COMMAND}: Manages command menus for whole mission file. --- * @{Menu#MENU_COALITION_COMMAND}: Manages command menus for whole coalition. --- * @{Menu#MENU_GROUP_COMMAND}: Manages command menus for GROUPs. +-- * @{Core.Menu#MENU_MISSION_COMMAND}: Manages command menus for whole mission file. +-- * @{Core.Menu#MENU_COALITION_COMMAND}: Manages command menus for whole coalition. +-- * @{Core.Menu#MENU_GROUP_COMMAND}: Manages command menus for GROUPs. -- -- === --- @@ -183,7 +183,7 @@ do -- MENU_BASE --- @type MENU_BASE -- @extends Base#BASE - --- The MENU_BASE class defines the main MENU class where other MENU classes are derived from. + --- Defines the main MENU class where other MENU classes are derived from. -- This is an abstract class, so don't use it. -- @field #MENU_BASE MENU_BASE = { @@ -286,7 +286,7 @@ do -- MENU_COMMAND_BASE -- @field #function MenuCallHandler -- @extends Core.Menu#MENU_BASE - --- The MENU_COMMAND_BASE class defines the main MENU class where other MENU COMMAND_ + --- Defines the main MENU class where other MENU COMMAND_ -- classes are derived from, in order to set commands. -- -- @field #MENU_COMMAND_BASE @@ -356,7 +356,8 @@ do -- MENU_MISSION --- @type MENU_MISSION -- @extends Core.Menu#MENU_BASE - --- The MENU_MISSION class manages the main menus for a complete mission. + --- Manages the main menus for a complete mission. + -- -- You can add menus with the @{#MENU_MISSION.New} method, which constructs a MENU_MISSION object and returns you the object reference. -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION.Remove}. -- @field #MENU_MISSION @@ -451,7 +452,8 @@ do -- MENU_MISSION_COMMAND --- @type MENU_MISSION_COMMAND -- @extends Core.Menu#MENU_COMMAND_BASE - --- The MENU_MISSION_COMMAND class manages the command menus for a complete mission, which allow players to execute functions during mission execution. + --- Manages the command menus for a complete mission, which allow players to execute functions during mission execution. + -- -- You can add menus with the @{#MENU_MISSION_COMMAND.New} method, which constructs a MENU_MISSION_COMMAND object and returns you the object reference. -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_MISSION_COMMAND.Remove}. -- @@ -536,7 +538,8 @@ do -- MENU_COALITION --- @type MENU_COALITION -- @extends Core.Menu#MENU_BASE - --- The @{Menu#MENU_COALITION} class manages the main menus for coalitions. + --- Manages the main menus for @{DCS.coalition}s. + -- -- You can add menus with the @{#MENU_COALITION.New} method, which constructs a MENU_COALITION object and returns you the object reference. -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_COALITION.Remove}. -- @@ -672,7 +675,8 @@ do -- MENU_COALITION_COMMAND --- @type MENU_COALITION_COMMAND -- @extends Core.Menu#MENU_COMMAND_BASE - --- The MENU_COALITION_COMMAND class manages the command menus for coalitions, which allow players to execute functions during mission execution. + --- Manages the command menus for coalitions, which allow players to execute functions during mission execution. + -- -- You can add menus with the @{#MENU_COALITION_COMMAND.New} method, which constructs a MENU_COALITION_COMMAND object and returns you the object reference. -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_COALITION_COMMAND.Remove}. -- @@ -770,7 +774,8 @@ do -- @extends Core.Menu#MENU_BASE - --- The MENU_GROUP class manages the main menus for coalitions. + --- Manages the main menus for @{Wrapper.Group}s. + -- -- You can add menus with the @{#MENU_GROUP.New} method, which constructs a MENU_GROUP object and returns you the object reference. -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP.Remove}. -- @@ -926,7 +931,7 @@ do --- @type MENU_GROUP_COMMAND -- @extends Core.Menu#MENU_COMMAND_BASE - --- The @{Menu#MENU_GROUP_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution. + --- The @{Core.Menu#MENU_GROUP_COMMAND} class manages the command menus for coalitions, which allow players to execute functions during mission execution. -- You can add menus with the @{#MENU_GROUP_COMMAND.New} method, which constructs a MENU_GROUP_COMMAND object and returns you the object reference. -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP_COMMAND.Remove}. -- @@ -1155,7 +1160,8 @@ do --- @type MENU_GROUP_COMMAND_DELAYED -- @extends Core.Menu#MENU_COMMAND_BASE - --- The @{Menu#MENU_GROUP_COMMAND_DELAYED} class manages the command menus for coalitions, which allow players to execute functions during mission execution. + --- Manages the command menus for coalitions, which allow players to execute functions during mission execution. + -- -- You can add menus with the @{#MENU_GROUP_COMMAND_DELAYED.New} method, which constructs a MENU_GROUP_COMMAND_DELAYED object and returns you the object reference. -- Using this object reference, you can then remove ALL the menus and submenus underlying automatically with @{#MENU_GROUP_COMMAND_DELAYED.Remove}. -- diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index be99b5d75..632c1007c 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -36,7 +36,7 @@ do -- COORDINATE -- @extends Core.Base#BASE - --- COORDINATE defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space. + --- Defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space. -- -- ## COORDINATE constructor -- @@ -1597,7 +1597,7 @@ do -- POINT_VEC3 -- @extends Core.Point#COORDINATE - --- POINT_VEC3 defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space. + --- Defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space. -- -- **Important Note:** Most of the functions in this section were taken from MIST, and reworked to OO concepts. -- In order to keep the credibility of the the author, @@ -1808,7 +1808,7 @@ do -- POINT_VEC2 -- @field DCS#Distance y the y coordinate in meters. -- @extends Core.Point#COORDINATE - --- The @{Point#POINT_VEC2} class defines a 2D point in the simulator. The height coordinate (if needed) will be the land height + an optional added height specified. + --- Defines a 2D point in the simulator. The height coordinate (if needed) will be the land height + an optional added height specified. -- -- ## POINT_VEC2 constructor -- diff --git a/Moose Development/Moose/Core/Radio.lua b/Moose Development/Moose/Core/Radio.lua index 1e1d96aaf..85dc9162a 100644 --- a/Moose Development/Moose/Core/Radio.lua +++ b/Moose Development/Moose/Core/Radio.lua @@ -16,9 +16,9 @@ -- * They need to be added in .\l10n\DEFAULT\ in you .miz file (wich can be decompressed like a .zip file), -- * For simplicty sake, you can **let DCS' Mission Editor add the file** itself, by creating a new Trigger with the action "Sound to Country", and choosing your sound file and a country you don't use in your mission. -- --- Due to weird DCS quirks, **radio communications behave differently** if sent by a @{Unit#UNIT} or a @{Wrapper.Group#GROUP} or by any other @{Positionable#POSITIONABLE} +-- Due to weird DCS quirks, **radio communications behave differently** if sent by a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP} or by any other @{Positionable#POSITIONABLE} -- --- * If the transmitter is a @{Unit#UNIT} or a @{Wrapper.Group#GROUP}, DCS will set the power of the transmission automatically, +-- * If the transmitter is a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}, DCS will set the power of the transmission automatically, -- * If the transmitter is any other @{Positionable#POSITIONABLE}, the transmisison can't be subtitled or looped. -- -- Note that obviously, the **frequency** and the **modulation** of the transmission are important only if the players are piloting an **Advanced System Modelling** enabled aircraft, @@ -34,7 +34,7 @@ -- @image Core_Radio.JPG ---- # RADIO class, extends @{Core.Base#BASE} +--- Models the radio capabilty. -- -- ## RADIO usage -- @@ -44,14 +44,14 @@ -- * Then, you will **set the relevant parameters** to the transmission (see below), -- * When done, you can actually **broadcast the transmission** (i.e. play the sound) with the @{RADIO.Broadcast}() function. -- --- Methods to set relevant parameters for both a @{Unit#UNIT} or a @{Wrapper.Group#GROUP} or any other @{Positionable#POSITIONABLE} +-- Methods to set relevant parameters for both a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP} or any other @{Positionable#POSITIONABLE} -- -- * @{#RADIO.SetFileName}() : Sets the file name of your sound file (e.g. "Noise.ogg"), -- * @{#RADIO.SetFrequency}() : Sets the frequency of your transmission. -- * @{#RADIO.SetModulation}() : Sets the modulation of your transmission. -- * @{#RADIO.SetLoop}() : Choose if you want the transmission to be looped. If you need your transmission to be looped, you might need a @{#BEACON} instead... -- --- Additional Methods to set relevant parameters if the transmiter is a @{Unit#UNIT} or a @{Wrapper.Group#GROUP} +-- Additional Methods to set relevant parameters if the transmiter is a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP} -- -- * @{#RADIO.SetSubtitle}() : Set both the subtitle and its duration, -- * @{#RADIO.NewUnitTransmission}() : Shortcut to set all the relevant parameters in one method call @@ -63,7 +63,7 @@ -- -- What is this power thing ? -- --- * If your transmission is sent by a @{Positionable#POSITIONABLE} other than a @{Unit#UNIT} or a @{Wrapper.Group#GROUP}, you can set the power of the antenna, +-- * If your transmission is sent by a @{Positionable#POSITIONABLE} other than a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}, you can set the power of the antenna, -- * Otherwise, DCS sets it automatically, depending on what's available on your Unit, -- * If the player gets **too far** from the transmiter, or if the antenna is **too weak**, the transmission will **fade** and **become noisyer**, -- * This an automated DCS calculation you have no say on, @@ -338,16 +338,14 @@ function RADIO:StopBroadcast() end ---- # BEACON class, extends @{Core.Base#BASE} --- --- After attaching a @{#BEACON} to your @{Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want. +--- After attaching a @{#BEACON} to your @{Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want. -- There are two types of BEACONs available : the AA TACAN Beacon and the general purpose Radio Beacon. -- Note that in both case, you can set an optional parameter : the `BeaconDuration`. This can be very usefull to simulate the battery time if your BEACON is -- attach to a cargo crate, for exemple. -- -- ## AA TACAN Beacon usage -- --- This beacon only works with airborne @{Unit#UNIT} or a @{Wrapper.Group#GROUP}. Use @{#BEACON:AATACAN}() to set the beacon parameters and start the beacon. +-- This beacon only works with airborne @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}. Use @{#BEACON:AATACAN}() to set the beacon parameters and start the beacon. -- Use @#BEACON:StopAATACAN}() to stop it. -- -- ## General Purpose Radio Beacon usage diff --git a/Moose Development/Moose/Core/Scheduler.lua b/Moose Development/Moose/Core/Scheduler.lua index 734c31c01..b8a75c5b8 100644 --- a/Moose Development/Moose/Core/Scheduler.lua +++ b/Moose Development/Moose/Core/Scheduler.lua @@ -46,7 +46,7 @@ -- @extends Core.Base#BASE ---- The SCHEDULER class creates schedule. +--- Creates and handles schedules over time, which allow to execute code at specific time intervals with randomization. -- -- A SCHEDULER can manage **multiple** (repeating) schedules. Each planned or executing schedule has a unique **ScheduleID**. -- The ScheduleID is returned when the method @{#SCHEDULER.Schedule}() is called. diff --git a/Moose Development/Moose/Core/Settings.lua b/Moose Development/Moose/Core/Settings.lua index 77de7dac2..819690607 100644 --- a/Moose Development/Moose/Core/Settings.lua +++ b/Moose Development/Moose/Core/Settings.lua @@ -21,7 +21,7 @@ --- @type SETTINGS -- @extends Core.Base#BASE ---- The SETTINGS class takes care of various settings that influence the behaviour of certain functionalities and classes within the MOOSE framework. +--- Takes care of various settings that influence the behaviour of certain functionalities and classes within the MOOSE framework. -- -- === -- diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 94c3b5564..7cf879f08 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -36,7 +36,8 @@ -- @extends Core.Base#BASE ---- The SPAWN class allows to spawn dynamically new groups. +--- Allows to spawn dynamically new @{Core.Group}s. +-- -- Each SPAWN object needs to be have related **template groups** setup in the Mission Editor (ME), -- which is a normal group with the **Late Activation** flag set. -- This template group will never be activated in your mission. diff --git a/Moose Development/Moose/Core/SpawnStatic.lua b/Moose Development/Moose/Core/SpawnStatic.lua index 52df548da..334201e4c 100644 --- a/Moose Development/Moose/Core/SpawnStatic.lua +++ b/Moose Development/Moose/Core/SpawnStatic.lua @@ -36,7 +36,7 @@ -- @extends Core.Base#BASE ---- The SPAWNSTATIC class allows to spawn dynamically new @{Static}s. +--- Allows to spawn dynamically new @{Static}s. -- Through creating a copy of an existing static object template as defined in the Mission Editor (ME), -- SPAWNSTATIC can retireve the properties of the defined static object template (like type, category etc), and "copy" -- these properties to create a new static object and place it at the desired coordinate. diff --git a/Moose Development/Moose/Core/Spot.lua b/Moose Development/Moose/Core/Spot.lua index 0af7a7b02..4e17118f5 100644 --- a/Moose Development/Moose/Core/Spot.lua +++ b/Moose Development/Moose/Core/Spot.lua @@ -46,7 +46,7 @@ do -- @extends Core.Fsm#FSM - --- SPOT implements the DCS Spot class functionality, but adds additional luxury to be able to: + --- Implements the target spotting or marking functionality, but adds additional luxury to be able to: -- -- * Mark targets for a defined duration. -- * wiggle the spot at the target. diff --git a/Moose Development/Moose/Core/Velocity.lua b/Moose Development/Moose/Core/Velocity.lua index 04e4675bb..7e7a368f3 100644 --- a/Moose Development/Moose/Core/Velocity.lua +++ b/Moose Development/Moose/Core/Velocity.lua @@ -16,7 +16,7 @@ do -- Velocity -- @extends Core.Base#BASE - -- VELOCITY models a speed, which can be expressed in various formats according the Settings. + --- VELOCITY models a speed, which can be expressed in various formats according the Settings. -- -- ## VELOCITY constructor -- diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 3697c6de2..55a1a0412 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -21,7 +21,7 @@ -- * @{#ZONE_BASE}: The ZONE_BASE class defining the base for all other zone classes. -- * @{#ZONE_RADIUS}: The ZONE_RADIUS class defined by a zone name, a location and a radius. -- * @{#ZONE}: The ZONE class, defined by the zone name as defined within the Mission Editor. --- * @{#ZONE_UNIT}: The ZONE_UNIT class defines by a zone around a @{Unit#UNIT} with a radius. +-- * @{#ZONE_UNIT}: The ZONE_UNIT class defines by a zone around a @{Wrapper.Unit#UNIT} with a radius. -- * @{#ZONE_GROUP}: The ZONE_GROUP class defines by a zone around a @{Wrapper.Group#GROUP} with a radius. -- * @{#ZONE_POLYGON}: The ZONE_POLYGON class defines by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon. -- @@ -1017,7 +1017,7 @@ end -- @field Wrapper.Unit#UNIT ZoneUNIT -- @extends Core.Zone#ZONE_RADIUS ---- The ZONE_UNIT class defined by a zone around a @{Unit#UNIT} with a radius. +--- The ZONE_UNIT class defined by a zone around a @{Wrapper.Unit#UNIT} with a radius. -- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. -- -- @field #ZONE_UNIT @@ -1045,9 +1045,9 @@ function ZONE_UNIT:New( ZoneName, ZoneUNIT, Radius ) end ---- Returns the current location of the @{Unit#UNIT}. +--- Returns the current location of the @{Wrapper.Unit#UNIT}. -- @param #ZONE_UNIT self --- @return DCS#Vec2 The location of the zone based on the @{Unit#UNIT}location. +-- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Unit#UNIT}location. function ZONE_UNIT:GetVec2() self:F2( self.ZoneName ) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index f8cfbf327..33b4c9331 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -1802,9 +1802,8 @@ end do -- DETECTION_UNITS - --- # DETECTION_UNITS class, extends @{Functional.Detection#DETECTION_BASE} + --- Will detect units within the battle zone. -- - -- The DETECTION_UNITS class will detect units within the battle zone. -- It will build a DetectedItems list filled with DetectedItems. Each DetectedItem will contain a field Set, which contains a @{Core.Set#SET_UNIT} containing ONE @{UNIT} object reference. -- Beware that when the amount of units detected is large, the DetectedItems list will be large also. -- @@ -2052,9 +2051,7 @@ end do -- DETECTION_TYPES - --- # 3) DETECTION_TYPES class, extends @{Functional.Detection#DETECTION_BASE} - -- - -- The DETECTION_TYPES class will detect units within the battle zone. + --- Will detect units within the battle zone. -- It will build a DetectedItems[] list filled with DetectedItems, grouped by the type of units detected. -- Each DetectedItem will contain a field Set, which contains a @{Core.Set#SET_UNIT} containing ONE @{UNIT} object reference. -- Beware that when the amount of different types detected is large, the DetectedItems[] list will be large also. @@ -2261,10 +2258,8 @@ end do -- DETECTION_AREAS - --- # 4) DETECTION_AREAS class, extends @{Functional.Detection#DETECTION_BASE} - -- - -- The DETECTION_AREAS class will detect units within the battle zone for a list of @{Wrapper.Group}s detecting targets following (a) detection method(s), - -- and will build a list (table) of @{Core.Set#SET_UNIT}s containing the @{Unit#UNIT}s detected. + --- Detect units within the battle zone for a list of @{Wrapper.Group}s detecting targets following (a) detection method(s), + -- and will build a list (table) of @{Core.Set#SET_UNIT}s containing the @{Wrapper.Unit#UNIT}s detected. -- The class is group the detected units within zones given a DetectedZoneRange parameter. -- A set with multiple detected zones will be created as there are groups of units detected. -- diff --git a/Moose Development/Moose/Functional/PseudoATC.lua b/Moose Development/Moose/Functional/PseudoATC.lua index b8a9f2cd0..a6b42d68f 100644 --- a/Moose Development/Moose/Functional/PseudoATC.lua +++ b/Moose Development/Moose/Functional/PseudoATC.lua @@ -53,7 +53,7 @@ -- @field #boolean eventsmoose If true, events are handled by MOOSE. If false, events are handled directly by DCS eventhandler. -- @extends Core.Base#BASE ---- The PSEUDOATC class adds some rudimentary ATC functionality via the radio menu. +--- Adds some rudimentary ATC functionality via the radio menu. -- -- Local weather reports can be requested for nearby airports and player's mission waypoints. -- The weather report includes @@ -986,7 +986,7 @@ function PSEUDOATC:_DisplayMessageToGroup(_unit, _text, _time, _clear) end --- Returns a string which consits of this callsign and the player name. --- @param #RANGE self +-- @param #PSEUDOATC self -- @param #string unitname Name of the player unit. function PSEUDOATC:_myname(unitname) self:F2(unitname) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 610c67b17..53b4d67f6 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -148,8 +148,7 @@ -- @field #boolean useparkingdb Parking spots are added to data base once an aircraft has used it. These spots can later be used by other aircraft. Default is true. -- @extends Core.Spawn#SPAWN ---- The RAT class implements an easy to use way to randomly fill your map with AI aircraft. --- +--- Implements an easy to use way to randomly fill your map with AI aircraft. -- -- ## Airport Selection -- diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index a7448cecd..1e39d0735 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -83,7 +83,7 @@ -- @field #boolean trackmissiles If true (default), all missile types are tracked and impact point to closest bombing target is evaluated. -- @extends Core.Base#BASE ---- The RANGE class enables a mission designer to easily set up practice ranges in DCS. A new RANGE object can be created with the @{#RANGE.New}(rangename) contructor. +--- Enables a mission designer to easily set up practice ranges in DCS. A new RANGE object can be created with the @{#RANGE.New}(rangename) contructor. -- The parameter "rangename" defindes the name of the range. It has to be unique since this is also the name displayed in the radio menu. -- -- Generally, a range consists of strafe pits and bombing targets. For strafe pits the number of hits for each pass is counted and tabulated. diff --git a/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua b/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua index b4551e87f..767f2e634 100644 --- a/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua +++ b/Moose Development/Moose/Functional/ZoneCaptureCoalition.lua @@ -26,9 +26,7 @@ do -- ZONE_CAPTURE_COALITION -- @extends Functional.ZoneGoalCoalition#ZONE_GOAL_COALITION - --- # ZONE\_CAPTURE\_COALITION class, extends @{ZoneGoalCoalition#ZONE_GOAL_COALITION} - -- - -- Models the process to capture a Zone for a Coalition, which is guarded by another Coalition. + --- Models the process to capture a Zone for a Coalition, which is guarded by another Coalition. -- This is a powerful concept that allows to create very dynamic missions based on the different state transitions of various zones. -- -- --- diff --git a/Moose Development/Moose/Functional/ZoneGoal.lua b/Moose Development/Moose/Functional/ZoneGoal.lua index fb610e7d9..0caabc8a3 100644 --- a/Moose Development/Moose/Functional/ZoneGoal.lua +++ b/Moose Development/Moose/Functional/ZoneGoal.lua @@ -20,9 +20,7 @@ do -- Zone -- @extends Core.Fsm#FSM - --- # ZONE_GOAL class, extends @{Fsm#FSM} - -- - -- ZONE_GOAL models processes that have a Goal with a defined achievement involving a Zone. + -- Models processes that have a Goal with a defined achievement involving a Zone. -- Derived classes implement the ways how the achievements can be realized. -- -- ## 1. ZONE_GOAL constructor diff --git a/Moose Development/Moose/Functional/ZoneGoalCargo.lua b/Moose Development/Moose/Functional/ZoneGoalCargo.lua index 48b492aaa..9ae8ae613 100644 --- a/Moose Development/Moose/Functional/ZoneGoalCargo.lua +++ b/Moose Development/Moose/Functional/ZoneGoalCargo.lua @@ -20,9 +20,7 @@ do -- ZoneGoal -- @extends Functional.ZoneGoal#ZONE_GOAL - --- # ZONE_GOAL_CARGO class, extends @{ZoneGoal#ZONE_GOAL} - -- - -- ZONE_GOAL_CARGO models processes that have a Goal with a defined achievement involving a Zone and Cargo. + --- Models processes that have a Goal with a defined achievement involving a Zone and Cargo. -- Derived classes implement the ways how the achievements can be realized. -- -- ## 1. ZONE_GOAL_CARGO constructor diff --git a/Moose Development/Moose/Functional/ZoneGoalCoalition.lua b/Moose Development/Moose/Functional/ZoneGoalCoalition.lua index cb66ad3bc..ba86ffe95 100644 --- a/Moose Development/Moose/Functional/ZoneGoalCoalition.lua +++ b/Moose Development/Moose/Functional/ZoneGoalCoalition.lua @@ -20,9 +20,7 @@ do -- ZoneGoal -- @extends Functional.ZoneGoal#ZONE_GOAL - --- # ZONE_GOAL_COALITION class, extends @{ZoneGoal#ZONE_GOAL} - -- - -- ZONE_GOAL_COALITION models processes that have a Goal with a defined achievement involving a Zone for a Coalition. + --- ZONE_GOAL_COALITION models processes that have a Goal with a defined achievement involving a Zone for a Coalition. -- Derived classes implement the ways how the achievements can be realized. -- -- ## 1. ZONE_GOAL_COALITION constructor diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 0a062eb1e..d31779479 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -15,7 +15,7 @@ --- @type AIRBASE -- @extends Wrapper.Positionable#POSITIONABLE ---- AIRBASE is a wrapper class to handle the DCS Airbase objects: +--- Wrapper class to handle the DCS Airbase objects: -- -- * Support all DCS Airbase APIs. -- * Enhance with Airbase specific APIs not in the DCS Airbase API set. diff --git a/Moose Development/Moose/Wrapper/Client.lua b/Moose Development/Moose/Wrapper/Client.lua index 09b082339..7352537f7 100644 --- a/Moose Development/Moose/Wrapper/Client.lua +++ b/Moose Development/Moose/Wrapper/Client.lua @@ -17,7 +17,8 @@ -- @extends Wrapper.Unit#UNIT ---- Clients are those **Units** defined within the Mission Editor that have the skillset defined as __Client__ or __Player__. +--- Wrapper class of those **Units** defined within the Mission Editor that have the skillset defined as __Client__ or __Player__. +-- -- Note that clients are NOT the same as Units, they are NOT necessarily alive. -- The CLIENT class is a wrapper class to handle the DCS Unit objects that have the skillset defined as __Client__ or __Player__: -- diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 1665f1b44..9e29ab41d 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -19,7 +19,7 @@ ---- CONTROLLABLE is a wrapper class to handle the "DCS Controllable objects", which are Groups and Units: +--- Wrapper class to handle the "DCS Controllable objects", which are Groups and Units: -- -- * Support all DCS Controllable APIs. -- * Enhance with Controllable specific APIs not in the DCS Controllable API set. @@ -421,7 +421,7 @@ end --- Return a Controlled Task taking a Task and a TaskCondition. -- @param #CONTROLLABLE self -- @param DCS#Task DCSTask --- @param #DCSStopCondition DCSStopCondition +-- @param DCS#DCSStopCondition DCSStopCondition -- @return DCS#Task function CONTROLLABLE:TaskControlled( DCSTask, DCSStopCondition ) self:F2( { DCSTask, DCSStopCondition } ) @@ -2096,7 +2096,7 @@ do -- Route methods -- A speed can be given in km/h. -- A given formation can be given. -- @param #CONTROLLABLE self - -- @param #Vec2 Vec2 The Vec2 where to route to. + -- @param DCS#Vec2 Vec2 The Vec2 where to route to. -- @param #number Speed The speed in m/s. Default is 5.555 m/s = 20 km/h. -- @param Base#FORMATION Formation The formation string. function CONTROLLABLE:TaskRouteToVec2( Vec2, Speed, Formation ) @@ -2152,7 +2152,7 @@ end -- Route methods --- Do Script command -- @param #CONTROLLABLE self -- @param #string DoScript --- @return #DCSCommand +-- @return DCS#DCSCommand function CONTROLLABLE:CommandDoScript( DoScript ) local DCSDoScript = { diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index dc8157cde..dd570df5e 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -32,7 +32,9 @@ -- @field #string GroupName The name of the group. ---- For each DCS Group object alive within a running mission, a GROUP wrapper object (instance) will be created within the _@{DATABASE} object. +--- Wrapper class of the DCS world Group object. +-- +-- For each DCS Group object alive within a running mission, a GROUP wrapper object (instance) will be created within the _@{DATABASE} object. -- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Group objects are spawned (using the @{SPAWN} class). -- -- The GROUP class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference diff --git a/Moose Development/Moose/Wrapper/Identifiable.lua b/Moose Development/Moose/Wrapper/Identifiable.lua index 671577882..6e226a466 100644 --- a/Moose Development/Moose/Wrapper/Identifiable.lua +++ b/Moose Development/Moose/Wrapper/Identifiable.lua @@ -15,7 +15,7 @@ -- @extends Wrapper.Object#OBJECT -- @field #string IdentifiableName The name of the identifiable. ---- The IDENTIFIABLE class is a wrapper class to handle the DCS Identifiable objects: +--- Wrapper class to handle the DCS Identifiable objects. -- -- * Support all DCS Identifiable APIs. -- * Enhance with Identifiable specific APIs not in the DCS Identifiable API set. diff --git a/Moose Development/Moose/Wrapper/Object.lua b/Moose Development/Moose/Wrapper/Object.lua index c218bf4c1..1b9b46995 100644 --- a/Moose Development/Moose/Wrapper/Object.lua +++ b/Moose Development/Moose/Wrapper/Object.lua @@ -17,7 +17,7 @@ -- @field #string ObjectName The name of the Object. ---- OBJECT handles the DCS Object objects: +--- Wrapper class to hendle the DCS Object objects. -- -- * Support all DCS Object APIs. -- * Enhance with Object specific APIs not in the DCS Object API set. diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 912467d20..b60ee07be 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -18,7 +18,7 @@ -- @extends Wrapper.Identifiable#IDENTIFIABLE ---- The POSITIONABLE class is a wrapper class to handle the POSITIONABLE objects: +--- Wrapper class to handle the POSITIONABLE objects. -- -- * Support all DCS APIs. -- * Enhance with POSITIONABLE specific APIs not in the DCS API set. @@ -716,7 +716,7 @@ end --- Create a @{Radio#RADIO}, to allow radio transmission for this POSITIONABLE. -- Set parameters with the methods provided, then use RADIO:Broadcast() to actually broadcast the message -- @param #POSITIONABLE self --- @return #RADIO Radio +-- @return Core.Radio#RADIO Radio function POSITIONABLE:GetRadio() --R2.1 self:F2(self) return RADIO:New(self) @@ -724,7 +724,7 @@ end --- Create a @{Radio#BEACON}, to allow this POSITIONABLE to broadcast beacon signals -- @param #POSITIONABLE self --- @return #RADIO Radio +-- @return Core.Radio#RADIO Radio function POSITIONABLE:GetBeacon() --R2.1 self:F2(self) return BEACON:New(self) diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index 31bfbf05a..9a9cb9ff2 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -17,7 +17,8 @@ -- @extends Wrapper.Positionable#POSITIONABLE ---- Scenery objects are defined on the map. +--- Wrapper class to handle Scenery objects that are defined on the map. +-- -- The @{Scenery#SCENERY} class is a wrapper class to handle the DCS Scenery objects: -- -- * Wraps the DCS Scenery objects. diff --git a/Moose Development/Moose/Wrapper/Static.lua b/Moose Development/Moose/Wrapper/Static.lua index a62aa11c1..27800cff0 100644 --- a/Moose Development/Moose/Wrapper/Static.lua +++ b/Moose Development/Moose/Wrapper/Static.lua @@ -15,7 +15,8 @@ --- @type STATIC -- @extends Wrapper.Positionable#POSITIONABLE ---- Statics are **Static Units** defined within the Mission Editor. +--- Wrapper class to handle Static objects. +-- -- Note that Statics are almost the same as Units, but they don't have a controller. -- The @{Static#STATIC} class is a wrapper class to handle the DCS Static objects: -- @@ -126,7 +127,7 @@ function STATIC:GetThreatLevel() end --- Respawn the @{Wrapper.Unit} using a (tweaked) template of the parent Group. --- @param #UNIT self +-- @param #STATIC self -- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static. -- @param #number Heading The heading of the unit respawn. function STATIC:SpawnAt( Coordinate, Heading ) @@ -139,7 +140,7 @@ end --- Respawn the @{Wrapper.Unit} at the same location with the same properties. -- This is useful to respawn a cargo after it has been destroyed. --- @param #UNIT self +-- @param #STATIC self function STATIC:ReSpawn() local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName ) @@ -149,7 +150,7 @@ end --- Respawn the @{Wrapper.Unit} at a defined Coordinate with an optional heading. --- @param #UNIT self +-- @param #STATIC self -- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static. -- @param #number Heading The heading of the unit respawn. function STATIC:ReSpawnAt( Coordinate, Heading ) From 0b959306743e1b86580111bfa1f28bfb366d9edc Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Fri, 1 Jun 2018 23:51:53 +0200 Subject: [PATCH 148/420] ARTY v0.9.8 Improved maker targets and moves assignments. Removed env.info() --- Moose Development/Moose/Core/Point.lua | 42 +- .../Moose/Functional/Artillery.lua | 437 ++++++++++++------ 2 files changed, 325 insertions(+), 154 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 0a90f6440..23dfae5ad 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1153,13 +1153,19 @@ do -- COORDINATE --- Mark to All -- @param #COORDINATE self -- @param #string MarkText Free format text that shows the marking clarification. + -- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false. + -- @param #string Text (Optional) Text displayed when mark is added. Default none. -- @return #number The resulting Mark ID which is a number. -- @usage -- local TargetCoord = TargetGroup:GetCoordinate() -- local MarkID = TargetCoord:MarkToAll( "This is a target for all players" ) - function COORDINATE:MarkToAll( MarkText ) + function COORDINATE:MarkToAll( MarkText, ReadOnly, Text ) local MarkID = UTILS.GetMarkID() - trigger.action.markToAll( MarkID, MarkText, self:GetVec3(), false, "" ) + if ReadOnly==nil then + ReadOnly=false + end + local text=Text or "" + trigger.action.markToAll( MarkID, MarkText, self:GetVec3(), ReadOnly, text) return MarkID end @@ -1167,50 +1173,66 @@ do -- COORDINATE -- @param #COORDINATE self -- @param #string MarkText Free format text that shows the marking clarification. -- @param Coalition + -- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false. + -- @param #string Text (Optional) Text displayed when mark is added. Default none. -- @return #number The resulting Mark ID which is a number. -- @usage -- local TargetCoord = TargetGroup:GetCoordinate() -- local MarkID = TargetCoord:MarkToCoalition( "This is a target for the red coalition", coalition.side.RED ) - function COORDINATE:MarkToCoalition( MarkText, Coalition ) + function COORDINATE:MarkToCoalition( MarkText, Coalition, ReadOnly, Text ) local MarkID = UTILS.GetMarkID() - trigger.action.markToCoalition( MarkID, MarkText, self:GetVec3(), Coalition, false, "" ) + if ReadOnly==nil then + ReadOnly=false + end + local text=Text or "" + trigger.action.markToCoalition( MarkID, MarkText, self:GetVec3(), Coalition, ReadOnly, text ) return MarkID end --- Mark to Red Coalition -- @param #COORDINATE self -- @param #string MarkText Free format text that shows the marking clarification. + -- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false. + -- @param #string Text (Optional) Text displayed when mark is added. Default none. -- @return #number The resulting Mark ID which is a number. -- @usage -- local TargetCoord = TargetGroup:GetCoordinate() -- local MarkID = TargetCoord:MarkToCoalitionRed( "This is a target for the red coalition" ) - function COORDINATE:MarkToCoalitionRed( MarkText ) - return self:MarkToCoalition( MarkText, coalition.side.RED ) + function COORDINATE:MarkToCoalitionRed( MarkText, ReadOnly, Text ) + return self:MarkToCoalition( MarkText, coalition.side.RED, ReadOnly, Text ) end --- Mark to Blue Coalition -- @param #COORDINATE self -- @param #string MarkText Free format text that shows the marking clarification. + -- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false. + -- @param #string Text (Optional) Text displayed when mark is added. Default none. -- @return #number The resulting Mark ID which is a number. -- @usage -- local TargetCoord = TargetGroup:GetCoordinate() -- local MarkID = TargetCoord:MarkToCoalitionBlue( "This is a target for the blue coalition" ) - function COORDINATE:MarkToCoalitionBlue( MarkText ) - return self:MarkToCoalition( MarkText, coalition.side.BLUE ) + function COORDINATE:MarkToCoalitionBlue( MarkText, ReadOnly, Text ) + return self:MarkToCoalition( MarkText, coalition.side.BLUE, ReadOnly, Text ) end --- Mark to Group -- @param #COORDINATE self -- @param #string MarkText Free format text that shows the marking clarification. -- @param Wrapper.Group#GROUP MarkGroup The @{Wrapper.Group} that receives the mark. + -- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false. + -- @param #string Text (Optional) Text displayed when mark is added. Default none. -- @return #number The resulting Mark ID which is a number. -- @usage -- local TargetCoord = TargetGroup:GetCoordinate() -- local MarkGroup = GROUP:FindByName( "AttackGroup" ) -- local MarkID = TargetCoord:MarkToGroup( "This is a target for the attack group", AttackGroup ) - function COORDINATE:MarkToGroup( MarkText, MarkGroup ) + function COORDINATE:MarkToGroup( MarkText, MarkGroup, ReadOnly, Text ) local MarkID = UTILS.GetMarkID() - trigger.action.markToGroup( MarkID, MarkText, self:GetVec3(), MarkGroup:GetID(), false, "" ) + if ReadOnly==nil then + ReadOnly=false + end + local text=Text or "" + trigger.action.markToGroup( MarkID, MarkText, self:GetVec3(), MarkGroup:GetID(), ReadOnly, text ) return MarkID end diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 0cbc4474d..2857d755a 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -75,7 +75,7 @@ -- @field #table ammorockets Table holding names of the rocket types which are included when counting the ammo. Default is {"weapons.nurs"} which includes most unguided rockets. -- @field #table ammomissiles Table holding names of the missile types which are included when counting the ammo. Default is {"weapons.missiles"} which includes some guided missiles. -- @field #number Nshots Number of shots fired on current target. --- @field #number minrange Minimum firing range in kilometers. Targets closer than this distance are not engaged. Default 0.5 km. +-- @field #number minrange Minimum firing range in kilometers. Targets closer than this distance are not engaged. Default 0.1 km. -- @field #number maxrange Maximum firing range in kilometers. Targets further away than this distance are not engaged. Default 10000 km. -- @field #number nukewarhead Explosion strength of tactical nuclear warhead in kg TNT. Default 75000. -- @field #number Nukes Number of nuclear shells, the group has available. Default is same number as normal shells. Note that if normal shells are empty, firing nukes is also not possible any more. @@ -86,7 +86,8 @@ -- @field #number relocateRmin Minimum distance in meters the group will look for places to relocate. -- @field #number relocateRmax Maximum distance in meters the group will look for places to relocate. -- @field #boolean markallow If true, Players are allowed to assign targets and moves for ARTY group by placing markers on the F10 map. Default is false. --- @field #number markkey Authorization key. Only player who know this key can assign targets and moves via markers on the F10 map. Default no authorization required. +-- @field #number markkey Authorization key. Only player who know this key can assign targets and moves via markers on the F10 map. Default no authorization required. +-- @field #boolean markreadonly Marks for targets are readonly and cannot be removed by players. Default is false. -- @extends Core.Fsm#FSM_CONTROLLABLE --- Enables mission designers easily to assign targets for artillery units. Since the implementation is based on a Finite State Model (FSM), the mission designer can @@ -424,7 +425,7 @@ ARTY={ ammorockets={}, ammomissiles={}, Nshots=0, - minrange=500, + minrange=100, maxrange=1000000, nukewarhead=75000, Nukes=nil, @@ -436,6 +437,7 @@ ARTY={ relocateRmax=800, markallow=false, markkey=nil, + markreadonly=false, } --- Weapong type ID. http://wiki.hoggit.us/view/DCS_enum_weapon_flag @@ -457,7 +459,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #string version -ARTY.version="0.9.7" +ARTY.version="0.9.8" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -665,7 +667,7 @@ function ARTY:AssignTargetCoord(coord, prio, radius, nshells, maxengage, time, w table.insert(self.targets, _target) -- Trigger new target event. - self:NewTarget(_target) + self:__NewTarget(1, _target) return _name end @@ -732,21 +734,18 @@ function ARTY:AssignMoveCoord(coord, time, speed, onroad, cancel, name, unique) -- Prepare move array. local _move={name=_name, coord=coord, time=_time, speed=speed, onroad=onroad, cancel=cancel} - if self.Debug then - coord:MarkToAll(string.format("Battery %s move position.", self.Controllable:GetName())) - end - -- Add to table. table.insert(self.moves, _move) + return _name end --- Set minimum firing range. Targets closer than this distance are not engaged. -- @param #ARTY self --- @param #number range Min range in kilometers. Default is 0.5 km. +-- @param #number range Min range in kilometers. Default is 0.1 km. function ARTY:SetMinFiringRange(range) self:F({range=range}) - self.minrange=range*1000 or 500 + self.minrange=range*1000 or 100 end --- Set maximum firing range. Targets further away than this distance are not engaged. @@ -864,7 +863,7 @@ function ARTY:RemoveTarget(name) self:T(ARTY.id..string.format("Group %s: Number of targets = %d.", self.Controllable:GetName(), #self.targets)) if self.currentTarget then if self.currentTarget.name==name then - self:T(ARTY.id..string.format("Group %s: Cancelling current target %s (id=%d).", self.Controllable:GetName(), name, id)) + self:T(ARTY.id..string.format("Group %s: Cancelling current target %s.", self.Controllable:GetName(), name)) self:CeaseFire(self.currentTarget) end end @@ -881,6 +880,12 @@ function ARTY:RemoveMove(name) table.remove(self.moves, id) end self:T(ARTY.id..string.format("Group %s: Number of moves = %d.", self.Controllable:GetName(), #self.moves)) + if self.currentMove then + if self.currentMove.name==name then + self:T(ARTY.id..string.format("Group %s: Cancelling current move %s.", self.Controllable:GetName(), name)) + self:Arrived() + end + end end --- Delete ALL targets from current target list. @@ -973,9 +978,13 @@ end --- Enable assigning targets and moves by placing markers on the F10 map. -- @param #ARTY self -- @param #number key (Optional) Authorization key. Only players knowing this key can assign targets. Default is no authorization required. -function ARTY:SetMarkAssignmentsOn(key) +-- @param #boolean readonly (Optional) Marks are readonly and cannot be removed by players. This also means that targets cannot be cancelled by removing the mark. Default false. +function ARTY:SetMarkAssignmentsOn(key, readonly) self.markkey=key self.markallow=true + if readonly==nil then + self.markreadonly=false + end end --- Disable assigning targets by placing markers on the F10 map. @@ -1055,6 +1064,7 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("Relocate max dist. = %d m\n", self.relocateRmax) text=text..string.format("Marker assignments = %s\n", tostring(self.markallow)) text=text..string.format("Marker auth. key = %s\n", tostring(self.markkey)) + text=text..string.format("Marker readonly = %s\n", tostring(self.markreadonly)) text=text..string.format("******************************************************\n") text=text..string.format("Targets:\n") for _, target in pairs(self.targets) do @@ -1112,6 +1122,7 @@ function ARTY:_Markertext(text) assignment.battery={} assignment.move=false assignment.engage=false + assignment.readonly=false assignment.time=nil assignment.nshells=nil assignment.prio=nil @@ -1136,7 +1147,6 @@ function ARTY:_Markertext(text) local keywords=self:_split(text, ",") for _,key in pairs(keywords) do - --env.info("key="..key) local s=self:_split(key, " ") local val=s[2] @@ -1148,7 +1158,7 @@ function ARTY:_Markertext(text) for i=2,#v,2 do table.insert(assignment.battery, v[i]) - env.info(string.format("FF: Battery=%s.", v[i])) + self:T2(ARTY.id..string.format("Key Battery=%s.", v[i])) end elseif key:lower():find("time") then @@ -1158,27 +1168,27 @@ function ARTY:_Markertext(text) else assignment.time=val end - env.info(string.format("FF: Time=%s.", val)) + self:T2(ARTY.id..string.format("Key Time=%s.", val)) elseif key:lower():find("shots") then assignment.nshells=tonumber(s[2]) - env.info(string.format("FF: Shots=%s.", val)) + self:T(ARTY.id..string.format("Key Shots=%s.", val)) elseif key:lower():find("prio") then assignment.prio=tonumber(val) - env.info(string.format("FF: Prio=%s.", val)) + self:T2(string.format("Key Prio=%s.", val)) elseif key:lower():find("maxengage") then assignment.maxengage=tonumber(val) - env.info(string.format("FF: Maxengage=%s.", val)) + self:T2(ARTY.id..string.format("Key Maxengage=%s.", val)) elseif key:lower():find("radius") then assignment.radius=tonumber(val) - env.info(string.format("FF: Radius=%s.", val)) + self:T2(ARTY.id..string.format("Key Radius=%s.", val)) elseif key:lower():find("weapon") then @@ -1193,28 +1203,31 @@ function ARTY:_Markertext(text) else assignment.weapontype=ARTY.WeaponType.Auto end - env.info(string.format("FF: Weapon=%s.", val)) + self:T2(ARTY.id..string.format("Key Weapon=%s.", val)) elseif key:lower():find("speed") then assignment.speed=tonumber(val) - env.info(string.format("FF: Speed=%s.", val)) + self:T2(ARTY.id..string.format("Key Speed=%s.", val)) elseif key:lower():find("road") then assignment.onroad=true - env.info(string.format("FF: Onroad=true.")) + self:T2(ARTY.id..string.format("Key Onroad=true.")) elseif key:lower():find("key") then assignment.key=tonumber(val) - env.info(string.format("FF: Key=%s.", val)) + self:T(ARTY.id..string.format("Key Key=%s.", val)) + elseif key:lower():find("irrevocable") then + assignment.readonly=true + self:T2(ARTY.id..string.format("Key Readonly=true.")) end end else - env.info("FF: This is NO arty command!") + self:T2(ARTY.id..string.format("This is NO arty command:\n%s", tostring(text))) end return assignment @@ -1230,143 +1243,275 @@ function ARTY:onEvent(Event) return true end + -- Set battery and coalition. local batteryname=self.Controllable:GetName() local batterycoalition=self.Controllable:GetCoalition() - env.info(string.format("Event captured = %s", tostring(batteryname))) - env.info(string.format("Event id = %s", tostring(Event.id))) - env.info(string.format("Event time = %s", tostring(Event.time))) - env.info(string.format("Event idx = %s", tostring(Event.idx))) - env.info(string.format("Event coalition = %s", tostring(Event.coalition))) - env.info(string.format("Event group id = %s", tostring(Event.groupID))) - env.info(string.format("Event text = %s", tostring(Event.text))) + self:T(string.format("Event captured = %s", tostring(batteryname))) + self:T(string.format("Event id = %s", tostring(Event.id))) + self:T(string.format("Event time = %s", tostring(Event.time))) + self:T(string.format("Event idx = %s", tostring(Event.idx))) + self:T(string.format("Event coalition = %s", tostring(Event.coalition))) + self:T(string.format("Event group id = %s", tostring(Event.groupID))) + self:T(string.format("Event text = %s", tostring(Event.text))) self:E({eventid=Event.id, vec3=Event.pos}) if Event.initiator~=nil then local _unitname=Event.initiator:getName() - env.info(string.format("Event ini unit name = %s", tostring(_unitname))) + self:T(string.format("Event ini unit name = %s", tostring(_unitname))) end - if Event.id==world.event.S_EVENT_MARK_ADDED then - self:E({event="S_EVENT_MARK_ADDED", vec3=Event.pos}) + self:E({event="S_EVENT_MARK_ADDED", battery=batteryname, vec3=Event.pos}) elseif Event.id==world.event.S_EVENT_MARK_CHANGE then - self:E({event="S_EVENT_MARK_CHANGE", vec3=Event.pos}) + self:E({event="S_EVENT_MARK_CHANGE", battery=batteryname, vec3=Event.pos}) - -- Check if marker has a text and the "arty" keyword. - if Event.text~=nil and Event.text:lower():find("arty") then - - -- Check if the coalition is the same or an authorization key has been defined. - if (batterycoalition==Event.coalition and self.markkey==nil) or self.markkey~=nil then - - -- Evaluate marker text and extract parameters. - local _assign=self:_Markertext(Event.text) - - -- Check if job is assigned to this ARTY group. Default is for all ARTY groups. - local _assigned=true - if #_assign.battery>0 then - _assigned=false - for _,bat in pairs(_assign.battery) do - env.info(string.format("FF: compare %s=%s ==> %s",batteryname, bat, tostring(batteryname==bat))) - if batteryname==bat then - _assigned=true - end - end - end - - -- Check if the authorization key is required and if it is valid. - local _validkey=true - if self.markkey~=nil then - _validkey=false - if _assign.key~=nil then - _validkey=self.markkey==_assign.key - end - self:T(ARTY.id..string.format("%s, authkey=%s == %s=playerkey ==> valid=%s", batteryname, tostring(self.markkey), tostring(_assign.key), tostring(_validkey))) - local text="" - if _assign.key==nil then - text=string.format("%s, authorization required but did not receive a key!", batteryname) - elseif _validkey==false then - text=string.format("%s, authorization required but did receive an incorrect key (key=%s)!", batteryname, tostring(_assign.key)) - elseif _validkey==true then - text=string.format("%s, authorization successful!", batteryname) - end - MESSAGE:New(text, 20):ToCoalitionIf(batterycoalition, self.report or self.Debug) - end - - -- We are meant. - if _assigned and _validkey then - - -- Convert (wrong x-->z, z-->x) vec3 - local vec3={y=Event.pos.y, x=Event.pos.z, z=Event.pos.x} - -- Get coordinate from vec3. - local _coord=COORDINATE:NewFromVec3(vec3) - - if _assign.move then - - local text=string.format("%s, received new relocation assignment.", batteryname) - MESSAGE:New(text, 20):ToCoalitionIf(batterycoalition, self.report or self.Debug) - - -- Create a new name. - local _name=string.format("Marked Move ID=%d for battery %s", Event.idx, batteryname) - self:E(ARTY.id.._name) - - -- Assign a relocation of the arty group. - self:AssignMoveCoord(_coord, _assign.time, _assign.speed, _assign.onroad, _assign.cancel,_name, true) - - else - - local text=string.format("%s, received new target assignment.", batteryname) - if _assign.time then - text=text..string.format("\nTime %s",_assign.time) - end - if _assign.prio then - text=text..string.format("\nPrio %d",_assign.prio) - end - if _assign.nshells then - text=text..string.format("\nShots %d",_assign.nshells) - end - if _assign.maxengage then - text=text..string.format("\nEngagements %d",_assign.maxengage) - end - if _assign.weapontype then - text=text..string.format("\nWeapon %s",self:_WeaponTypeName(_assign.weapontype)) - end - MESSAGE:New(text, 20):ToCoalitionIf(batterycoalition, self.report or self.Debug) - - -- Create a new name. - local _name=string.format("Marked Target ID=%d for battery %s", Event.idx, batteryname) - self:E(ARTY.id.._name) - - -- Assign a new firing engagement. - self:AssignTargetCoord(_coord,_assign.prio,_assign.radius,_assign.nshells,_assign.maxengage,_assign.time,_assign.weapontype, _name, true) - - end - end - - end - end + -- Handle event. + self:_OnEventMarkChange(Event) elseif Event.id==world.event.S_EVENT_MARK_REMOVED then - self:E({event="S_EVENT_MARK_REMOVED", vec3=Event.pos}) - - -- Check if we have the right coalition. - if batterycoalition==Event.coalition and Event.text:lower():find("arty") then - - -- This should be the unique name of the target or move. - - if Event.text:lower():find("move") then - local _name=string.format("Marked Move ID=%d for battery %s", Event.idx, batteryname) - self:RemoveMove(_name) - else - local _name=string.format("Marked Target ID=%d for battery %s", Event.idx, batteryname) - self:RemoveTarget(_name) - end - end + self:E({event="S_EVENT_MARK_REMOVED", battery=batteryname, vec3=Event.pos}) + -- Hande event. + self:_OnEventMarkRemove(Event) end end +--- Function called when a F10 map mark was removed. +-- @param #ARTY self +-- @param #table Event Event data. +function ARTY:_OnEventMarkRemove(Event) + + -- Get battery coalition and name. + local batterycoalition=self.Controllable:GetCoalition() + local batteryname=self.Controllable:GetName() + + if Event.text~=nil and Event.text:find("BATTERY") then + + local _cancelmove=false + local _canceltarget=false + local _name="" + local _id=nil + if Event.text:find("Marked Relocation") then + _cancelmove=true + _name=string.format("BATTERY %s Marked Relocation ID=%d", batteryname, Event.idx) + _id=self:_GetMoveIndexByName(_name) + elseif Event.text:find("Marked Target") then + _canceltarget=true + _name=string.format("BATTERY %s Marked Target ID=%d", batteryname, Event.idx) + _id=self:_GetTargetIndexByName(_name) + else + return + end + + if _id==nil then + return + end + + -- Check if the coalition is the same or an authorization key has been defined. + if (batterycoalition==Event.coalition and self.markkey==nil) or self.markkey~=nil then + + -- Get assignment. + local mykey=nil + if self.markkey~=nil then + -- keywords are split by "," + local keywords=self:_split(Event.text, ",") + for _,key in pairs(keywords) do + local s=self:_split(key, " ") + local val=s[2] + if key:lower():find("key") then + mykey=tonumber(val) + self:T(ARTY.id..string.format("Key Key=%s.", val)) + end + end + end + + -- Check if the authorization key is required and if it is valid. + local _validkey=true + if self.markkey~=nil then + _validkey=false + if mykey~=nil then + _validkey=self.markkey==mykey + end + self:T2(ARTY.id..string.format("%s, authkey=%s == %s=playerkey ==> valid=%s", batteryname, tostring(self.markkey), tostring(mykey), tostring(_validkey))) + local text="" + if mykey==nil then + text=string.format("%s, authorization required but did not receive a key!", batteryname) + elseif _validkey==false then + text=string.format("%s, authorization required but did receive an incorrect key (key=%s)!", batteryname, tostring(mykey)) + elseif _validkey==true then + text=string.format("%s, authentification successful!", batteryname) + end + MESSAGE:New(text, 20):ToCoalitionIf(batterycoalition, self.report or self.Debug) + end + + -- Check if we have the right coalition. + if _validkey then + + -- This should be the unique name of the target or move. + if _cancelmove then + self:RemoveMove(_name) + elseif _canceltarget then + self:RemoveTarget(_name) + end + + end + + end + + end +end + +--- Function called when a F10 map mark was changed. +-- @param #ARTY self +-- @param #table Event Event data. +function ARTY:_OnEventMarkChange(Event) + + -- Check if marker has a text and the "arty" keyword. + if Event.text~=nil and Event.text:lower():find("arty") then + + -- Get battery coalition and name. + local batterycoalition=self.Controllable:GetCoalition() + local batteryname=self.Controllable:GetName() + + -- Check if the coalition is the same or an authorization key has been defined. + if (batterycoalition==Event.coalition and self.markkey==nil) or self.markkey~=nil then + + -- Evaluate marker text and extract parameters. + local _assign=self:_Markertext(Event.text) + + -- Check if job is assigned to this ARTY group. Default is for all ARTY groups. + local _assigned=true + if #_assign.battery>0 then + _assigned=false + for _,bat in pairs(_assign.battery) do + self:T2(ARTY.id..string.format("Compare battery names %s=%s ==> %s",batteryname, bat, tostring(batteryname==bat))) + if batteryname==bat then + _assigned=true + end + end + end + + -- Check if ENGAGE or MOVE keywords were found. + if not (_assign.engage or _assign.move) or (not _assigned) then + return + end + + -- Check if the authorization key is required and if it is valid. + local _validkey=true + if self.markkey~=nil then + _validkey=false + if _assign.key~=nil then + _validkey=self.markkey==_assign.key + end + self:T2(ARTY.id..string.format("%s, authkey=%s == %s=playerkey ==> valid=%s", batteryname, tostring(self.markkey), tostring(_assign.key), tostring(_validkey))) + local text="" + if _assign.key==nil then + text=string.format("%s, authorization required but did not receive a key!", batteryname) + elseif _validkey==false then + text=string.format("%s, authorization required but did receive an incorrect key (key=%s)!", batteryname, tostring(_assign.key)) + elseif _validkey==true then + text=string.format("%s, authentification successful!", batteryname) + end + MESSAGE:New(text, 20):ToCoalitionIf(batterycoalition, self.report or self.Debug) + end + + -- We are meant. + if _validkey then + + -- Convert (wrong x-->z, z-->x) vec3 + local vec3={y=Event.pos.y, x=Event.pos.z, z=Event.pos.x} + + -- Get coordinate from vec3. + local _coord=COORDINATE:NewFromVec3(vec3) + + -- Remove old mark because it might contain confidential data such as the key. + -- Also I don't know who can see the mark which was created. + _coord:RemoveMark(Event.idx) + + local _id=UTILS._MarkID+1 + + if _assign.move then + + -- Create a new name. This determins the string we search when deleting a move! + local _name=string.format("BATTERY %s Marked Relocation ID=%d", batteryname, _id) + self:E(ARTY.id.._name) + + local text=string.format("%s, received new relocation assignment.", batteryname) + text=text..string.format("\nCoordinates %s",_coord:ToStringLLDMS()) + MESSAGE:New(text, 20):ToCoalitionIf(batterycoalition, self.report or self.Debug) + + -- Assign a relocation of the arty group. + local _movename=self:AssignMoveCoord(_coord, _assign.time, _assign.speed, _assign.onroad, _assign.cancel,_name, true) + + if _movename~=nil then + local _mid=self:_GetMoveIndexByName(_movename) + local _move=self.moves[_mid] + + -- Create new target name. + local clock=tostring(self:_SecondsToClock(_move.time)) + local _road="Off Road" + if _move.onroad==true then + _road="On Road" + end + local _markertext=_movename..string.format(", Time %s, Speed %d km/h, %s.", clock, _move.speed, _road) + + -- Create a new mark. This will trigger the mark added event. + local _randomcoord=_coord:GetRandomCoordinateInRadius(100) + _randomcoord:MarkToCoalition(_markertext, batterycoalition, self.markreadonly or _assign.readonly) + end + + else + + -- Create a new name. + local _name=string.format("BATTERY %s Marked Target ID=%d", batteryname, _id) + self:E(ARTY.id.._name) + + local text=string.format("%s, received new target assignment.", batteryname) + text=text..string.format("\nCoordinates %s",_coord:ToStringLLDMS()) + if _assign.time then + text=text..string.format("\nTime %s",_assign.time) + end + if _assign.prio then + text=text..string.format("\nPrio %d",_assign.prio) + end + if _assign.nshells then + text=text..string.format("\nShots %d",_assign.nshells) + end + if _assign.maxengage then + text=text..string.format("\nEngagements %d",_assign.maxengage) + end + if _assign.weapontype then + text=text..string.format("\nWeapon %s",self:_WeaponTypeName(_assign.weapontype)) + end + MESSAGE:New(text, 20):ToCoalitionIf(batterycoalition, self.report or self.Debug) + + -- Assign a new firing engagement. + -- Note, we set unique=true so this target gets only added once. + local _targetname=self:AssignTargetCoord(_coord,_assign.prio,_assign.radius,_assign.nshells,_assign.maxengage,_assign.time,_assign.weapontype, _name, true) + + if _targetname~=nil then + local _tid=self:_GetTargetIndexByName(_targetname) + local _target=self.targets[_tid] + + -- Create new target name. + local clock=tostring(self:_SecondsToClock(_target.time)) + local weapon=self:_WeaponTypeName(_target.weapontype) + local _markertext=_targetname..string.format(", Priority %d, Radius=%d m, Shots %d, Engagements=%d, Weapon %s, Time %s", _target.prio, _target.radius, _target.nshells, _target.maxengage, weapon, clock) + + -- Create a new mark. This will trigger the mark added event. + local _randomcoord=_coord:GetRandomCoordinateInRadius(250) + _randomcoord:MarkToCoalition(_markertext, batterycoalition, self.markreadonly or _assign.readonly) + end + end + end + + end + end + +end + --- After "Start" event. Initialized ROE and alarm state. Starts the event handler. -- @param #ARTY self function ARTY:_StatusReport() @@ -2298,6 +2443,9 @@ function ARTY:onafterArrived(Controllable, From, Event, To) -- Set alarm state to auto. self.Controllable:OptionAlarmStateAuto() + -- Clear Tasks + self.Controllable:ClearTasks() + -- Send message local text=string.format("%s, arrived at destination.", Controllable:GetName()) self:T(ARTY.id..text) @@ -2827,8 +2975,9 @@ function ARTY:_GetTargetIndexByName(name) for i=1,#self.targets do local targetname=self.targets[i].name + self:T(ARTY.id..string.format("Have target with name %s. Index = %d", targetname, i)) if targetname==name then - self:T2(ARTY.id..string.format("Found target with name %s. Index = %d", name, i)) + self:T(ARTY.id..string.format("Found target with name %s. Index = %d", name, i)) return i end end From e9473e917991a8dc659cbd2e16689eeb7732ece3 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sat, 2 Jun 2018 07:25:43 +0200 Subject: [PATCH 149/420] Documentation improvements --- Moose Development/Moose/Tasking/Task_A2A.lua | 12 +++--------- Moose Development/Moose/Tasking/Task_A2G.lua | 12 +++--------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/Moose Development/Moose/Tasking/Task_A2A.lua b/Moose Development/Moose/Tasking/Task_A2A.lua index e3d3ed793..0a18e28cc 100644 --- a/Moose Development/Moose/Tasking/Task_A2A.lua +++ b/Moose Development/Moose/Tasking/Task_A2A.lua @@ -361,9 +361,7 @@ do -- TASK_A2A_INTERCEPT -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK - --- # TASK_A2A_INTERCEPT class, extends @{Task_A2A#TASK_A2A} - -- - -- The TASK_A2A_INTERCEPT class defines an intercept task for a human player to be executed. + --- Defines an intercept task for a human player to be executed. -- When enemy planes need to be intercepted by human players, use this task type to urgen the players to get out there! -- -- The TASK_A2A_INTERCEPT is used by the @{Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create intercept tasks @@ -460,9 +458,7 @@ do -- TASK_A2A_SWEEP -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK - --- # TASK_A2A_SWEEP class, extends @{Task_A2A#TASK_A2A} - -- - -- The TASK_A2A_SWEEP class defines a sweep task for a human player to be executed. + --- Defines a sweep task for a human player to be executed. -- A sweep task needs to be given when targets were detected but somehow the detection was lost. -- Most likely, these enemy planes are hidden in the mountains or are flying under radar. -- These enemy planes need to be sweeped by human players, and use this task type to urge the players to get out there and find those enemy fighters. @@ -571,9 +567,7 @@ do -- TASK_A2A_ENGAGE -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK - --- # TASK_A2A_ENGAGE class, extends @{Task_A2A#TASK_A2A} - -- - -- The TASK_A2A_ENGAGE class defines an engage task for a human player to be executed. + --- Defines an engage task for a human player to be executed. -- When enemy planes are close to human players, use this task type is used urge the players to get out there! -- -- The TASK_A2A_ENGAGE is used by the @{Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create engage tasks diff --git a/Moose Development/Moose/Tasking/Task_A2G.lua b/Moose Development/Moose/Tasking/Task_A2G.lua index 319a30fb2..d30eff7aa 100644 --- a/Moose Development/Moose/Tasking/Task_A2G.lua +++ b/Moose Development/Moose/Tasking/Task_A2G.lua @@ -366,9 +366,7 @@ do -- TASK_A2G_SEAD -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK - --- # TASK_A2G_SEAD class, extends @{Task_A2G#TASK_A2G} - -- - -- The TASK_A2G_SEAD class defines an Suppression or Extermination of Air Defenses task for a human player to be executed. + --- Defines an Suppression or Extermination of Air Defenses task for a human player to be executed. -- These tasks are important to be executed as they will help to achieve air superiority at the vicinity. -- -- The TASK_A2G_SEAD is used by the @{Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create SEAD tasks @@ -459,9 +457,7 @@ do -- TASK_A2G_BAI -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK - --- # TASK_A2G_BAI class, extends @{Task_A2G#TASK_A2G} - -- - -- The TASK_A2G_BAI class defines an Battlefield Air Interdiction task for a human player to be executed. + -- Defines an Battlefield Air Interdiction task for a human player to be executed. -- These tasks are more strategic in nature and are most of the time further away from friendly forces. -- BAI tasks can also be used to express the abscence of friendly forces near the vicinity. -- @@ -555,9 +551,7 @@ do -- TASK_A2G_CAS -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK - --- # TASK_A2G_CAS class, extends @{Task_A2G#TASK_A2G} - -- - -- The TASK_A2G_CAS class defines an Close Air Support task for a human player to be executed. + -- Defines an Close Air Support task for a human player to be executed. -- Friendly forces will be in the vicinity within 6km from the enemy. -- -- The TASK_A2G_CAS is used by the @{Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create CAS tasks From d9d53db53f04c71e90648e716ffc31f570cda60d Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sat, 2 Jun 2018 18:23:06 +0200 Subject: [PATCH 150/420] Documentation improvements. --- Moose Development/Moose/AI/AI_A2A_Cap.lua | 14 ++-- .../Moose/AI/AI_A2A_Dispatcher.lua | 8 +-- Moose Development/Moose/AI/AI_A2A_Gci.lua | 14 ++-- Moose Development/Moose/AI/AI_BAI.lua | 14 ++-- Moose Development/Moose/AI/AI_Balancer.lua | 2 +- Moose Development/Moose/AI/AI_CAP.lua | 14 ++-- Moose Development/Moose/AI/AI_CAS.lua | 14 ++-- Moose Development/Moose/AI/AI_Formation.lua | 24 +++---- .../Moose/Actions/Act_Account.lua | 4 +- .../Moose/Actions/Act_Assign.lua | 6 +- .../Moose/Actions/Act_Assist.lua | 2 +- Moose Development/Moose/Actions/Act_Route.lua | 4 +- Moose Development/Moose/Cargo/Cargo.lua | 8 +-- Moose Development/Moose/Core/Base.lua | 2 +- Moose Development/Moose/Core/Event.lua | 10 +-- Moose Development/Moose/Core/Message.lua | 18 ++--- Moose Development/Moose/Core/Point.lua | 10 +-- Moose Development/Moose/Core/Radio.lua | 26 +++---- .../Moose/Core/ScheduleDispatcher.lua | 2 +- Moose Development/Moose/Core/Set.lua | 34 ++++----- Moose Development/Moose/Core/Spawn.lua | 2 +- Moose Development/Moose/Core/Zone.lua | 72 +++++++++---------- .../Moose/Functional/Artillery.lua | 4 +- .../Moose/Functional/Designate.lua | 2 +- .../Moose/Functional/Scoring.lua | 2 +- .../Moose/Tasking/DetectionManager.lua | 22 +++--- Moose Development/Moose/Tasking/Task.lua | 6 +- .../Moose/Tasking/TaskZoneCapture.lua | 8 +-- Moose Development/Moose/Tasking/Task_A2A.lua | 16 ++--- .../Moose/Tasking/Task_A2A_Dispatcher.lua | 2 +- Moose Development/Moose/Tasking/Task_A2G.lua | 10 +-- .../Moose/Tasking/Task_CARGO.lua | 6 +- .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 2 +- .../Moose/Tasking/Task_Manager.lua | 18 ++--- Moose Development/Moose/Wrapper/Client.lua | 6 +- Moose Development/Moose/Wrapper/Group.lua | 4 +- Moose Development/Moose/Wrapper/Object.lua | 2 +- .../Moose/Wrapper/Positionable.lua | 12 ++-- Moose Development/Moose/Wrapper/Scenery.lua | 2 +- Moose Development/Moose/Wrapper/Static.lua | 2 +- 40 files changed, 215 insertions(+), 215 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A_Cap.lua b/Moose Development/Moose/AI/AI_A2A_Cap.lua index e6abde8d9..480bd39ec 100644 --- a/Moose Development/Moose/AI/AI_A2A_Cap.lua +++ b/Moose Development/Moose/AI/AI_A2A_Cap.lua @@ -61,13 +61,13 @@ -- -- ### 2.2 AI_A2A_CAP Events -- --- * **@{AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. --- * **@{AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. -- * **@{#AI_A2A_CAP.Engage}**: Let the AI engage the bogeys. -- * **@{#AI_A2A_CAP.Abort}**: Aborts the engagement and return patrolling in the patrol zone. --- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. --- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. --- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. -- * **@{#AI_A2A_CAP.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}. -- * **@{#AI_A2A_CAP.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task. -- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. @@ -80,7 +80,7 @@ -- that will define when the AI will engage with the detected airborne enemy targets. -- The range can be beyond or smaller than the range of the Patrol Zone. -- The range is applied at the position of the AI. --- Use the method @{AI_CAP#AI_A2A_CAP.SetEngageRange}() to define that range. +-- Use the method @{AI.AI_CAP#AI_A2A_CAP.SetEngageRange}() to define that range. -- -- ## 4. Set the Zone of Engagement -- @@ -88,7 +88,7 @@ -- -- An optional @{Zone} can be set, -- that will define when the AI will engage with the detected airborne enemy targets. --- Use the method @{AI_Cap#AI_A2A_CAP.SetEngageZone}() to define that Zone. +-- Use the method @{AI.AI_Cap#AI_A2A_CAP.SetEngageZone}() to define that Zone. -- -- === -- diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index 92ecfce9b..1dd660b49 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -220,7 +220,7 @@ do -- AI_A2A_DISPATCHER -- therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map. -- It all depends on what the desired effect is. -- - -- EWR networks are **dynamically constructed**, that is, they form part of the @{Functional#DETECTION_BASE} object that is given as the input parameter of the AI\_A2A\_DISPATCHER class. + -- EWR networks are **dynamically constructed**, that is, they form part of the @{Functional.Detection#DETECTION_BASE} object that is given as the input parameter of the AI\_A2A\_DISPATCHER class. -- By defining in a **smart way the names or name prefixes of the groups** with EWR capable units, these groups will be **automatically added or deleted** from the EWR network, -- increasing or decreasing the radar coverage of the Early Warning System. -- @@ -821,7 +821,7 @@ do -- AI_A2A_DISPATCHER --- AI_A2A_DISPATCHER constructor. -- This is defining the A2A DISPATCHER for one coaliton. - -- The Dispatcher works with a @{Functional#Detection} object that is taking of the detection of targets using the EWR units. + -- The Dispatcher works with a @{Functional.Detection#DETECTION_BASE} object that is taking of the detection of targets using the EWR units. -- The Detection object is polymorphic, depending on the type of detection object choosen, the detection will work differently. -- @param #AI_A2A_DISPATCHER self -- @param Functional.Detection#DETECTION_BASE Detection The DETECTION object that will detects targets using the the Early Warning Radar network. @@ -3165,7 +3165,7 @@ do -- @extends #AI_A2A_DISPATCHER --- Create an automatic air defence system for a coalition setting up GCI and CAP air defenses. - -- The class derives from @{AI#AI_A2A_DISPATCHER} and thus, all the methods that are defined in the @{AI#AI_A2A_DISPATCHER} class, can be used also in AI\_A2A\_GCICAP. + -- The class derives from @{#AI_A2A_DISPATCHER} and thus, all the methods that are defined in the @{#AI_A2A_DISPATCHER} class, can be used also in AI\_A2A\_GCICAP. -- -- === -- @@ -3268,7 +3268,7 @@ do -- -- **The place of the helicopter is important, as the airbase closest to the helicopter will be the airbase from where the CAP planes will take off for CAP.** -- - -- ## 2) There are a lot of defaults set, which can be further modified using the methods in @{AI#AI_A2A_DISPATCHER}: + -- ## 2) There are a lot of defaults set, which can be further modified using the methods in @{#AI_A2A_DISPATCHER}: -- -- ### 2.1) Planes are taking off in the air from the airbases. -- diff --git a/Moose Development/Moose/AI/AI_A2A_Gci.lua b/Moose Development/Moose/AI/AI_A2A_Gci.lua index c1acb6df7..cc24341c3 100644 --- a/Moose Development/Moose/AI/AI_A2A_Gci.lua +++ b/Moose Development/Moose/AI/AI_A2A_Gci.lua @@ -64,13 +64,13 @@ -- -- ### 2.2 AI_A2A_GCI Events -- --- * **@{AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. --- * **@{AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. -- * **@{#AI_A2A_GCI.Engage}**: Let the AI engage the bogeys. -- * **@{#AI_A2A_GCI.Abort}**: Aborts the engagement and return patrolling in the patrol zone. --- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. --- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. --- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. -- * **@{#AI_A2A_GCI.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}. -- * **@{#AI_A2A_GCI.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task. -- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. @@ -83,7 +83,7 @@ -- that will define when the AI will engage with the detected airborne enemy targets. -- The range can be beyond or smaller than the range of the Patrol Zone. -- The range is applied at the position of the AI. --- Use the method @{AI_GCI#AI_A2A_GCI.SetEngageRange}() to define that range. +-- Use the method @{AI.AI_GCI#AI_A2A_GCI.SetEngageRange}() to define that range. -- -- ## 4. Set the Zone of Engagement -- @@ -91,7 +91,7 @@ -- -- An optional @{Zone} can be set, -- that will define when the AI will engage with the detected airborne enemy targets. --- Use the method @{AI_Cap#AI_A2A_GCI.SetEngageZone}() to define that Zone. +-- Use the method @{AI.AI_Cap#AI_A2A_GCI.SetEngageZone}() to define that Zone. -- -- === -- diff --git a/Moose Development/Moose/AI/AI_BAI.lua b/Moose Development/Moose/AI/AI_BAI.lua index fa66d3993..0265e7113 100644 --- a/Moose Development/Moose/AI/AI_BAI.lua +++ b/Moose Development/Moose/AI/AI_BAI.lua @@ -99,13 +99,13 @@ -- -- ### 2.2. AI_BAI_ZONE Events -- --- * **@{AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. --- * **@{AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. -- * **@{#AI_BAI_ZONE.Engage}**: Engage the AI to provide BOMB in the Engage Zone, destroying any target it finds. -- * **@{#AI_BAI_ZONE.Abort}**: Aborts the engagement and return patrolling in the patrol zone. --- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. --- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. --- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. -- * **@{#AI_BAI_ZONE.Destroy}**: The AI has destroyed a target @{Wrapper.Unit}. -- * **@{#AI_BAI_ZONE.Destroyed}**: The AI has destroyed all target @{Wrapper.Unit}s assigned in the BOMB task. -- * **Status**: The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. @@ -179,7 +179,7 @@ function AI_BAI_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude -- @param DCS#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. -- @param DCS#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. -- If parameter is not defined the unit / controllable will choose expend on its own discretion. - -- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. + -- Use the structure @{DCS#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param DCS#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. @@ -191,7 +191,7 @@ function AI_BAI_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude -- @param DCS#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. -- @param DCS#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. -- If parameter is not defined the unit / controllable will choose expend on its own discretion. - -- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. + -- Use the structure @{DCS#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param DCS#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. diff --git a/Moose Development/Moose/AI/AI_Balancer.lua b/Moose Development/Moose/AI/AI_Balancer.lua index f82631dab..845826703 100644 --- a/Moose Development/Moose/AI/AI_Balancer.lua +++ b/Moose Development/Moose/AI/AI_Balancer.lua @@ -31,7 +31,7 @@ -- CLIENTS in a SET\_CLIENT collection, which are not occupied by human players. -- In other words, use AI_BALANCER to simulate human behaviour by spawning in replacement AI in multi player missions. -- --- The parent class @{Fsm#FSM_SET} manages the functionality to control the Finite State Machine (FSM). +-- The parent class @{Core.Fsm#FSM_SET} manages the functionality to control the Finite State Machine (FSM). -- The mission designer can tailor the behaviour of the AI_BALANCER, by defining event and state transition methods. -- An explanation about state and event transition methods can be found in the @{FSM} module documentation. -- diff --git a/Moose Development/Moose/AI/AI_CAP.lua b/Moose Development/Moose/AI/AI_CAP.lua index f6b42f37b..e98f4f135 100644 --- a/Moose Development/Moose/AI/AI_CAP.lua +++ b/Moose Development/Moose/AI/AI_CAP.lua @@ -79,13 +79,13 @@ -- -- ### 2.2 AI_CAP_ZONE Events -- --- * **@{AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. --- * **@{AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. -- * **@{#AI_CAP_ZONE.Engage}**: Let the AI engage the bogeys. -- * **@{#AI_CAP_ZONE.Abort}**: Aborts the engagement and return patrolling in the patrol zone. --- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. --- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. --- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. -- * **@{#AI_CAP_ZONE.Destroy}**: The AI has destroyed a bogey @{Wrapper.Unit}. -- * **@{#AI_CAP_ZONE.Destroyed}**: The AI has destroyed all bogeys @{Wrapper.Unit}s assigned in the CAS task. -- * **Status** ( Group ): The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. @@ -98,7 +98,7 @@ -- that will define when the AI will engage with the detected airborne enemy targets. -- The range can be beyond or smaller than the range of the Patrol Zone. -- The range is applied at the position of the AI. --- Use the method @{AI_CAP#AI_CAP_ZONE.SetEngageRange}() to define that range. +-- Use the method @{AI.AI_CAP#AI_CAP_ZONE.SetEngageRange}() to define that range. -- -- ## 4. Set the Zone of Engagement -- @@ -106,7 +106,7 @@ -- -- An optional @{Zone} can be set, -- that will define when the AI will engage with the detected airborne enemy targets. --- Use the method @{AI_Cap#AI_CAP_ZONE.SetEngageZone}() to define that Zone. +-- Use the method @{AI.AI_Cap#AI_CAP_ZONE.SetEngageZone}() to define that Zone. -- -- === -- diff --git a/Moose Development/Moose/AI/AI_CAS.lua b/Moose Development/Moose/AI/AI_CAS.lua index 2ef978853..7ddb55876 100644 --- a/Moose Development/Moose/AI/AI_CAS.lua +++ b/Moose Development/Moose/AI/AI_CAS.lua @@ -99,13 +99,13 @@ -- -- ### 2.2. AI_CAS_ZONE Events -- --- * **@{AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. --- * **@{AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Start}**: Start the process. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Route}**: Route the AI to a new random 3D point within the Patrol Zone. -- * **@{#AI_CAS_ZONE.Engage}**: Engage the AI to provide CAS in the Engage Zone, destroying any target it finds. -- * **@{#AI_CAS_ZONE.Abort}**: Aborts the engagement and return patrolling in the patrol zone. --- * **@{AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. --- * **@{AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. --- * **@{AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.RTB}**: Route the AI to the home base. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detect}**: The AI is detecting targets. +-- * **@{AI.AI_Patrol#AI_PATROL_ZONE.Detected}**: The AI has detected new targets. -- * **@{#AI_CAS_ZONE.Destroy}**: The AI has destroyed a target @{Wrapper.Unit}. -- * **@{#AI_CAS_ZONE.Destroyed}**: The AI has destroyed all target @{Wrapper.Unit}s assigned in the CAS task. -- * **Status**: The AI is checking status (fuel and damage). When the tresholds have been reached, the AI will RTB. @@ -166,7 +166,7 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude -- @param DCS#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. -- @param DCS#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. -- If parameter is not defined the unit / controllable will choose expend on its own discretion. - -- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. + -- Use the structure @{DCS#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param DCS#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. @@ -178,7 +178,7 @@ function AI_CAS_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude -- @param DCS#Distance EngageAltitude (optional) Desired altitude to perform the unit engagement. -- @param DCS#AI.Task.WeaponExpend EngageWeaponExpend (optional) Determines how much weapon will be released at each attack. -- If parameter is not defined the unit / controllable will choose expend on its own discretion. - -- Use the structure @{DCSTypes#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. + -- Use the structure @{DCS#AI.Task.WeaponExpend} to define the amount of weapons to be release at each attack. -- @param #number EngageAttackQty (optional) This parameter limits maximal quantity of attack. The aicraft/controllable will not make more attack than allowed even if the target controllable not destroyed and the aicraft/controllable still have ammo. If not defined the aircraft/controllable will attack target until it will be destroyed or until the aircraft/controllable will run out of ammo. -- @param DCS#Azimuth EngageDirection (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. diff --git a/Moose Development/Moose/AI/AI_Formation.lua b/Moose Development/Moose/AI/AI_Formation.lua index be8abc9e9..e262a67e4 100644 --- a/Moose Development/Moose/AI/AI_Formation.lua +++ b/Moose Development/Moose/AI/AI_Formation.lua @@ -84,25 +84,25 @@ -- -- Create a new SPAWN object with the @{#AI_FORMATION.New} method: -- --- * @{Follow#AI_FORMATION.New}(): Creates a new AI_FORMATION object from a @{Wrapper.Group#GROUP} for a @{Wrapper.Client#CLIENT} or a @{Wrapper.Unit#UNIT}, with an optional briefing text. +-- * @{#AI_FORMATION.New}(): Creates a new AI_FORMATION object from a @{Wrapper.Group#GROUP} for a @{Wrapper.Client#CLIENT} or a @{Wrapper.Unit#UNIT}, with an optional briefing text. -- -- ## Formation methods -- -- The following methods can be used to set or change the formation: -- --- * @{AI_Formation#AI_FORMATION.FormationLine}(): Form a line formation (core formation function). --- * @{AI_Formation#AI_FORMATION.FormationTrail}(): Form a trail formation. --- * @{AI_Formation#AI_FORMATION.FormationLeftLine}(): Form a left line formation. --- * @{AI_Formation#AI_FORMATION.FormationRightLine}(): Form a right line formation. --- * @{AI_Formation#AI_FORMATION.FormationRightWing}(): Form a right wing formation. --- * @{AI_Formation#AI_FORMATION.FormationLeftWing}(): Form a left wing formation. --- * @{AI_Formation#AI_FORMATION.FormationCenterWing}(): Form a center wing formation. --- * @{AI_Formation#AI_FORMATION.FormationCenterVic}(): Form a Vic formation (same as CenterWing. --- * @{AI_Formation#AI_FORMATION.FormationCenterBoxed}(): Form a center boxed formation. +-- * @{#AI_FORMATION.FormationLine}(): Form a line formation (core formation function). +-- * @{#AI_FORMATION.FormationTrail}(): Form a trail formation. +-- * @{#AI_FORMATION.FormationLeftLine}(): Form a left line formation. +-- * @{#AI_FORMATION.FormationRightLine}(): Form a right line formation. +-- * @{#AI_FORMATION.FormationRightWing}(): Form a right wing formation. +-- * @{#AI_FORMATION.FormationLeftWing}(): Form a left wing formation. +-- * @{#AI_FORMATION.FormationCenterWing}(): Form a center wing formation. +-- * @{#AI_FORMATION.FormationCenterVic}(): Form a Vic formation (same as CenterWing. +-- * @{#AI_FORMATION.FormationCenterBoxed}(): Form a center boxed formation. -- -- ## Randomization -- --- Use the method @{AI_Formation#AI_FORMATION.SetFlightRandomization}() to simulate the formation flying errors that pilots make while in formation. Is a range set in meters. +-- Use the method @{AI.AI_Formation#AI_FORMATION.SetFlightRandomization}() to simulate the formation flying errors that pilots make while in formation. Is a range set in meters. -- -- @usage -- local FollowGroupSet = SET_GROUP:New():FilterCategories("plane"):FilterCoalitions("blue"):FilterPrefixes("Follow"):FilterStart() @@ -901,7 +901,7 @@ function AI_FORMATION:onafterFormationBox( FollowGroupSet, From , Event , To, XS end ---- Use the method @{AI_Formation#AI_FORMATION.SetFlightRandomization}() to make the air units in your formation randomize their flight a bit while in formation. +--- Use the method @{AI.AI_Formation#AI_FORMATION.SetFlightRandomization}() to make the air units in your formation randomize their flight a bit while in formation. -- @param #AI_FORMATION self -- @param #number FlightRandomization The formation flying errors that pilots can make while in formation. Is a range set in meters. -- @return #AI_FORMATION diff --git a/Moose Development/Moose/Actions/Act_Account.lua b/Moose Development/Moose/Actions/Act_Account.lua index 0d057dc24..b0d26c9ea 100644 --- a/Moose Development/Moose/Actions/Act_Account.lua +++ b/Moose Development/Moose/Actions/Act_Account.lua @@ -9,7 +9,7 @@ do -- ACT_ACCOUNT - --- # @{#ACT_ACCOUNT} FSM class, extends @{Fsm#FSM_PROCESS} + --- # @{#ACT_ACCOUNT} FSM class, extends @{Core.Fsm#FSM_PROCESS} -- -- ## ACT_ACCOUNT state machine: -- @@ -138,7 +138,7 @@ end -- ACT_ACCOUNT do -- ACT_ACCOUNT_DEADS - --- # @{#ACT_ACCOUNT_DEADS} FSM class, extends @{Fsm.Account#ACT_ACCOUNT} + --- # @{#ACT_ACCOUNT_DEADS} FSM class, extends @{Core.Fsm.Account#ACT_ACCOUNT} -- -- The ACT_ACCOUNT_DEADS class accounts (detects, counts and reports) successful kills of DCS units. -- The process is given a @{Set} of units that will be tracked upon successful destruction. diff --git a/Moose Development/Moose/Actions/Act_Assign.lua b/Moose Development/Moose/Actions/Act_Assign.lua index 5e8984b18..969009ab1 100644 --- a/Moose Development/Moose/Actions/Act_Assign.lua +++ b/Moose Development/Moose/Actions/Act_Assign.lua @@ -2,7 +2,7 @@ -- -- === -- --- # @{#ACT_ASSIGN} FSM template class, extends @{Fsm#FSM_PROCESS} +-- # @{#ACT_ASSIGN} FSM template class, extends @{Core.Fsm#FSM_PROCESS} -- -- ## ACT_ASSIGN state machine: -- @@ -54,7 +54,7 @@ -- -- === -- --- # 1) @{#ACT_ASSIGN_ACCEPT} class, extends @{Fsm.Assign#ACT_ASSIGN} +-- # 1) @{#ACT_ASSIGN_ACCEPT} class, extends @{Core.Fsm.Assign#ACT_ASSIGN} -- -- The ACT_ASSIGN_ACCEPT class accepts by default a task for a player. No player intervention is allowed to reject the task. -- @@ -64,7 +64,7 @@ -- -- === -- --- # 2) @{#ACT_ASSIGN_MENU_ACCEPT} class, extends @{Fsm.Assign#ACT_ASSIGN} +-- # 2) @{#ACT_ASSIGN_MENU_ACCEPT} class, extends @{Core.Fsm.Assign#ACT_ASSIGN} -- -- The ACT_ASSIGN_MENU_ACCEPT class accepts a task when the player accepts the task through an added menu option. -- This assignment type is useful to conditionally allow the player to choose whether or not he would accept the task. diff --git a/Moose Development/Moose/Actions/Act_Assist.lua b/Moose Development/Moose/Actions/Act_Assist.lua index b5a473fda..f9cd5fc3a 100644 --- a/Moose Development/Moose/Actions/Act_Assist.lua +++ b/Moose Development/Moose/Actions/Act_Assist.lua @@ -48,7 +48,7 @@ -- -- === -- --- # 1) @{#ACT_ASSIST_SMOKE_TARGETS_ZONE} class, extends @{Fsm.Route#ACT_ASSIST} +-- # 1) @{#ACT_ASSIST_SMOKE_TARGETS_ZONE} class, extends @{Core.Fsm.Route#ACT_ASSIST} -- -- The ACT_ASSIST_SMOKE_TARGETS_ZONE class implements the core functions to smoke targets in a @{Zone}. -- The targets are smoked within a certain range around each target, simulating a realistic smoking behaviour. diff --git a/Moose Development/Moose/Actions/Act_Route.lua b/Moose Development/Moose/Actions/Act_Route.lua index 87ee9a72e..9f858879f 100644 --- a/Moose Development/Moose/Actions/Act_Route.lua +++ b/Moose Development/Moose/Actions/Act_Route.lua @@ -2,7 +2,7 @@ -- -- === -- --- # @{#ACT_ROUTE} FSM class, extends @{Fsm#FSM_PROCESS} +-- # @{#ACT_ROUTE} FSM class, extends @{Core.Fsm#FSM_PROCESS} -- -- ## ACT_ROUTE state machine: -- @@ -60,7 +60,7 @@ -- -- === -- --- # 1) @{#ACT_ROUTE_ZONE} class, extends @{Fsm.Route#ACT_ROUTE} +-- # 1) @{#ACT_ROUTE_ZONE} class, extends @{Core.Fsm.Route#ACT_ROUTE} -- -- The ACT_ROUTE_ZONE class implements the core functions to route an AIR @{Wrapper.Controllable} player @{Wrapper.Unit} to a @{Zone}. -- The player receives on perioding times messages with the coordinates of the route to follow. diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index f5570dd18..8add3c374 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -36,14 +36,14 @@ -- The cargo must be in the **Loaded** state. -- @function [parent=#CARGO] UnBoard -- @param #CARGO self --- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location. +-- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Core.Point#POINT_VEC2) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location. --- UnBoards the cargo to a Carrier. The event will create a movement (= running or driving) of the cargo from the Carrier. -- The cargo must be in the **Loaded** state. -- @function [parent=#CARGO] __UnBoard -- @param #CARGO self -- @param #number DelaySeconds The amount of seconds to delay the action. --- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location. +-- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Core.Point#POINT_VEC2) to where the cargo should run after onboarding. If not provided, the cargo will run to 60 meters behind the Carrier location. -- Load @@ -68,14 +68,14 @@ -- The cargo must be in the **Loaded** state. -- @function [parent=#CARGO] UnLoad -- @param #CARGO self --- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location. +-- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Core.Point#POINT_VEC2) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location. --- UnLoads the cargo to a Carrier. The event will unload the cargo from the Carrier. There will be no movement simulated of the cargo loading. -- The cargo must be in the **Loaded** state. -- @function [parent=#CARGO] __UnLoad -- @param #CARGO self -- @param #number DelaySeconds The amount of seconds to delay the action. --- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Point#POINT_VEC2) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location. +-- @param Core.Point#POINT_VEC2 ToPointVec2 (optional) @{Core.Point#POINT_VEC2) to where the cargo will be placed after unloading. If not provided, the cargo will be placed 60 meters behind the Carrier location. -- State Transition Functions diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index 996376961..83d9cd5a7 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -122,7 +122,7 @@ local _ClassID = 0 -- ### 1.3.2 Event Handling of DCS Events -- -- Once the class is subscribed to the event, an **Event Handling** method on the object or class needs to be written that will be called --- when the DCS event occurs. The Event Handling method receives an @{Event#EVENTDATA} structure, which contains a lot of information +-- when the DCS event occurs. The Event Handling method receives an @{Core.Event#EVENTDATA} structure, which contains a lot of information -- about the event that occurred. -- -- Find below an example of the prototype how to write an event handling function for two units: diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index f61e0ca40..4585a965b 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -72,7 +72,7 @@ -- ### 1.3.2 Event Handling of DCS Events -- -- Once the class is subscribed to the event, an **Event Handling** method on the object or class needs to be written that will be called --- when the DCS event occurs. The Event Handling method receives an @{Event#EVENTDATA} structure, which contains a lot of information +-- when the DCS event occurs. The Event Handling method receives an @{Core.Event#EVENTDATA} structure, which contains a lot of information -- about the event that occurred. -- -- Find below an example of the prototype how to write an event handling function for two units: @@ -114,7 +114,7 @@ -- -- # 3) EVENTDATA type -- --- The @{Event#EVENTDATA} structure contains all the fields that are populated with event information before +-- The @{Core.Event#EVENTDATA} structure contains all the fields that are populated with event information before -- an Event Handler method is being called by the event dispatcher. -- The Event Handler received the EVENTDATA object as a parameter, and can be used to investigate further the different events. -- There are basically 4 main categories of information stored in the EVENTDATA structure: @@ -229,7 +229,7 @@ EVENTS = { -- -- @field DCS#Unit initiator (UNIT/STATIC/SCENERY) The initiating @{DCS#Unit} or @{DCS#StaticObject}. -- @field DCS#Object.Category IniObjectCategory (UNIT/STATIC/SCENERY) The initiator object category ( Object.Category.UNIT or Object.Category.STATIC ). --- @field DCS#Unit IniDCSUnit (UNIT/STATIC) The initiating @{DCS#Unit} or @{DCSStaticObject#StaticObject}. +-- @field DCS#Unit IniDCSUnit (UNIT/STATIC) The initiating @{DCS#Unit} or @{DCS#StaticObject}. -- @field #string IniDCSUnitName (UNIT/STATIC) The initiating Unit name. -- @field Wrapper.Unit#UNIT IniUnit (UNIT/STATIC) The initiating MOOSE wrapper @{Wrapper.Unit#UNIT} of the initiator Unit object. -- @field #string IniUnitName (UNIT/STATIC) The initiating UNIT name (same as IniDCSUnitName). @@ -242,9 +242,9 @@ EVENTS = { -- @field DCS#Unit.Category IniCategory (UNIT) The category of the initiator. -- @field #string IniTypeName (UNIT) The type name of the initiator. -- --- @field DCS#Unit target (UNIT/STATIC) The target @{DCS#Unit} or @{DCSStaticObject#StaticObject}. +-- @field DCS#Unit target (UNIT/STATIC) The target @{DCS#Unit} or @{DCS#StaticObject}. -- @field DCS#Object.Category TgtObjectCategory (UNIT/STATIC) The target object category ( Object.Category.UNIT or Object.Category.STATIC ). --- @field DCS#Unit TgtDCSUnit (UNIT/STATIC) The target @{DCS#Unit} or @{DCSStaticObject#StaticObject}. +-- @field DCS#Unit TgtDCSUnit (UNIT/STATIC) The target @{DCS#Unit} or @{DCS#StaticObject}. -- @field #string TgtDCSUnitName (UNIT/STATIC) The target Unit name. -- @field Wrapper.Unit#UNIT TgtUnit (UNIT/STATIC) The target MOOSE wrapper @{Wrapper.Unit#UNIT} of the target Unit object. -- @field #string TgtUnitName (UNIT/STATIC) The target UNIT name (same as TgtDCSUnitName). diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 926284332..bb58abaac 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -15,26 +15,26 @@ -- -- ## MESSAGE construction -- --- Messages are created with @{Message#MESSAGE.New}. Note that when the MESSAGE object is created, no message is sent yet. +-- Messages are created with @{#MESSAGE.New}. Note that when the MESSAGE object is created, no message is sent yet. -- To send messages, you need to use the To functions. -- -- ## Send messages to an audience -- -- Messages are sent: -- --- * To a @{Client} using @{Message#MESSAGE.ToClient}(). --- * To a @{Wrapper.Group} using @{Message#MESSAGE.ToGroup}() --- * To a coalition using @{Message#MESSAGE.ToCoalition}(). --- * To the red coalition using @{Message#MESSAGE.ToRed}(). --- * To the blue coalition using @{Message#MESSAGE.ToBlue}(). --- * To all Players using @{Message#MESSAGE.ToAll}(). +-- * To a @{Client} using @{#MESSAGE.ToClient}(). +-- * To a @{Wrapper.Group} using @{#MESSAGE.ToGroup}() +-- * To a coalition using @{#MESSAGE.ToCoalition}(). +-- * To the red coalition using @{#MESSAGE.ToRed}(). +-- * To the blue coalition using @{#MESSAGE.ToBlue}(). +-- * To all Players using @{#MESSAGE.ToAll}(). -- -- ## Send conditionally to an audience -- -- Messages can be sent conditionally to an audience (when a condition is true): -- --- * To all players using @{Message#MESSAGE.ToAllIf}(). --- * To a coalition using @{Message#MESSAGE.ToCoalitionIf}(). +-- * To all players using @{#MESSAGE.ToAllIf}(). +-- * To a coalition using @{#MESSAGE.ToCoalitionIf}(). -- -- === -- diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 632c1007c..faf1a49d6 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -43,8 +43,8 @@ do -- COORDINATE -- A new COORDINATE object can be created with: -- -- * @{#COORDINATE.New}(): a 3D point. - -- * @{#COORDINATE.NewFromVec2}(): a 2D point created from a @{DCSTypes#Vec2}. - -- * @{#COORDINATE.NewFromVec3}(): a 3D point created from a @{DCSTypes#Vec3}. + -- * @{#COORDINATE.NewFromVec2}(): a 2D point created from a @{DCS#Vec2}. + -- * @{#COORDINATE.NewFromVec3}(): a 3D point created from a @{DCS#Vec3}. -- -- ## Create waypoints for routes -- @@ -1610,7 +1610,7 @@ do -- POINT_VEC3 -- A new POINT_VEC3 object can be created with: -- -- * @{#POINT_VEC3.New}(): a 3D point. - -- * @{#POINT_VEC3.NewFromVec3}(): a 3D point created from a @{DCSTypes#Vec3}. + -- * @{#POINT_VEC3.NewFromVec3}(): a 3D point created from a @{DCS#Vec3}. -- -- -- ## Manupulate the X, Y, Z coordinates of the POINT_VEC3 @@ -1814,8 +1814,8 @@ do -- POINT_VEC2 -- -- A new POINT_VEC2 instance can be created with: -- - -- * @{Point#POINT_VEC2.New}(): a 2D point, taking an additional height parameter. - -- * @{Point#POINT_VEC2.NewFromVec2}(): a 2D point created from a @{DCSTypes#Vec2}. + -- * @{Core.Point#POINT_VEC2.New}(): a 2D point, taking an additional height parameter. + -- * @{Core.Point#POINT_VEC2.NewFromVec2}(): a 2D point created from a @{DCS#Vec2}. -- -- ## Manupulate the X, Altitude, Y coordinates of the 2D point -- diff --git a/Moose Development/Moose/Core/Radio.lua b/Moose Development/Moose/Core/Radio.lua index 85dc9162a..77def8fa6 100644 --- a/Moose Development/Moose/Core/Radio.lua +++ b/Moose Development/Moose/Core/Radio.lua @@ -16,10 +16,10 @@ -- * They need to be added in .\l10n\DEFAULT\ in you .miz file (wich can be decompressed like a .zip file), -- * For simplicty sake, you can **let DCS' Mission Editor add the file** itself, by creating a new Trigger with the action "Sound to Country", and choosing your sound file and a country you don't use in your mission. -- --- Due to weird DCS quirks, **radio communications behave differently** if sent by a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP} or by any other @{Positionable#POSITIONABLE} +-- Due to weird DCS quirks, **radio communications behave differently** if sent by a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP} or by any other @{Wrapper.Positionable#POSITIONABLE} -- -- * If the transmitter is a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}, DCS will set the power of the transmission automatically, --- * If the transmitter is any other @{Positionable#POSITIONABLE}, the transmisison can't be subtitled or looped. +-- * If the transmitter is any other @{Wrapper.Positionable#POSITIONABLE}, the transmisison can't be subtitled or looped. -- -- Note that obviously, the **frequency** and the **modulation** of the transmission are important only if the players are piloting an **Advanced System Modelling** enabled aircraft, -- like the A10C or the Mirage 2000C. They will **hear the transmission** if they are tuned on the **right frequency and modulation** (and if they are close enough - more on that below). @@ -40,11 +40,11 @@ -- -- There are 3 steps to a successful radio transmission. -- --- * First, you need to **"add a @{#RADIO} object** to your @{Positionable#POSITIONABLE}. This is done using the @{Positionable#POSITIONABLE.GetRadio}() function, +-- * First, you need to **"add a @{#RADIO} object** to your @{Wrapper.Positionable#POSITIONABLE}. This is done using the @{Wrapper.Positionable#POSITIONABLE.GetRadio}() function, -- * Then, you will **set the relevant parameters** to the transmission (see below), -- * When done, you can actually **broadcast the transmission** (i.e. play the sound) with the @{RADIO.Broadcast}() function. -- --- Methods to set relevant parameters for both a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP} or any other @{Positionable#POSITIONABLE} +-- Methods to set relevant parameters for both a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP} or any other @{Wrapper.Positionable#POSITIONABLE} -- -- * @{#RADIO.SetFileName}() : Sets the file name of your sound file (e.g. "Noise.ogg"), -- * @{#RADIO.SetFrequency}() : Sets the frequency of your transmission. @@ -56,14 +56,14 @@ -- * @{#RADIO.SetSubtitle}() : Set both the subtitle and its duration, -- * @{#RADIO.NewUnitTransmission}() : Shortcut to set all the relevant parameters in one method call -- --- Additional Methods to set relevant parameters if the transmiter is any other @{Positionable#POSITIONABLE} +-- Additional Methods to set relevant parameters if the transmiter is any other @{Wrapper.Positionable#POSITIONABLE} -- -- * @{#RADIO.SetPower}() : Sets the power of the antenna in Watts -- * @{#RADIO.NewGenericTransmission}() : Shortcut to set all the relevant parameters in one method call -- -- What is this power thing ? -- --- * If your transmission is sent by a @{Positionable#POSITIONABLE} other than a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}, you can set the power of the antenna, +-- * If your transmission is sent by a @{Wrapper.Positionable#POSITIONABLE} other than a @{Wrapper.Unit#UNIT} or a @{Wrapper.Group#GROUP}, you can set the power of the antenna, -- * Otherwise, DCS sets it automatically, depending on what's available on your Unit, -- * If the player gets **too far** from the transmiter, or if the antenna is **too weak**, the transmission will **fade** and **become noisyer**, -- * This an automated DCS calculation you have no say on, @@ -92,7 +92,7 @@ RADIO = { } --- Create a new RADIO Object. This doesn't broadcast a transmission, though, use @{#RADIO.Broadcast} to actually broadcast --- If you want to create a RADIO, you probably should use @{Positionable#POSITIONABLE.GetRadio}() instead +-- If you want to create a RADIO, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetRadio}() instead -- @param #RADIO self -- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities. -- @return #RADIO Radio @@ -261,7 +261,7 @@ end --- Create a new transmission, that is to say, populate the RADIO with relevant data -- In this function the data is especially relevant if the broadcaster is a UNIT or a GROUP, --- but it will work for any @{Positionable#POSITIONABLE}. +-- but it will work for any @{Wrapper.Positionable#POSITIONABLE}. -- Only the RADIO and the Filename are mandatory. -- @param #RADIO self -- @param #string FileName @@ -286,7 +286,7 @@ end --- Actually Broadcast the transmission -- * The Radio has to be populated with the new transmission before broadcasting. --- * Please use RADIO setters or either @{Radio#RADIO.NewGenericTransmission} or @{Radio#RADIO.NewUnitTransmission} +-- * Please use RADIO setters or either @{#RADIO.NewGenericTransmission} or @{#RADIO.NewUnitTransmission} -- * This class is in fact pretty smart, it determines the right DCS function to use depending on the type of POSITIONABLE -- * If the POSITIONABLE is not a UNIT or a GROUP, we use the generic (but limited) trigger.action.radioTransmission() -- * If the POSITIONABLE is a UNIT or a GROUP, we use the "TransmitMessage" Command @@ -338,7 +338,7 @@ function RADIO:StopBroadcast() end ---- After attaching a @{#BEACON} to your @{Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want. +--- After attaching a @{#BEACON} to your @{Wrapper.Positionable#POSITIONABLE}, you need to select the right function to activate the kind of beacon you want. -- There are two types of BEACONs available : the AA TACAN Beacon and the general purpose Radio Beacon. -- Note that in both case, you can set an optional parameter : the `BeaconDuration`. This can be very usefull to simulate the battery time if your BEACON is -- attach to a cargo crate, for exemple. @@ -350,8 +350,8 @@ end -- -- ## General Purpose Radio Beacon usage -- --- This beacon will work with any @{Positionable#POSITIONABLE}, but **it won't follow the @{Positionable#POSITIONABLE}** ! This means that you should only use it with --- @{Positionable#POSITIONABLE} that don't move, or move very slowly. Use @{#BEACON:RadioBeacon}() to set the beacon parameters and start the beacon. +-- This beacon will work with any @{Wrapper.Positionable#POSITIONABLE}, but **it won't follow the @{Wrapper.Positionable#POSITIONABLE}** ! This means that you should only use it with +-- @{Wrapper.Positionable#POSITIONABLE} that don't move, or move very slowly. Use @{#BEACON:RadioBeacon}() to set the beacon parameters and start the beacon. -- Use @{#BEACON:StopRadioBeacon}() to stop it. -- -- @type BEACON @@ -361,7 +361,7 @@ BEACON = { } --- Create a new BEACON Object. This doesn't activate the beacon, though, use @{#BEACON.AATACAN} or @{#BEACON.Generic} --- If you want to create a BEACON, you probably should use @{Positionable#POSITIONABLE.GetBeacon}() instead. +-- If you want to create a BEACON, you probably should use @{Wrapper.Positionable#POSITIONABLE.GetBeacon}() instead. -- @param #BEACON self -- @param Wrapper.Positionable#POSITIONABLE Positionable The @{Positionable} that will receive radio capabilities. -- @return #BEACON Beacon diff --git a/Moose Development/Moose/Core/ScheduleDispatcher.lua b/Moose Development/Moose/Core/ScheduleDispatcher.lua index a9d27adbc..8173c85c8 100644 --- a/Moose Development/Moose/Core/ScheduleDispatcher.lua +++ b/Moose Development/Moose/Core/ScheduleDispatcher.lua @@ -22,7 +22,7 @@ -- -- The SCHEDULEDISPATCHER allows multiple scheduled functions to be planned and executed for one SCHEDULER object. -- The SCHEDULER object therefore keeps a table of "CallID's", which are returned after each planning of a new scheduled function by the SCHEDULEDISPATCHER. --- The SCHEDULER object plans new scheduled functions through the @{Scheduler#SCHEDULER.Schedule}() method. +-- The SCHEDULER object plans new scheduled functions through the @{Core.Scheduler#SCHEDULER.Schedule}() method. -- The Schedule() method returns the CallID that is the reference ID for each planned schedule. -- -- === diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 2209dcad1..78607d83d 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -381,9 +381,9 @@ function SET_BASE:FilterStop() return self end ---- Iterate the SET_BASE while identifying the nearest object from a @{Point#POINT_VEC2}. +--- Iterate the SET_BASE while identifying the nearest object from a @{Core.Point#POINT_VEC2}. -- @param #SET_BASE self --- @param Core.Point#POINT_VEC2 PointVec2 A @{Point#POINT_VEC2} object from where to evaluate the closest object in the set. +-- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest object in the set. -- @return Core.Base#BASE The closest object. function SET_BASE:FindNearestObjectFromPointVec2( PointVec2 ) self:F2( PointVec2 ) @@ -852,9 +852,9 @@ function SET_GROUP:FindGroup( GroupName ) return GroupFound end ---- Iterate the SET_GROUP while identifying the nearest object from a @{Point#POINT_VEC2}. +--- Iterate the SET_GROUP while identifying the nearest object from a @{Core.Point#POINT_VEC2}. -- @param #SET_GROUP self --- @param Core.Point#POINT_VEC2 PointVec2 A @{Point#POINT_VEC2} object from where to evaluate the closest object in the set. +-- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest object in the set. -- @return Wrapper.Group#GROUP The closest group. function SET_GROUP:FindNearestGroupFromPointVec2( PointVec2 ) self:F2( PointVec2 ) @@ -4125,9 +4125,9 @@ function SET_AIRBASE:ForEachAirbase( IteratorFunction, ... ) return self end ---- Iterate the SET_AIRBASE while identifying the nearest @{Wrapper.Airbase#AIRBASE} from a @{Point#POINT_VEC2}. +--- Iterate the SET_AIRBASE while identifying the nearest @{Wrapper.Airbase#AIRBASE} from a @{Core.Point#POINT_VEC2}. -- @param #SET_AIRBASE self --- @param Core.Point#POINT_VEC2 PointVec2 A @{Point#POINT_VEC2} object from where to evaluate the closest @{Wrapper.Airbase#AIRBASE}. +-- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest @{Wrapper.Airbase#AIRBASE}. -- @return Wrapper.Airbase#AIRBASE The closest @{Wrapper.Airbase#AIRBASE}. function SET_AIRBASE:FindNearestAirbaseFromPointVec2( PointVec2 ) self:F2( PointVec2 ) @@ -4440,10 +4440,10 @@ function SET_CARGO:ForEachCargo( IteratorFunction, ... ) --R2.1 return self end ---- (R2.1) Iterate the SET_CARGO while identifying the nearest @{Cargo#CARGO} from a @{Point#POINT_VEC2}. +--- (R2.1) Iterate the SET_CARGO while identifying the nearest @{Cargo.Cargo#CARGO} from a @{Core.Point#POINT_VEC2}. -- @param #SET_CARGO self --- @param Core.Point#POINT_VEC2 PointVec2 A @{Point#POINT_VEC2} object from where to evaluate the closest @{Cargo#CARGO}. --- @return Wrapper.Cargo#CARGO The closest @{Cargo#CARGO}. +-- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest @{Cargo.Cargo#CARGO}. +-- @return Wrapper.Cargo#CARGO The closest @{Cargo.Cargo#CARGO}. function SET_CARGO:FindNearestCargoFromPointVec2( PointVec2 ) --R2.1 self:F2( PointVec2 ) @@ -4480,36 +4480,36 @@ function SET_CARGO:FirstCargoWithStateAndNotDeployed( State ) end ---- Iterate the SET_CARGO while identifying the first @{Cargo#CARGO} that is UnLoaded. +--- Iterate the SET_CARGO while identifying the first @{Cargo.Cargo#CARGO} that is UnLoaded. -- @param #SET_CARGO self --- @return Cargo.Cargo#CARGO The first @{Cargo#CARGO}. +-- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. function SET_CARGO:FirstCargoUnLoaded() local FirstCargo = self:FirstCargoWithState( "UnLoaded" ) return FirstCargo end ---- Iterate the SET_CARGO while identifying the first @{Cargo#CARGO} that is UnLoaded and not Deployed. +--- Iterate the SET_CARGO while identifying the first @{Cargo.Cargo#CARGO} that is UnLoaded and not Deployed. -- @param #SET_CARGO self --- @return Cargo.Cargo#CARGO The first @{Cargo#CARGO}. +-- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. function SET_CARGO:FirstCargoUnLoadedAndNotDeployed() local FirstCargo = self:FirstCargoWithStateAndNotDeployed( "UnLoaded" ) return FirstCargo end ---- Iterate the SET_CARGO while identifying the first @{Cargo#CARGO} that is Loaded. +--- Iterate the SET_CARGO while identifying the first @{Cargo.Cargo#CARGO} that is Loaded. -- @param #SET_CARGO self --- @return Cargo.Cargo#CARGO The first @{Cargo#CARGO}. +-- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. function SET_CARGO:FirstCargoLoaded() local FirstCargo = self:FirstCargoWithState( "Loaded" ) return FirstCargo end ---- Iterate the SET_CARGO while identifying the first @{Cargo#CARGO} that is Deployed. +--- Iterate the SET_CARGO while identifying the first @{Cargo.Cargo#CARGO} that is Deployed. -- @param #SET_CARGO self --- @return Cargo.Cargo#CARGO The first @{Cargo#CARGO}. +-- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. function SET_CARGO:FirstCargoDeployed() local FirstCargo = self:FirstCargoWithState( "Deployed" ) return FirstCargo diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 7cf879f08..b07ea4421 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -184,7 +184,7 @@ -- * @{#SPAWN.SpawnInZone}(): Spawn a new group in a @{Zone}. -- * @{#SPAWN.SpawnAtAirbase}(): Spawn a new group at an @{Wrapper.Airbase}, which can be an airdrome, ship or helipad. -- --- Note that @{#SPAWN.Spawn} and @{#SPAWN.ReSpawn} return a @{GROUP#GROUP.New} object, that contains a reference to the DCSGroup object. +-- Note that @{#SPAWN.Spawn} and @{#SPAWN.ReSpawn} return a @{Wrapper.Group#GROUP.New} object, that contains a reference to the DCSGroup object. -- You can use the @{GROUP} object to do further actions with the DCSGroup. -- -- ### **Scheduled** spawning methods diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 55a1a0412..211c44306 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -180,14 +180,14 @@ function ZONE_BASE:IsPointVec3InZone( PointVec3 ) end ---- Returns the @{DCSTypes#Vec2} coordinate of the zone. +--- Returns the @{DCS#Vec2} coordinate of the zone. -- @param #ZONE_BASE self -- @return #nil. function ZONE_BASE:GetVec2() return nil end ---- Returns a @{Point#POINT_VEC2} of the zone. +--- Returns a @{Core.Point#POINT_VEC2} of the zone. -- @param #ZONE_BASE self -- @param DCS#Distance Height The height to add to the land height where the center of the zone is located. -- @return Core.Point#POINT_VEC2 The PointVec2 of the zone. @@ -204,7 +204,7 @@ function ZONE_BASE:GetPointVec2() end ---- Returns a @{Point#COORDINATE} of the zone. +--- Returns a @{Core.Point#COORDINATE} of the zone. -- @param #ZONE_BASE self -- @return Core.Point#COORDINATE The Coordinate of the zone. function ZONE_BASE:GetCoordinate() @@ -220,7 +220,7 @@ function ZONE_BASE:GetCoordinate() end ---- Returns the @{DCSTypes#Vec3} of the zone. +--- Returns the @{DCS#Vec3} of the zone. -- @param #ZONE_BASE self -- @param DCS#Distance Height The height to add to the land height where the center of the zone is located. -- @return DCS#Vec3 The Vec3 of the zone. @@ -238,7 +238,7 @@ function ZONE_BASE:GetVec3( Height ) return Vec3 end ---- Returns a @{Point#POINT_VEC3} of the zone. +--- Returns a @{Core.Point#POINT_VEC3} of the zone. -- @param #ZONE_BASE self -- @param DCS#Distance Height The height to add to the land height where the center of the zone is located. -- @return Core.Point#POINT_VEC3 The PointVec3 of the zone. @@ -254,7 +254,7 @@ function ZONE_BASE:GetPointVec3( Height ) return PointVec3 end ---- Returns a @{Point#COORDINATE} of the zone. +--- Returns a @{Core.Point#COORDINATE} of the zone. -- @param #ZONE_BASE self -- @param DCS#Distance Height The height to add to the land height where the center of the zone is located. -- @return Core.Point#COORDINATE The Coordinate of the zone. @@ -271,21 +271,21 @@ function ZONE_BASE:GetCoordinate( Height ) --R2.1 end ---- Define a random @{DCSTypes#Vec2} within the zone. +--- Define a random @{DCS#Vec2} within the zone. -- @param #ZONE_BASE self -- @return DCS#Vec2 The Vec2 coordinates. function ZONE_BASE:GetRandomVec2() return nil end ---- Define a random @{Point#POINT_VEC2} within the zone. +--- Define a random @{Core.Point#POINT_VEC2} within the zone. -- @param #ZONE_BASE self -- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. function ZONE_BASE:GetRandomPointVec2() return nil end ---- Define a random @{Point#POINT_VEC3} within the zone. +--- Define a random @{Core.Point#POINT_VEC3} within the zone. -- @param #ZONE_BASE self -- @return Core.Point#POINT_VEC3 The PointVec3 coordinates. function ZONE_BASE:GetRandomPointVec3() @@ -391,17 +391,17 @@ end -- -- ## Manage the location of the zone -- --- * @{#ZONE_RADIUS.SetVec2}(): Sets the @{DCSTypes#Vec2} of the zone. --- * @{#ZONE_RADIUS.GetVec2}(): Returns the @{DCSTypes#Vec2} of the zone. --- * @{#ZONE_RADIUS.GetVec3}(): Returns the @{DCSTypes#Vec3} of the zone, taking an additional height parameter. +-- * @{#ZONE_RADIUS.SetVec2}(): Sets the @{DCS#Vec2} of the zone. +-- * @{#ZONE_RADIUS.GetVec2}(): Returns the @{DCS#Vec2} of the zone. +-- * @{#ZONE_RADIUS.GetVec3}(): Returns the @{DCS#Vec3} of the zone, taking an additional height parameter. -- -- ## Zone point randomization -- -- Various functions exist to find random points within the zone. -- -- * @{#ZONE_RADIUS.GetRandomVec2}(): Gets a random 2D point in the zone. --- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Point#POINT_VEC2} object representing a random 2D point in the zone. --- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Point#POINT_VEC3} object representing a random 3D point in the zone. Note that the height of the point is at landheight. +-- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Core.Point#POINT_VEC2} object representing a random 2D point in the zone. +-- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Core.Point#POINT_VEC3} object representing a random 3D point in the zone. Note that the height of the point is at landheight. -- -- @field #ZONE_RADIUS ZONE_RADIUS = { @@ -557,7 +557,7 @@ function ZONE_RADIUS:SetRadius( Radius ) return self.Radius end ---- Returns the @{DCSTypes#Vec2} of the zone. +--- Returns the @{DCS#Vec2} of the zone. -- @param #ZONE_RADIUS self -- @return DCS#Vec2 The location of the zone. function ZONE_RADIUS:GetVec2() @@ -568,7 +568,7 @@ function ZONE_RADIUS:GetVec2() return self.Vec2 end ---- Sets the @{DCSTypes#Vec2} of the zone. +--- Sets the @{DCS#Vec2} of the zone. -- @param #ZONE_RADIUS self -- @param DCS#Vec2 Vec2 The new location of the zone. -- @return DCS#Vec2 The new location of the zone. @@ -582,7 +582,7 @@ function ZONE_RADIUS:SetVec2( Vec2 ) return self.Vec2 end ---- Returns the @{DCSTypes#Vec3} of the ZONE_RADIUS. +--- Returns the @{DCS#Vec3} of the ZONE_RADIUS. -- @param #ZONE_RADIUS self -- @param DCS#Distance Height The height to add to the land height where the center of the zone is located. -- @return DCS#Vec3 The point of the zone. @@ -879,11 +879,11 @@ function ZONE_RADIUS:GetRandomVec2( inner, outer ) return Point end ---- Returns a @{Point#POINT_VEC2} object reflecting a random 2D location within the zone. +--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. -- @param #ZONE_RADIUS self -- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. --- @return Core.Point#POINT_VEC2 The @{Point#POINT_VEC2} object reflecting the random 3D location within the zone. +-- @return Core.Point#POINT_VEC2 The @{Core.Point#POINT_VEC2} object reflecting the random 3D location within the zone. function ZONE_RADIUS:GetRandomPointVec2( inner, outer ) self:F( self.ZoneName, inner, outer ) @@ -910,11 +910,11 @@ function ZONE_RADIUS:GetRandomVec3( inner, outer ) end ---- Returns a @{Point#POINT_VEC3} object reflecting a random 3D location within the zone. +--- Returns a @{Core.Point#POINT_VEC3} object reflecting a random 3D location within the zone. -- @param #ZONE_RADIUS self -- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. --- @return Core.Point#POINT_VEC3 The @{Point#POINT_VEC3} object reflecting the random 3D location within the zone. +-- @return Core.Point#POINT_VEC3 The @{Core.Point#POINT_VEC3} object reflecting the random 3D location within the zone. function ZONE_RADIUS:GetRandomPointVec3( inner, outer ) self:F( self.ZoneName, inner, outer ) @@ -926,7 +926,7 @@ function ZONE_RADIUS:GetRandomPointVec3( inner, outer ) end ---- Returns a @{Point#COORDINATE} object reflecting a random 3D location within the zone. +--- Returns a @{Core.Point#COORDINATE} object reflecting a random 3D location within the zone. -- @param #ZONE_RADIUS self -- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. @@ -1086,7 +1086,7 @@ function ZONE_UNIT:GetRandomVec2() return RandomVec2 end ---- Returns the @{DCSTypes#Vec3} of the ZONE_UNIT. +--- Returns the @{DCS#Vec3} of the ZONE_UNIT. -- @param #ZONE_UNIT self -- @param DCS#Distance Height The height to add to the land height where the center of the zone is located. -- @return DCS#Vec3 The point of the zone. @@ -1166,11 +1166,11 @@ function ZONE_GROUP:GetRandomVec2() return Point end ---- Returns a @{Point#POINT_VEC2} object reflecting a random 2D location within the zone. +--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. -- @param #ZONE_GROUP self -- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. --- @return Core.Point#POINT_VEC2 The @{Point#POINT_VEC2} object reflecting the random 3D location within the zone. +-- @return Core.Point#POINT_VEC2 The @{Core.Point#POINT_VEC2} object reflecting the random 3D location within the zone. function ZONE_GROUP:GetRandomPointVec2( inner, outer ) self:F( self.ZoneName, inner, outer ) @@ -1183,7 +1183,7 @@ end --- @type ZONE_POLYGON_BASE --- --@field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCSTypes#Vec2}. +-- --@field #ZONE_POLYGON_BASE.ListVec2 Polygon The polygon defined by an array of @{DCS#Vec2}. -- @extends #ZONE_BASE @@ -1196,8 +1196,8 @@ end -- Various functions exist to find random points within the zone. -- -- * @{#ZONE_POLYGON_BASE.GetRandomVec2}(): Gets a random 2D point in the zone. --- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Point#POINT_VEC2} object representing a random 2D point within the zone. --- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Point#POINT_VEC3} object representing a random 3D point at landheight within the zone. +-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Core.Point#POINT_VEC2} object representing a random 2D point within the zone. +-- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Core.Point#POINT_VEC3} object representing a random 3D point at landheight within the zone. -- -- @field #ZONE_POLYGON_BASE ZONE_POLYGON_BASE = { @@ -1208,11 +1208,11 @@ ZONE_POLYGON_BASE = { -- @type ZONE_POLYGON_BASE.ListVec2 -- @list ---- Constructor to create a ZONE_POLYGON_BASE instance, taking the zone name and an array of @{DCSTypes#Vec2}, forming a polygon. +--- Constructor to create a ZONE_POLYGON_BASE instance, taking the zone name and an array of @{DCS#Vec2}, forming a polygon. -- The @{Wrapper.Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected. -- @param #ZONE_POLYGON_BASE self -- @param #string ZoneName Name of the zone. --- @param #ZONE_POLYGON_BASE.ListVec2 PointsArray An array of @{DCSTypes#Vec2}, forming a polygon.. +-- @param #ZONE_POLYGON_BASE.ListVec2 PointsArray An array of @{DCS#Vec2}, forming a polygon.. -- @return #ZONE_POLYGON_BASE self function ZONE_POLYGON_BASE:New( ZoneName, PointsArray ) local self = BASE:Inherit( self, ZONE_BASE:New( ZoneName ) ) @@ -1368,7 +1368,7 @@ function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 ) return InPolygon end ---- Define a random @{DCSTypes#Vec2} within the zone. +--- Define a random @{DCS#Vec2} within the zone. -- @param #ZONE_POLYGON_BASE self -- @return DCS#Vec2 The Vec2 coordinate. function ZONE_POLYGON_BASE:GetRandomVec2() @@ -1394,9 +1394,9 @@ function ZONE_POLYGON_BASE:GetRandomVec2() return Vec2 end ---- Return a @{Point#POINT_VEC2} object representing a random 2D point at landheight within the zone. +--- Return a @{Core.Point#POINT_VEC2} object representing a random 2D point at landheight within the zone. -- @param #ZONE_POLYGON_BASE self --- @return @{Point#POINT_VEC2} +-- @return @{Core.Point#POINT_VEC2} function ZONE_POLYGON_BASE:GetRandomPointVec2() self:F2() @@ -1407,9 +1407,9 @@ function ZONE_POLYGON_BASE:GetRandomPointVec2() return PointVec2 end ---- Return a @{Point#POINT_VEC3} object representing a random 3D point at landheight within the zone. +--- Return a @{Core.Point#POINT_VEC3} object representing a random 3D point at landheight within the zone. -- @param #ZONE_POLYGON_BASE self --- @return @{Point#POINT_VEC3} +-- @return @{Core.Point#POINT_VEC3} function ZONE_POLYGON_BASE:GetRandomPointVec3() self:F2() @@ -1421,7 +1421,7 @@ function ZONE_POLYGON_BASE:GetRandomPointVec3() end ---- Return a @{Point#COORDINATE} object representing a random 3D point at landheight within the zone. +--- Return a @{Core.Point#COORDINATE} object representing a random 3D point at landheight within the zone. -- @param #ZONE_POLYGON_BASE self -- @return Core.Point#COORDINATE function ZONE_POLYGON_BASE:GetRandomCoordinate() diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index acd0811d4..47d63dd8a 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -134,7 +134,7 @@ -- -- ### Parameters: -- --- * *coord*: Coordinates of the target, given as @{Point#COORDINATE} object. +-- * *coord*: Coordinates of the target, given as @{Core.Point#COORDINATE} object. -- * *prio*: Priority of the target. This a number between 1 (high prio) and 100 (low prio). Targets with higher priority are engaged before targets with lower priority. -- * *radius*: Radius in meters which defines the area the ARTY group will attempt to be hitting. Default is 100 meters. -- * *nshells*: Number of shots (shells, rockets, missiles) fired by the group at each engagement of a target. Default is 5. @@ -220,7 +220,7 @@ -- -- ### Parameters -- --- * *coord*: Coordinates where the group should move to given as @{Point#COORDINATE} object. +-- * *coord*: Coordinates where the group should move to given as @{Core.Point#COORDINATE} object. -- * *time*: The time when the move should be executed. This has to be given as a string in the format "hh:mm:ss" (hh=hours, mm=minutes, ss=seconds). -- * *speed*: Speed of the group in km/h. -- * *onroad*: If this parameter is set to true, the group uses mainly roads to get to the commanded coordinates. diff --git a/Moose Development/Moose/Functional/Designate.lua b/Moose Development/Moose/Functional/Designate.lua index c6818c456..f23961e12 100644 --- a/Moose Development/Moose/Functional/Designate.lua +++ b/Moose Development/Moose/Functional/Designate.lua @@ -267,7 +267,7 @@ do -- DESIGNATE -- -- ## 6. Designate Menu Location for a Mission -- - -- You can make DESIGNATE work for a @{Mission#MISSION} object. In this way, the designate menu will not appear in the root of the radio menu, but in the menu of the Mission. + -- You can make DESIGNATE work for a @{Tasking.Mission#MISSION} object. In this way, the designate menu will not appear in the root of the radio menu, but in the menu of the Mission. -- Use the method @{#DESIGNATE.SetMission}() to set the @{Mission} object for the designate function. -- -- ## 7. Status Report diff --git a/Moose Development/Moose/Functional/Scoring.lua b/Moose Development/Moose/Functional/Scoring.lua index 6d0204b22..cea1f948b 100644 --- a/Moose Development/Moose/Functional/Scoring.lua +++ b/Moose Development/Moose/Functional/Scoring.lua @@ -52,7 +52,7 @@ -- Use the radio menu F10 to consult the scores while running the mission. -- Scores can be reported for your user, or an overall score can be reported of all players currently active in the mission. -- --- # 1) @{Scoring#SCORING} class, extends @{Core.Base#BASE} +-- # 1) @{Functional.Scoring#SCORING} class, extends @{Core.Base#BASE} -- -- ## 1.1) Set the destroy score or penalty scale -- diff --git a/Moose Development/Moose/Tasking/DetectionManager.lua b/Moose Development/Moose/Tasking/DetectionManager.lua index 0c76973dd..c27511cde 100644 --- a/Moose Development/Moose/Tasking/DetectionManager.lua +++ b/Moose Development/Moose/Tasking/DetectionManager.lua @@ -2,35 +2,35 @@ -- -- === -- --- The @{DetectionManager#DETECTION_MANAGER} class defines the core functions to report detected objects to groups. +-- The @{#DETECTION_MANAGER} class defines the core functions to report detected objects to groups. -- Reportings can be done in several manners, and it is up to the derived classes if DETECTION_MANAGER to model the reporting behaviour. -- -- 1.1) DETECTION_MANAGER constructor: -- ----------------------------------- --- * @{DetectionManager#DETECTION_MANAGER.New}(): Create a new DETECTION_MANAGER instance. +-- * @{#DETECTION_MANAGER.New}(): Create a new DETECTION_MANAGER instance. -- -- 1.2) DETECTION_MANAGER reporting: -- --------------------------------- --- Derived DETECTION_MANAGER classes will reports detected units using the method @{DetectionManager#DETECTION_MANAGER.ReportDetected}(). This method implements polymorphic behaviour. +-- Derived DETECTION_MANAGER classes will reports detected units using the method @{#DETECTION_MANAGER.ReportDetected}(). This method implements polymorphic behaviour. -- --- The time interval in seconds of the reporting can be changed using the methods @{DetectionManager#DETECTION_MANAGER.SetRefreshTimeInterval}(). --- To control how long a reporting message is displayed, use @{DetectionManager#DETECTION_MANAGER.SetReportDisplayTime}(). --- Derived classes need to implement the method @{DetectionManager#DETECTION_MANAGER.GetReportDisplayTime}() to use the correct display time for displayed messages during a report. +-- The time interval in seconds of the reporting can be changed using the methods @{#DETECTION_MANAGER.SetRefreshTimeInterval}(). +-- To control how long a reporting message is displayed, use @{#DETECTION_MANAGER.SetReportDisplayTime}(). +-- Derived classes need to implement the method @{#DETECTION_MANAGER.GetReportDisplayTime}() to use the correct display time for displayed messages during a report. -- --- Reporting can be started and stopped using the methods @{DetectionManager#DETECTION_MANAGER.StartReporting}() and @{DetectionManager#DETECTION_MANAGER.StopReporting}() respectively. --- If an ad-hoc report is requested, use the method @{DetectionManager#DETECTION_MANAGER#ReportNow}(). +-- Reporting can be started and stopped using the methods @{#DETECTION_MANAGER.StartReporting}() and @{#DETECTION_MANAGER.StopReporting}() respectively. +-- If an ad-hoc report is requested, use the method @{#DETECTION_MANAGER#ReportNow}(). -- -- The default reporting interval is every 60 seconds. The reporting messages are displayed 15 seconds. -- -- === -- --- 2) @{DetectionManager#DETECTION_REPORTING} class, extends @{DetectionManager#DETECTION_MANAGER} +-- 2) @{#DETECTION_REPORTING} class, extends @{#DETECTION_MANAGER} -- === --- The @{DetectionManager#DETECTION_REPORTING} class implements detected units reporting. Reporting can be controlled using the reporting methods available in the @{DetectionManager#DETECTION_MANAGER} class. +-- The @{#DETECTION_REPORTING} class implements detected units reporting. Reporting can be controlled using the reporting methods available in the @{Tasking.DetectionManager#DETECTION_MANAGER} class. -- -- 2.1) DETECTION_REPORTING constructor: -- ------------------------------- --- The @{DetectionManager#DETECTION_REPORTING.New}() method creates a new DETECTION_REPORTING instance. +-- The @{#DETECTION_REPORTING.New}() method creates a new DETECTION_REPORTING instance. -- -- -- === diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index 962770e14..f76cbf7fe 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -31,9 +31,9 @@ -- * @{#TASK.AssignToGroup}():Assign a task to a group (of players). -- * @{#TASK.AddProcess}():Add a @{Process} to a task. -- * @{#TASK.RemoveProcesses}():Remove a running @{Process} from a running task. --- * @{#TASK.SetStateMachine}():Set a @{Fsm} to a task. --- * @{#TASK.RemoveStateMachine}():Remove @{Fsm} from a task. --- * @{#TASK.HasStateMachine}():Enquire if the task has a @{Fsm} +-- * @{#TASK.SetStateMachine}():Set a @{Core.Fsm} to a task. +-- * @{#TASK.RemoveStateMachine}():Remove @{Core.Fsm} from a task. +-- * @{#TASK.HasStateMachine}():Enquire if the task has a @{Core.Fsm} -- * @{#TASK.AssignToUnit}(): Assign a task to a unit. (Needs to be implemented in the derived classes from @{#TASK}. -- * @{#TASK.UnAssignFromUnit}(): Unassign the task from a unit. -- * @{#TASK.SetTimeOut}(): Set timer in seconds before task gets cancelled if not assigned. diff --git a/Moose Development/Moose/Tasking/TaskZoneCapture.lua b/Moose Development/Moose/Tasking/TaskZoneCapture.lua index e32ea58bf..fb1ab81c5 100644 --- a/Moose Development/Moose/Tasking/TaskZoneCapture.lua +++ b/Moose Development/Moose/Tasking/TaskZoneCapture.lua @@ -18,10 +18,10 @@ do -- TASK_ZONE_GOAL -- @field Core.ZoneGoal#ZONE_GOAL ZoneGoal -- @extends Tasking.Task#TASK - --- # TASK_ZONE_GOAL class, extends @{Task#TASK} + --- # TASK_ZONE_GOAL class, extends @{Tasking.Task#TASK} -- -- The TASK_ZONE_GOAL class defines the task to protect or capture a protection zone. - -- The TASK_ZONE_GOAL is implemented using a @{Fsm#FSM_TASK}, and has the following statuses: + -- The TASK_ZONE_GOAL is implemented using a @{Core.Fsm#FSM_TASK}, and has the following statuses: -- -- * **None**: Start of the process -- * **Planned**: The A2G task is planned. @@ -167,12 +167,12 @@ do -- TASK_ZONE_CAPTURE -- @field Core.ZoneGoalCoalition#ZONE_GOAL_COALITION ZoneGoal -- @extends #TASK_ZONE_GOAL - --- # TASK_ZONE_CAPTURE class, extends @{TaskZoneGoal#TASK_ZONE_GOAL} + --- # TASK_ZONE_CAPTURE class, extends @{Tasking.TaskZoneGoal#TASK_ZONE_GOAL} -- -- The TASK_ZONE_CAPTURE class defines an Suppression or Extermination of Air Defenses task for a human player to be executed. -- These tasks are important to be executed as they will help to achieve air superiority at the vicinity. -- - -- The TASK_ZONE_CAPTURE is used by the @{Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create SEAD tasks + -- The TASK_ZONE_CAPTURE is used by the @{Tasking.Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create SEAD tasks -- based on detected enemy ground targets. -- -- @field #TASK_ZONE_CAPTURE diff --git a/Moose Development/Moose/Tasking/Task_A2A.lua b/Moose Development/Moose/Tasking/Task_A2A.lua index 0a18e28cc..5fb22050b 100644 --- a/Moose Development/Moose/Tasking/Task_A2A.lua +++ b/Moose Development/Moose/Tasking/Task_A2A.lua @@ -19,8 +19,8 @@ do -- TASK_A2A -- @extends Tasking.Task#TASK --- Defines Air To Air tasks for a @{Set} of Target Units, - -- based on the tasking capabilities defined in @{Task#TASK}. - -- The TASK_A2A is implemented using a @{Fsm#FSM_TASK}, and has the following statuses: + -- based on the tasking capabilities defined in @{Tasking.Task#TASK}. + -- The TASK_A2A is implemented using a @{Core.Fsm#FSM_TASK}, and has the following statuses: -- -- * **None**: Start of the process -- * **Planned**: The A2A task is planned. @@ -364,10 +364,10 @@ do -- TASK_A2A_INTERCEPT --- Defines an intercept task for a human player to be executed. -- When enemy planes need to be intercepted by human players, use this task type to urgen the players to get out there! -- - -- The TASK_A2A_INTERCEPT is used by the @{Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create intercept tasks + -- The TASK_A2A_INTERCEPT is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create intercept tasks -- based on detected airborne enemy targets intruding friendly airspace. -- - -- The task is defined for a @{Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is intercepting the targets. + -- The task is defined for a @{Tasking.Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is intercepting the targets. -- The task is given a name and a briefing, that is used in the menu structure and in the reporting. -- -- @field #TASK_A2A_INTERCEPT @@ -463,10 +463,10 @@ do -- TASK_A2A_SWEEP -- Most likely, these enemy planes are hidden in the mountains or are flying under radar. -- These enemy planes need to be sweeped by human players, and use this task type to urge the players to get out there and find those enemy fighters. -- - -- The TASK_A2A_SWEEP is used by the @{Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create sweep tasks + -- The TASK_A2A_SWEEP is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create sweep tasks -- based on detected airborne enemy targets intruding friendly airspace, for which the detection has been lost for more than 60 seconds. -- - -- The task is defined for a @{Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is sweeping the targets. + -- The task is defined for a @{Tasking.Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is sweeping the targets. -- The task is given a name and a briefing, that is used in the menu structure and in the reporting. -- -- @field #TASK_A2A_SWEEP @@ -570,10 +570,10 @@ do -- TASK_A2A_ENGAGE --- Defines an engage task for a human player to be executed. -- When enemy planes are close to human players, use this task type is used urge the players to get out there! -- - -- The TASK_A2A_ENGAGE is used by the @{Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create engage tasks + -- The TASK_A2A_ENGAGE is used by the @{Tasking.Task_A2A_Dispatcher#TASK_A2A_DISPATCHER} to automatically create engage tasks -- based on detected airborne enemy targets intruding friendly airspace. -- - -- The task is defined for a @{Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is engaging the targets. + -- The task is defined for a @{Tasking.Mission#MISSION}, where a friendly @{Core.Set#SET_GROUP} consisting of GROUPs with one human players each, is engaging the targets. -- The task is given a name and a briefing, that is used in the menu structure and in the reporting. -- -- @field #TASK_A2A_ENGAGE diff --git a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua index 9b68fd38a..ddbb7b148 100644 --- a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua @@ -80,7 +80,7 @@ do -- TASK_A2A_DISPATCHER -- therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map. -- It all depends on what the desired effect is. -- - -- EWR networks are **dynamically constructed**, that is, they form part of the @{Functional#DETECTION_BASE} object that is given as the input parameter of the TASK\_A2A\_DISPATCHER class. + -- EWR networks are **dynamically constructed**, that is, they form part of the @{Functional.Detection#DETECTION_BASE} object that is given as the input parameter of the TASK\_A2A\_DISPATCHER class. -- By defining in a **smart way the names or name prefixes of the groups** with EWR capable units, these groups will be **automatically added or deleted** from the EWR network, -- increasing or decreasing the radar coverage of the Early Warning System. -- diff --git a/Moose Development/Moose/Tasking/Task_A2G.lua b/Moose Development/Moose/Tasking/Task_A2G.lua index d30eff7aa..51294075d 100644 --- a/Moose Development/Moose/Tasking/Task_A2G.lua +++ b/Moose Development/Moose/Tasking/Task_A2G.lua @@ -19,8 +19,8 @@ do -- TASK_A2G -- @extends Tasking.Task#TASK --- The TASK_A2G class defines Air To Ground tasks for a @{Set} of Target Units, - -- based on the tasking capabilities defined in @{Task#TASK}. - -- The TASK_A2G is implemented using a @{Fsm#FSM_TASK}, and has the following statuses: + -- based on the tasking capabilities defined in @{Tasking.Task#TASK}. + -- The TASK_A2G is implemented using a @{Core.Fsm#FSM_TASK}, and has the following statuses: -- -- * **None**: Start of the process -- * **Planned**: The A2G task is planned. @@ -369,7 +369,7 @@ do -- TASK_A2G_SEAD --- Defines an Suppression or Extermination of Air Defenses task for a human player to be executed. -- These tasks are important to be executed as they will help to achieve air superiority at the vicinity. -- - -- The TASK_A2G_SEAD is used by the @{Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create SEAD tasks + -- The TASK_A2G_SEAD is used by the @{Tasking.Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create SEAD tasks -- based on detected enemy ground targets. -- -- @field #TASK_A2G_SEAD @@ -461,7 +461,7 @@ do -- TASK_A2G_BAI -- These tasks are more strategic in nature and are most of the time further away from friendly forces. -- BAI tasks can also be used to express the abscence of friendly forces near the vicinity. -- - -- The TASK_A2G_BAI is used by the @{Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create BAI tasks + -- The TASK_A2G_BAI is used by the @{Tasking.Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create BAI tasks -- based on detected enemy ground targets. -- -- @field #TASK_A2G_BAI @@ -554,7 +554,7 @@ do -- TASK_A2G_CAS -- Defines an Close Air Support task for a human player to be executed. -- Friendly forces will be in the vicinity within 6km from the enemy. -- - -- The TASK_A2G_CAS is used by the @{Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create CAS tasks + -- The TASK_A2G_CAS is used by the @{Tasking.Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create CAS tasks -- based on detected enemy ground targets. -- -- @field #TASK_A2G_CAS diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 200877922..cb6edeb49 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -32,7 +32,7 @@ do -- TASK_CARGO -- @extends Tasking.Task#TASK --- - -- # TASK_CARGO class, extends @{Task#TASK} + -- # TASK_CARGO class, extends @{Tasking.Task#TASK} -- -- ## A flexible tasking system -- @@ -119,7 +119,7 @@ do -- TASK_CARGO -- ## Handle TASK_CARGO Events ... -- -- The TASK_CARGO classes define @{Cargo} transport tasks, - -- based on the tasking capabilities defined in @{Task#TASK}. + -- based on the tasking capabilities defined in @{Tasking.Task#TASK}. -- -- ### Specific TASK_CARGO Events -- @@ -130,7 +130,7 @@ do -- TASK_CARGO -- -- ### Standard TASK_CARGO Events -- - -- The TASK_CARGO is implemented using a @{Statemachine#FSM_TASK}, and has the following standard statuses: + -- The TASK_CARGO is implemented using a @{Core.Fsm#FSM_TASK}, and has the following standard statuses: -- -- * **None**: Start of the process. -- * **Planned**: The cargo task is planned. diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index 1fd738087..7644674da 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -82,7 +82,7 @@ do -- TASK_CARGO_DISPATCHER -- therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map. -- It all depends on what the desired effect is. -- - -- EWR networks are **dynamically constructed**, that is, they form part of the @{Functional#DETECTION_BASE} object that is given as the input parameter of the TASK\_A2A\_DISPATCHER class. + -- EWR networks are **dynamically constructed**, that is, they form part of the @{Functional.Detection#DETECTION_BASE} object that is given as the input parameter of the TASK\_A2A\_DISPATCHER class. -- By defining in a **smart way the names or name prefixes of the groups** with EWR capable units, these groups will be **automatically added or deleted** from the EWR network, -- increasing or decreasing the radar coverage of the Early Warning System. -- diff --git a/Moose Development/Moose/Tasking/Task_Manager.lua b/Moose Development/Moose/Tasking/Task_Manager.lua index dc7f31c62..8c2d76406 100644 --- a/Moose Development/Moose/Tasking/Task_Manager.lua +++ b/Moose Development/Moose/Tasking/Task_Manager.lua @@ -2,25 +2,25 @@ -- -- === -- --- 1) @{Task_Manager#TASK_MANAGER} class, extends @{Fsm#FSM} +-- 1) @{Tasking.Task_Manager#TASK_MANAGER} class, extends @{Core.Fsm#FSM} -- === --- The @{Task_Manager#TASK_MANAGER} class defines the core functions to report tasks to groups. +-- The @{Tasking.Task_Manager#TASK_MANAGER} class defines the core functions to report tasks to groups. -- Reportings can be done in several manners, and it is up to the derived classes if TASK_MANAGER to model the reporting behaviour. -- -- 1.1) TASK_MANAGER constructor: -- ----------------------------------- --- * @{Task_Manager#TASK_MANAGER.New}(): Create a new TASK_MANAGER instance. +-- * @{Tasking.Task_Manager#TASK_MANAGER.New}(): Create a new TASK_MANAGER instance. -- -- 1.2) TASK_MANAGER reporting: -- --------------------------------- --- Derived TASK_MANAGER classes will manage tasks using the method @{Task_Manager#TASK_MANAGER.ManageTasks}(). This method implements polymorphic behaviour. +-- Derived TASK_MANAGER classes will manage tasks using the method @{Tasking.Task_Manager#TASK_MANAGER.ManageTasks}(). This method implements polymorphic behaviour. -- --- The time interval in seconds of the task management can be changed using the methods @{Task_Manager#TASK_MANAGER.SetRefreshTimeInterval}(). --- To control how long a reporting message is displayed, use @{Task_Manager#TASK_MANAGER.SetReportDisplayTime}(). --- Derived classes need to implement the method @{Task_Manager#TASK_MANAGER.GetReportDisplayTime}() to use the correct display time for displayed messages during a report. +-- The time interval in seconds of the task management can be changed using the methods @{Tasking.Task_Manager#TASK_MANAGER.SetRefreshTimeInterval}(). +-- To control how long a reporting message is displayed, use @{Tasking.Task_Manager#TASK_MANAGER.SetReportDisplayTime}(). +-- Derived classes need to implement the method @{Tasking.Task_Manager#TASK_MANAGER.GetReportDisplayTime}() to use the correct display time for displayed messages during a report. -- --- Task management can be started and stopped using the methods @{Task_Manager#TASK_MANAGER.StartTasks}() and @{Task_Manager#TASK_MANAGER.StopTasks}() respectively. --- If an ad-hoc report is requested, use the method @{Task_Manager#TASK_MANAGER#ManageTasks}(). +-- Task management can be started and stopped using the methods @{Tasking.Task_Manager#TASK_MANAGER.StartTasks}() and @{Tasking.Task_Manager#TASK_MANAGER.StopTasks}() respectively. +-- If an ad-hoc report is requested, use the method @{Tasking.Task_Manager#TASK_MANAGER#ManageTasks}(). -- -- The default task management interval is every 60 seconds. -- diff --git a/Moose Development/Moose/Wrapper/Client.lua b/Moose Development/Moose/Wrapper/Client.lua index 7352537f7..7df3c0586 100644 --- a/Moose Development/Moose/Wrapper/Client.lua +++ b/Moose Development/Moose/Wrapper/Client.lua @@ -411,8 +411,8 @@ function CLIENT:IsTransport() return self.ClientTransport end ---- Shows the @{AI_Cargo#CARGO} contained within the CLIENT to the player as a message. --- The @{AI_Cargo#CARGO} is shown using the @{Message#MESSAGE} distribution system. +--- Shows the @{AI.AI_Cargo#CARGO} contained within the CLIENT to the player as a message. +-- The @{AI.AI_Cargo#CARGO} is shown using the @{Core.Message#MESSAGE} distribution system. -- @param #CLIENT self function CLIENT:ShowCargo() self:F() @@ -445,7 +445,7 @@ end -- @param #string Message is the text describing the message. -- @param #number MessageDuration is the duration in seconds that the Message should be displayed. -- @param #string MessageCategory is the category of the message (the title). --- @param #number MessageInterval is the interval in seconds between the display of the @{Message#MESSAGE} when the CLIENT is in the air. +-- @param #number MessageInterval is the interval in seconds between the display of the @{Core.Message#MESSAGE} when the CLIENT is in the air. -- @param #string MessageID is the identifier of the message when displayed with intervals. function CLIENT:Message( Message, MessageDuration, MessageCategory, MessageInterval, MessageID ) self:F( { Message, MessageDuration, MessageCategory, MessageInterval } ) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index dd570df5e..776cf923c 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -185,7 +185,7 @@ function GROUP:GetDCSObject() return nil end ---- Returns the @{DCSTypes#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission. +--- Returns the @{DCS#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission. -- @param Wrapper.Positionable#POSITIONABLE self -- @return DCS#Position The 3D position vectors of the POSITIONABLE. -- @return #nil The POSITIONABLE is not existing or alive. @@ -722,7 +722,7 @@ function GROUP:GetCoordinate() end ---- Returns a random @{DCSTypes#Vec3} vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP. +--- Returns a random @{DCS#Vec3} vector (point in 3D of the UNIT within the mission) within a range around the first UNIT of the GROUP. -- @param #GROUP self -- @param #number Radius -- @return DCS#Vec3 The random 3D point vector around the first UNIT of the GROUP. diff --git a/Moose Development/Moose/Wrapper/Object.lua b/Moose Development/Moose/Wrapper/Object.lua index 1b9b46995..f67ec389e 100644 --- a/Moose Development/Moose/Wrapper/Object.lua +++ b/Moose Development/Moose/Wrapper/Object.lua @@ -27,7 +27,7 @@ -- -- The OBJECT class provides the following functions to construct a OBJECT instance: -- --- * @{Object#OBJECT.New}(): Create a OBJECT instance. +-- * @{Wrapper.Object#OBJECT.New}(): Create a OBJECT instance. -- -- @field #OBJECT OBJECT = { diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index b60ee07be..23b85806a 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -69,7 +69,7 @@ function POSITIONABLE:New( PositionableName ) return self end ---- Returns the @{DCSTypes#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission. +--- Returns the @{DCS#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission. -- @param Wrapper.Positionable#POSITIONABLE self -- @return DCS#Position The 3D position vectors of the POSITIONABLE. -- @return #nil The POSITIONABLE is not existing or alive. @@ -89,7 +89,7 @@ function POSITIONABLE:GetPositionVec3() return nil end ---- Returns the @{DCSTypes#Vec2} vector indicating the point in 2D of the POSITIONABLE within the mission. +--- Returns the @{DCS#Vec2} vector indicating the point in 2D of the POSITIONABLE within the mission. -- @param Wrapper.Positionable#POSITIONABLE self -- @return DCS#Vec2 The 2D point vector of the POSITIONABLE. -- @return #nil The POSITIONABLE is not existing or alive. @@ -185,7 +185,7 @@ function POSITIONABLE:GetCoordinate() end ---- Returns a random @{DCSTypes#Vec3} vector within a range, indicating the point in 3D of the POSITIONABLE within the mission. +--- Returns a random @{DCS#Vec3} vector within a range, indicating the point in 3D of the POSITIONABLE within the mission. -- @param Wrapper.Positionable#POSITIONABLE self -- @param #number Radius -- @return DCS#Vec3 The 3D point vector of the POSITIONABLE. @@ -220,7 +220,7 @@ function POSITIONABLE:GetRandomVec3( Radius ) return nil end ---- Returns the @{DCSTypes#Vec3} vector indicating the 3D vector of the POSITIONABLE within the mission. +--- Returns the @{DCS#Vec3} vector indicating the 3D vector of the POSITIONABLE within the mission. -- @param Wrapper.Positionable#POSITIONABLE self -- @return DCS#Vec3 The 3D point vector of the POSITIONABLE. -- @return #nil The POSITIONABLE is not existing or alive. @@ -713,7 +713,7 @@ function POSITIONABLE:Message( Message, Duration, Name ) return nil end ---- Create a @{Radio#RADIO}, to allow radio transmission for this POSITIONABLE. +--- Create a @{Core.Radio#RADIO}, to allow radio transmission for this POSITIONABLE. -- Set parameters with the methods provided, then use RADIO:Broadcast() to actually broadcast the message -- @param #POSITIONABLE self -- @return Core.Radio#RADIO Radio @@ -722,7 +722,7 @@ function POSITIONABLE:GetRadio() --R2.1 return RADIO:New(self) end ---- Create a @{Radio#BEACON}, to allow this POSITIONABLE to broadcast beacon signals +--- Create a @{Core.Radio#BEACON}, to allow this POSITIONABLE to broadcast beacon signals -- @param #POSITIONABLE self -- @return Core.Radio#RADIO Radio function POSITIONABLE:GetBeacon() --R2.1 diff --git a/Moose Development/Moose/Wrapper/Scenery.lua b/Moose Development/Moose/Wrapper/Scenery.lua index 9a9cb9ff2..01d9af957 100644 --- a/Moose Development/Moose/Wrapper/Scenery.lua +++ b/Moose Development/Moose/Wrapper/Scenery.lua @@ -19,7 +19,7 @@ --- Wrapper class to handle Scenery objects that are defined on the map. -- --- The @{Scenery#SCENERY} class is a wrapper class to handle the DCS Scenery objects: +-- The @{Wrapper.Scenery#SCENERY} class is a wrapper class to handle the DCS Scenery objects: -- -- * Wraps the DCS Scenery objects. -- * Support all DCS Scenery APIs. diff --git a/Moose Development/Moose/Wrapper/Static.lua b/Moose Development/Moose/Wrapper/Static.lua index 27800cff0..a591522af 100644 --- a/Moose Development/Moose/Wrapper/Static.lua +++ b/Moose Development/Moose/Wrapper/Static.lua @@ -18,7 +18,7 @@ --- Wrapper class to handle Static objects. -- -- Note that Statics are almost the same as Units, but they don't have a controller. --- The @{Static#STATIC} class is a wrapper class to handle the DCS Static objects: +-- The @{Wrapper.Static#STATIC} class is a wrapper class to handle the DCS Static objects: -- -- * Wraps the DCS Static objects. -- * Support all DCS Static APIs. From e9a055219e3036418443d7f2722a8fa9a383bd6e Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sat, 2 Jun 2018 19:13:26 +0200 Subject: [PATCH 151/420] ARTY v0.9.8-buggy Improved marker assignments. There is a bug that makes DCS crash when a group is ordered to move and reaches its destination, i.e. arrives. --- .../Moose/Functional/Artillery.lua | 281 +++++++++++------- 1 file changed, 180 insertions(+), 101 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 2857d755a..03fdf5035 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -459,7 +459,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #string version -ARTY.version="0.9.8" +ARTY.version="0.9.8-buggy" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -856,17 +856,21 @@ end function ARTY:RemoveTarget(name) self:F2(name) local id=self:_GetTargetIndexByName(name) + if id then + + -- Remove target from table. self:T(ARTY.id..string.format("Group %s: Removing target %s (id=%d).", self.Controllable:GetName(), name, id)) table.remove(self.targets, id) + + -- Delete marker belonging to this engagement. + local batteryname,markTargetID, markMoveID=self:_GetMarkIDfromName(name) + if batteryname==self.Controllable:GetName() and markTargetID~=nil then + COORDINATE:RemoveMark(markTargetID) + end + end self:T(ARTY.id..string.format("Group %s: Number of targets = %d.", self.Controllable:GetName(), #self.targets)) - if self.currentTarget then - if self.currentTarget.name==name then - self:T(ARTY.id..string.format("Group %s: Cancelling current target %s.", self.Controllable:GetName(), name)) - self:CeaseFire(self.currentTarget) - end - end end --- Delete a move from move list. @@ -875,16 +879,17 @@ end function ARTY:RemoveMove(name) self:F2(name) local id=self:_GetMoveIndexByName(name) + if id then + -- Remove move from table. self:T(ARTY.id..string.format("Group %s: Removing move %s (id=%d).", self.Controllable:GetName(), name, id)) table.remove(self.moves, id) - end - self:T(ARTY.id..string.format("Group %s: Number of moves = %d.", self.Controllable:GetName(), #self.moves)) - if self.currentMove then - if self.currentMove.name==name then - self:T(ARTY.id..string.format("Group %s: Cancelling current move %s.", self.Controllable:GetName(), name)) - self:Arrived() - end + env.info("FF debug remove move") + -- Delete marker belonging to this relocation move. + --local batteryname,markTargetID,markMoveID=self:_GetMarkIDfromName(name) + --if batteryname==self.Controllable:GetName() and markMoveID~=nil then + --COORDINATE:RemoveMark(markMoveID) + --end end end @@ -1110,6 +1115,62 @@ function ARTY:onafterStart(Controllable, From, Event, To) self:__Status(self.StatusInterval) end +--- Extract engagement assignments and parameters from mark text. +-- @param #ARTY self +-- @param #string text Marker text. +-- @return #boolean If true, authentification successful. +function ARTY:_MarkerKeyAuthentification(text) + + -- Set battery and coalition. + local batteryname=self.Controllable:GetName() + local batterycoalition=self.Controllable:GetCoalition() + + -- Get assignment. + local mykey=nil + if self.markkey~=nil then + + -- keywords are split by "," + local keywords=self:_split(text, ",") + for _,key in pairs(keywords) do + local s=self:_split(key, " ") + local val=s[2] + if key:lower():find("key") then + mykey=tonumber(val) + self:T(ARTY.id..string.format("Authorisation Key=%s.", val)) + end + end + + end + + -- Check if the authorization key is required and if it is valid. + local _validkey=true + + -- Check if group needs authorization. + if self.markkey~=nil then + -- Assume key is incorrect. + _validkey=false + + -- If key was found, check if matches. + if mykey~=nil then + _validkey=self.markkey==mykey + end + self:T2(ARTY.id..string.format("%s, authkey=%s == %s=playerkey ==> valid=%s", batteryname, tostring(self.markkey), tostring(mykey), tostring(_validkey))) + + -- Send message + local text="" + if mykey==nil then + text=string.format("%s, authorization required but did not receive a key!", batteryname) + elseif _validkey==false then + text=string.format("%s, authorization required but did receive an incorrect key (key=%s)!", batteryname, tostring(mykey)) + elseif _validkey==true then + text=string.format("%s, authentification successful!", batteryname) + end + MESSAGE:New(text, 20):ToCoalitionIf(batterycoalition, self.report or self.Debug) + end + + return _validkey +end + --- Extract engagement assignments and parameters from mark text. -- @param #ARTY self -- @param #string text Marker text to be analyzed. @@ -1123,6 +1184,7 @@ function ARTY:_Markertext(text) assignment.move=false assignment.engage=false assignment.readonly=false + assignment.cancelcurrent=false assignment.time=nil assignment.nshells=nil assignment.prio=nil @@ -1133,7 +1195,7 @@ function ARTY:_Markertext(text) assignment.onroad=nil assignment.key=nil - if text:lower():find("arty") then + if text:lower():find("arty") then if text:lower():find("engage") then assignment.engage=true elseif text:lower():find("move") then @@ -1210,19 +1272,18 @@ function ARTY:_Markertext(text) assignment.speed=tonumber(val) self:T2(ARTY.id..string.format("Key Speed=%s.", val)) - elseif key:lower():find("road") then + elseif key:lower():find("on road") or key:lower():find("onroad") or key:lower():find("use road")then assignment.onroad=true self:T2(ARTY.id..string.format("Key Onroad=true.")) - - elseif key:lower():find("key") then - - assignment.key=tonumber(val) - self:T(ARTY.id..string.format("Key Key=%s.", val)) - - elseif key:lower():find("irrevocable") then + + elseif key:lower():find("irrevocable") or key:lower():find("readonly") then assignment.readonly=true self:T2(ARTY.id..string.format("Key Readonly=true.")) + + elseif key:lower():find("cancel current") then + assignment.cancelcurrent=true + self:T2(ARTY.id..string.format("Key Cancel Current=true.")) end end @@ -1278,6 +1339,58 @@ function ARTY:onEvent(Event) end +--- Create a name for an engagement initiated by placing a marker. +-- @param #ARTY self +-- @param #number markerid ID of the placed marker. +-- @return #string Name of target engagement. +function ARTY:_MarkTargetName(markerid) + return string.format("BATTERY=%s, Marked Target ID=%d", self.Controllable:GetName(), markerid) +end + +--- Create a name for a relocation move initiated by placing a marker. +-- @param #ARTY self +-- @param #number markerid ID of the placed marker. +-- @return #string Name of relocation move. +function ARTY:_MarkMoveName(markerid) + return string.format("BATTERY=%s, Marked Relocation ID=%d", self.Controllable:GetName(), markerid) +end + +--- Create a name for a relocation move initiated by placing a marker. +-- @param #ARTY self +-- @param #sting name Name of the assignment. +-- @return #string Name of the ARTY group or nil +-- @return #number ID of the marked target or nil. +-- @return #number ID of the marked relocation move or nil +function ARTY:_GetMarkIDfromName(name) + + -- keywords are split by "," + local keywords=self:_split(name, ",") + + local battery=nil + local markTID=nil + local markMID=nil + + for _,key in pairs(keywords) do + + local str=self:_split(key, "=") + local par=str[1] + local val=str[2] + + if par:find("BATTERY") then + battery=val + end + if par:find("Marked Target ID") then + markTID=tonumber(val) + end + if par:find("Marked Relocation ID") then + markMID=tonumber(val) + end + + end + + return battery, markTID, markMID +end + --- Function called when a F10 map mark was removed. -- @param #ARTY self -- @param #table Event Event data. @@ -1293,18 +1406,20 @@ function ARTY:_OnEventMarkRemove(Event) local _canceltarget=false local _name="" local _id=nil + if Event.text:find("Marked Relocation") then _cancelmove=true - _name=string.format("BATTERY %s Marked Relocation ID=%d", batteryname, Event.idx) + _name=self:_MarkMoveName(Event.idx) _id=self:_GetMoveIndexByName(_name) elseif Event.text:find("Marked Target") then _canceltarget=true - _name=string.format("BATTERY %s Marked Target ID=%d", batteryname, Event.idx) + _name=self:_MarkTargetName(Event.idx) _id=self:_GetTargetIndexByName(_name) else return end + -- Check if there is a task which matches. if _id==nil then return end @@ -1312,48 +1427,25 @@ function ARTY:_OnEventMarkRemove(Event) -- Check if the coalition is the same or an authorization key has been defined. if (batterycoalition==Event.coalition and self.markkey==nil) or self.markkey~=nil then - -- Get assignment. - local mykey=nil - if self.markkey~=nil then - -- keywords are split by "," - local keywords=self:_split(Event.text, ",") - for _,key in pairs(keywords) do - local s=self:_split(key, " ") - local val=s[2] - if key:lower():find("key") then - mykey=tonumber(val) - self:T(ARTY.id..string.format("Key Key=%s.", val)) - end - end - end - - -- Check if the authorization key is required and if it is valid. - local _validkey=true - if self.markkey~=nil then - _validkey=false - if mykey~=nil then - _validkey=self.markkey==mykey - end - self:T2(ARTY.id..string.format("%s, authkey=%s == %s=playerkey ==> valid=%s", batteryname, tostring(self.markkey), tostring(mykey), tostring(_validkey))) - local text="" - if mykey==nil then - text=string.format("%s, authorization required but did not receive a key!", batteryname) - elseif _validkey==false then - text=string.format("%s, authorization required but did receive an incorrect key (key=%s)!", batteryname, tostring(mykey)) - elseif _validkey==true then - text=string.format("%s, authentification successful!", batteryname) - end - MESSAGE:New(text, 20):ToCoalitionIf(batterycoalition, self.report or self.Debug) - end + -- Authentify key + local _validkey=self:_MarkerKeyAuthentification(Event.text) -- Check if we have the right coalition. if _validkey then -- This should be the unique name of the target or move. if _cancelmove then - self:RemoveMove(_name) + if self.currentMove and self.currentMove.name==_name then + self:Arrived() + else + self:RemoveMove(_name) + end elseif _canceltarget then - self:RemoveTarget(_name) + if self.currentTarget and self.currentTarget.name==_name then + self:CeaseFire(self.currentTarget) + else + self:RemoveTarget(_name) + end end end @@ -1397,24 +1489,19 @@ function ARTY:_OnEventMarkChange(Event) if not (_assign.engage or _assign.move) or (not _assigned) then return end - + -- Check if the authorization key is required and if it is valid. - local _validkey=true - if self.markkey~=nil then - _validkey=false - if _assign.key~=nil then - _validkey=self.markkey==_assign.key + local _validkey=self:_MarkerKeyAuthentification(Event.text) + + -- Cancel current target. + if _validkey and _assign.cancelcurrent then + if _assign.move and self.currentMove then + self:Arrived() end - self:T2(ARTY.id..string.format("%s, authkey=%s == %s=playerkey ==> valid=%s", batteryname, tostring(self.markkey), tostring(_assign.key), tostring(_validkey))) - local text="" - if _assign.key==nil then - text=string.format("%s, authorization required but did not receive a key!", batteryname) - elseif _validkey==false then - text=string.format("%s, authorization required but did receive an incorrect key (key=%s)!", batteryname, tostring(_assign.key)) - elseif _validkey==true then - text=string.format("%s, authentification successful!", batteryname) + if _assign.engage and self.currentTarget then + self:CeaseFire(self.currentTarget) end - MESSAGE:New(text, 20):ToCoalitionIf(batterycoalition, self.report or self.Debug) + return end -- We are meant. @@ -1430,17 +1517,18 @@ function ARTY:_OnEventMarkChange(Event) -- Also I don't know who can see the mark which was created. _coord:RemoveMark(Event.idx) + -- Anticipate marker ID. + -- WARNING: Make sure, no marks are set until the COORDINATE:MarkToCoalition() is called or the target/move name will be wrong and target cannot be removed by deleting its marker. local _id=UTILS._MarkID+1 if _assign.move then -- Create a new name. This determins the string we search when deleting a move! - local _name=string.format("BATTERY %s Marked Relocation ID=%d", batteryname, _id) - self:E(ARTY.id.._name) + local _name=self:_MarkMoveName(_id) local text=string.format("%s, received new relocation assignment.", batteryname) text=text..string.format("\nCoordinates %s",_coord:ToStringLLDMS()) - MESSAGE:New(text, 20):ToCoalitionIf(batterycoalition, self.report or self.Debug) + MESSAGE:New(text, 10):ToCoalitionIf(batterycoalition, self.report or self.Debug) -- Assign a relocation of the arty group. local _movename=self:AssignMoveCoord(_coord, _assign.time, _assign.speed, _assign.onroad, _assign.cancel,_name, true) @@ -1451,11 +1539,7 @@ function ARTY:_OnEventMarkChange(Event) -- Create new target name. local clock=tostring(self:_SecondsToClock(_move.time)) - local _road="Off Road" - if _move.onroad==true then - _road="On Road" - end - local _markertext=_movename..string.format(", Time %s, Speed %d km/h, %s.", clock, _move.speed, _road) + local _markertext=_movename..string.format(", Time=%s, Speed=%d km/h, Use Roads=%s.", clock, _move.speed, tostring(_move.onroad)) -- Create a new mark. This will trigger the mark added event. local _randomcoord=_coord:GetRandomCoordinateInRadius(100) @@ -1465,8 +1549,7 @@ function ARTY:_OnEventMarkChange(Event) else -- Create a new name. - local _name=string.format("BATTERY %s Marked Target ID=%d", batteryname, _id) - self:E(ARTY.id.._name) + local _name=self:_MarkTargetName(_id) local text=string.format("%s, received new target assignment.", batteryname) text=text..string.format("\nCoordinates %s",_coord:ToStringLLDMS()) @@ -1476,6 +1559,9 @@ function ARTY:_OnEventMarkChange(Event) if _assign.prio then text=text..string.format("\nPrio %d",_assign.prio) end + if _assign.prio then + text=text..string.format("\nRadius %d m",_assign.radius) + end if _assign.nshells then text=text..string.format("\nShots %d",_assign.nshells) end @@ -1485,7 +1571,7 @@ function ARTY:_OnEventMarkChange(Event) if _assign.weapontype then text=text..string.format("\nWeapon %s",self:_WeaponTypeName(_assign.weapontype)) end - MESSAGE:New(text, 20):ToCoalitionIf(batterycoalition, self.report or self.Debug) + MESSAGE:New(text, 10):ToCoalitionIf(batterycoalition, self.report or self.Debug) -- Assign a new firing engagement. -- Note, we set unique=true so this target gets only added once. @@ -1498,7 +1584,7 @@ function ARTY:_OnEventMarkChange(Event) -- Create new target name. local clock=tostring(self:_SecondsToClock(_target.time)) local weapon=self:_WeaponTypeName(_target.weapontype) - local _markertext=_targetname..string.format(", Priority %d, Radius=%d m, Shots %d, Engagements=%d, Weapon %s, Time %s", _target.prio, _target.radius, _target.nshells, _target.maxengage, weapon, clock) + local _markertext=_targetname..string.format(", Priority=%d, Radius=%d m, Shots=%d, Engagements=%d, Weapon=%s, Time=%s", _target.prio, _target.radius, _target.nshells, _target.maxengage, weapon, clock) -- Create a new mark. This will trigger the mark added event. local _randomcoord=_coord:GetRandomCoordinateInRadius(250) @@ -1547,6 +1633,11 @@ function ARTY:_StatusReport() for i=1,#self.targets do text=text..string.format("- %s\n", self:_TargetInfo(self.targets[i])) end + if self.currentMove then + text=text..string.format("Current Move = %s\n", tostring(self.currentMove.name)) + else + text=text..string.format("Current Move = %s\n", "none") + end text=text..string.format("Moves:\n") for i=1,#self.moves do text=text..string.format("- %s\n", self:_MoveInfo(self.moves[i])) @@ -1669,18 +1760,6 @@ function ARTY:_NuclearBlast(_coord) end ---- Eventhandler for shot event. --- @param #ARTY self --- @param Core.Event#EVENTDATA EventData -function ARTY:_OnMarkAdded(EventData) - self:F(EventData) - if EventData.MarkCoordinate then - local coord=EventData.MarkCoordinate --Core.Point#COORDINATE - - coord:SmokeGreen() - end -end - --- Eventhandler for shot event. -- @param #ARTY self -- @param Core.Event#EVENTDATA EventData @@ -2453,8 +2532,8 @@ function ARTY:onafterArrived(Controllable, From, Event, To) -- Remove executed move from queue. if self.currentMove then - self:RemoveMove(self.currentMove.name) - self.currentMove=nil + --self:RemoveMove(self.currentMove.name) + --self.currentMove=nil end end From 0d246d3f49841b8d8986be29d6059a4124842fca Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sun, 3 Jun 2018 00:58:24 +0200 Subject: [PATCH 152/420] ARTY v0.9.9 ARTY: Fixed CTD bug. Caused by group:ClearTasks() when no task is assigned (group arrived). Many other improvements Cleared up function location. MESSAGES: Added optional clear screen parameter. --- Moose Development/Moose/Core/Message.lua | 22 +- .../Moose/Functional/Artillery.lua | 1826 +++++++++-------- .../Moose/Wrapper/Controllable.lua | 4 +- 3 files changed, 956 insertions(+), 896 deletions(-) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 926284332..e11f807ea 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -66,6 +66,7 @@ MESSAGE.Type = { -- @param #string MessageText is the text of the Message. -- @param #number MessageDuration is a number in seconds of how long the MESSAGE should be shown on the display panel. -- @param #string MessageCategory (optional) is a string expressing the "category" of the Message. The category will be shown as the first text in the message followed by a ": ". +-- @param #boolean ClearScreen (optional) Clear all previous messages. -- @return #MESSAGE -- @usage -- -- Create a series of new Messages. @@ -77,7 +78,7 @@ MESSAGE.Type = { -- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", 25, "Penalty" ) -- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", 25, "Score" ) -- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", 25, "Score") -function MESSAGE:New( MessageText, MessageDuration, MessageCategory ) +function MESSAGE:New( MessageText, MessageDuration, MessageCategory, ClearScreen ) local self = BASE:Inherit( self, BASE:New() ) self:F( { MessageText, MessageDuration, MessageCategory } ) @@ -94,6 +95,11 @@ function MESSAGE:New( MessageText, MessageDuration, MessageCategory ) else self.MessageCategory = "" end + + self.ClearScreen=false + if ClearScreen~=nil then + self.ClearScreen=ClearScreen + end self.MessageDuration = MessageDuration or 5 self.MessageTime = timer.getTime() @@ -114,18 +120,24 @@ end -- @param self -- @param #string MessageText is the text of the Message. -- @param #MESSAGE.Type MessageType The type of the message. +-- @param #boolean ClearScreen (optional) Clear all previous messages. -- @return #MESSAGE -- @usage -- MessageAll = MESSAGE:NewType( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", MESSAGE.Type.Information ) -- MessageRED = MESSAGE:NewType( "To the RED Players: You receive a penalty because you've killed one of your own units", MESSAGE.Type.Information ) -- MessageClient1 = MESSAGE:NewType( "Congratulations, you've just hit a target", MESSAGE.Type.Update ) -- MessageClient2 = MESSAGE:NewType( "Congratulations, you've just killed a target", MESSAGE.Type.Update ) -function MESSAGE:NewType( MessageText, MessageType ) +function MESSAGE:NewType( MessageText, MessageType, ClearScreen ) local self = BASE:Inherit( self, BASE:New() ) self:F( { MessageText } ) self.MessageType = MessageType + + self.ClearScreen=false + if ClearScreen~=nil then + self.ClearScreen=ClearScreen + end self.MessageTime = timer.getTime() self.MessageText = MessageText:gsub("^\n","",1):gsub("\n$","",1) @@ -170,7 +182,7 @@ function MESSAGE:ToClient( Client, Settings ) if self.MessageDuration ~= 0 then local ClientGroupID = Client:GetClientGroupID() self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration ) - trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration ) + trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration , self.ClearScreen) end end @@ -194,7 +206,7 @@ function MESSAGE:ToGroup( Group, Settings ) if self.MessageDuration ~= 0 then self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration ) - trigger.action.outTextForGroup( Group:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration ) + trigger.action.outTextForGroup( Group:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen ) end end @@ -262,7 +274,7 @@ function MESSAGE:ToCoalition( CoalitionSide, Settings ) if CoalitionSide then if self.MessageDuration ~= 0 then self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration ) - trigger.action.outTextForCoalition( CoalitionSide, self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration ) + trigger.action.outTextForCoalition( CoalitionSide, self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen ) end end diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 03fdf5035..17cdccf94 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -51,7 +51,7 @@ -- @field #number Nshells0 Initial amount of shells of the whole group. -- @field #number Nrockets0 Initial amount of rockets of the whole group. -- @field #number Nmissiles0 Initial amount of missiles of the whole group. --- @field #number Nukes0 Initial amount of tactical nukes of the whole group. +-- @field #number Nukes0 Initial amount of tactical nukes of the whole group. Default is 0. -- @field #number FullAmmo Full amount of all ammunition taking the number of alive units into account. -- @field #number StatusInterval Update interval in seconds between status updates. Default 10 seconds. -- @field #number WaitForShotTime Max time in seconds to wait until fist shot event occurs after target is assigned. If time is passed without shot, the target is deleted. Default is 300 seconds. @@ -459,7 +459,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #string version -ARTY.version="0.9.8-buggy" +ARTY.version="0.9.9" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -855,6 +855,8 @@ end -- @param #string name Name of the target. function ARTY:RemoveTarget(name) self:F2(name) + + -- Get target ID from namd local id=self:_GetTargetIndexByName(name) if id then @@ -864,11 +866,13 @@ function ARTY:RemoveTarget(name) table.remove(self.targets, id) -- Delete marker belonging to this engagement. - local batteryname,markTargetID, markMoveID=self:_GetMarkIDfromName(name) - if batteryname==self.Controllable:GetName() and markTargetID~=nil then - COORDINATE:RemoveMark(markTargetID) - end - + if self.markallow then + local batteryname,markTargetID, markMoveID=self:_GetMarkIDfromName(name) + if batteryname==self.Controllable:GetName() and markTargetID~=nil then + COORDINATE:RemoveMark(markTargetID) + end + end + end self:T(ARTY.id..string.format("Group %s: Number of targets = %d.", self.Controllable:GetName(), #self.targets)) end @@ -878,19 +882,26 @@ end -- @param #string name Name of the target. function ARTY:RemoveMove(name) self:F2(name) + + -- Get move ID from name. local id=self:_GetMoveIndexByName(name) if id then + -- Remove move from table. self:T(ARTY.id..string.format("Group %s: Removing move %s (id=%d).", self.Controllable:GetName(), name, id)) table.remove(self.moves, id) - env.info("FF debug remove move") + -- Delete marker belonging to this relocation move. - --local batteryname,markTargetID,markMoveID=self:_GetMarkIDfromName(name) - --if batteryname==self.Controllable:GetName() and markMoveID~=nil then - --COORDINATE:RemoveMark(markMoveID) - --end + if self.markallow then + local batteryname,markTargetID,markMoveID=self:_GetMarkIDfromName(name) + if batteryname==self.Controllable:GetName() and markMoveID~=nil then + COORDINATE:RemoveMark(markMoveID) + end + end + end + self:T(ARTY.id..string.format("Group %s: Number of moves = %d.", self.Controllable:GetName(), #self.moves)) end --- Delete ALL targets from current target list. @@ -1015,7 +1026,7 @@ function ARTY:onafterStart(Controllable, From, Event, To) -- Debug output. local text=string.format("Started ARTY version %s for group %s.", ARTY.version, Controllable:GetName()) self:E(ARTY.id..text) - MESSAGE:New(text, 10):ToAllIf(self.Debug) + MESSAGE:New(text, 5):ToAllIf(self.Debug) -- Get Ammo. self.Nammo0, self.Nshells0, self.Nrockets0, self.Nmissiles0=self:GetAmmo(self.Debug) @@ -1027,10 +1038,11 @@ function ARTY:onafterStart(Controllable, From, Event, To) if self.nukefires==nil then self.nukefires=20/1000/1000*self.nukerange*self.nukerange end - if self.Nukes==nil then - self.Nukes0=self.Nshells0 + if self.Nukes~=nil then + self.Nukes0=math.min(self.Nukes, self.Nshells0) else - self.Nukes0=self.Nukes + self.Nukes=0 + self.Nukes0=0 end local text=string.format("\n******************************************************\n") @@ -1106,510 +1118,25 @@ function ARTY:onafterStart(Controllable, From, Event, To) -- Add event handler. self:HandleEvent(EVENTS.Shot, self._OnEventShot) self:HandleEvent(EVENTS.Dead, self._OnEventDead) - self:HandleEvent(EVENTS.MarkAdded, self._OnEventMarkAdded) + --self:HandleEvent(EVENTS.MarkAdded, self._OnEventMarkAdded) - -- Add DCS event handler. - world.addEventHandler(self) + -- Add DCS event handler - necessary for S_EVENT_MARK_* events. So we only start it, if this was requested. + if self.markallow then + world.addEventHandler(self) + end -- Start checking status. self:__Status(self.StatusInterval) end ---- Extract engagement assignments and parameters from mark text. --- @param #ARTY self --- @param #string text Marker text. --- @return #boolean If true, authentification successful. -function ARTY:_MarkerKeyAuthentification(text) - - -- Set battery and coalition. - local batteryname=self.Controllable:GetName() - local batterycoalition=self.Controllable:GetCoalition() - - -- Get assignment. - local mykey=nil - if self.markkey~=nil then - - -- keywords are split by "," - local keywords=self:_split(text, ",") - for _,key in pairs(keywords) do - local s=self:_split(key, " ") - local val=s[2] - if key:lower():find("key") then - mykey=tonumber(val) - self:T(ARTY.id..string.format("Authorisation Key=%s.", val)) - end - end - - end - - -- Check if the authorization key is required and if it is valid. - local _validkey=true - - -- Check if group needs authorization. - if self.markkey~=nil then - -- Assume key is incorrect. - _validkey=false - - -- If key was found, check if matches. - if mykey~=nil then - _validkey=self.markkey==mykey - end - self:T2(ARTY.id..string.format("%s, authkey=%s == %s=playerkey ==> valid=%s", batteryname, tostring(self.markkey), tostring(mykey), tostring(_validkey))) - - -- Send message - local text="" - if mykey==nil then - text=string.format("%s, authorization required but did not receive a key!", batteryname) - elseif _validkey==false then - text=string.format("%s, authorization required but did receive an incorrect key (key=%s)!", batteryname, tostring(mykey)) - elseif _validkey==true then - text=string.format("%s, authentification successful!", batteryname) - end - MESSAGE:New(text, 20):ToCoalitionIf(batterycoalition, self.report or self.Debug) - end - - return _validkey -end - ---- Extract engagement assignments and parameters from mark text. --- @param #ARTY self --- @param #string text Marker text to be analyzed. --- @return #table Table with assignment parameters, e.g. number of shots, radius, time etc. -function ARTY:_Markertext(text) - self:F(text) - - -- Assignment parameters. - local assignment={} - assignment.battery={} - assignment.move=false - assignment.engage=false - assignment.readonly=false - assignment.cancelcurrent=false - assignment.time=nil - assignment.nshells=nil - assignment.prio=nil - assignment.maxengage=nil - assignment.radius=nil - assignment.weapontype=nil - assignment.speed=nil - assignment.onroad=nil - assignment.key=nil - - if text:lower():find("arty") then - if text:lower():find("engage") then - assignment.engage=true - elseif text:lower():find("move") then - assignment.move=true - else - self:E(ARTY.id.."ERROR: Neither ENGAGE nor MOVE keyword specified!") - return - end - - -- keywords are split by "," - local keywords=self:_split(text, ",") - - for _,key in pairs(keywords) do - - local s=self:_split(key, " ") - local val=s[2] - - -- Battery name, i.e. which ARTY group should fire. - if key:lower():find("battery") then - - local v=self:_split(key, '"') - - for i=2,#v,2 do - table.insert(assignment.battery, v[i]) - self:T2(ARTY.id..string.format("Key Battery=%s.", v[i])) - end - - elseif key:lower():find("time") then - - if val:lower():find("now") then - assignment.time=self:_SecondsToClock(timer.getTime0()+5) - else - assignment.time=val - end - self:T2(ARTY.id..string.format("Key Time=%s.", val)) - - elseif key:lower():find("shots") then - - assignment.nshells=tonumber(s[2]) - self:T(ARTY.id..string.format("Key Shots=%s.", val)) - - elseif key:lower():find("prio") then - - assignment.prio=tonumber(val) - self:T2(string.format("Key Prio=%s.", val)) - - elseif key:lower():find("maxengage") then - - assignment.maxengage=tonumber(val) - self:T2(ARTY.id..string.format("Key Maxengage=%s.", val)) - - elseif key:lower():find("radius") then - - assignment.radius=tonumber(val) - self:T2(ARTY.id..string.format("Key Radius=%s.", val)) - - elseif key:lower():find("weapon") then - - if val:lower():find("cannon") then - assignment.weapontype=ARTY.WeaponType.Cannon - elseif val:lower():find("rocket") then - assignment.weapontype=ARTY.WeaponType.Rockets - elseif val:lower():find("missile") then - assignment.weapontype=ARTY.WeaponType.GuidedMissile - elseif val:lower():find("nuke") then - assignment.weapontype=ARTY.WeaponType.TacticalNukes - else - assignment.weapontype=ARTY.WeaponType.Auto - end - self:T2(ARTY.id..string.format("Key Weapon=%s.", val)) - - elseif key:lower():find("speed") then - - assignment.speed=tonumber(val) - self:T2(ARTY.id..string.format("Key Speed=%s.", val)) - - elseif key:lower():find("on road") or key:lower():find("onroad") or key:lower():find("use road")then - - assignment.onroad=true - self:T2(ARTY.id..string.format("Key Onroad=true.")) - - elseif key:lower():find("irrevocable") or key:lower():find("readonly") then - assignment.readonly=true - self:T2(ARTY.id..string.format("Key Readonly=true.")) - - elseif key:lower():find("cancel current") then - assignment.cancelcurrent=true - self:T2(ARTY.id..string.format("Key Cancel Current=true.")) - end - - end - else - self:T2(ARTY.id..string.format("This is NO arty command:\n%s", tostring(text))) - end - - return assignment -end - ---- After "Start" event. Initialized ROE and alarm state. Starts the event handler. --- @param #ARTY self --- @param #table Event -function ARTY:onEvent(Event) - - if Event == nil or Event.idx == nil then - self:T3("Skipping onEvent. Event or Event.idx unknown.") - return true - end - - -- Set battery and coalition. - local batteryname=self.Controllable:GetName() - local batterycoalition=self.Controllable:GetCoalition() - - self:T(string.format("Event captured = %s", tostring(batteryname))) - self:T(string.format("Event id = %s", tostring(Event.id))) - self:T(string.format("Event time = %s", tostring(Event.time))) - self:T(string.format("Event idx = %s", tostring(Event.idx))) - self:T(string.format("Event coalition = %s", tostring(Event.coalition))) - self:T(string.format("Event group id = %s", tostring(Event.groupID))) - self:T(string.format("Event text = %s", tostring(Event.text))) - self:E({eventid=Event.id, vec3=Event.pos}) - if Event.initiator~=nil then - local _unitname=Event.initiator:getName() - self:T(string.format("Event ini unit name = %s", tostring(_unitname))) - end - - if Event.id==world.event.S_EVENT_MARK_ADDED then - self:E({event="S_EVENT_MARK_ADDED", battery=batteryname, vec3=Event.pos}) - - elseif Event.id==world.event.S_EVENT_MARK_CHANGE then - self:E({event="S_EVENT_MARK_CHANGE", battery=batteryname, vec3=Event.pos}) - - -- Handle event. - self:_OnEventMarkChange(Event) - - elseif Event.id==world.event.S_EVENT_MARK_REMOVED then - self:E({event="S_EVENT_MARK_REMOVED", battery=batteryname, vec3=Event.pos}) - - -- Hande event. - self:_OnEventMarkRemove(Event) - end - -end - ---- Create a name for an engagement initiated by placing a marker. --- @param #ARTY self --- @param #number markerid ID of the placed marker. --- @return #string Name of target engagement. -function ARTY:_MarkTargetName(markerid) - return string.format("BATTERY=%s, Marked Target ID=%d", self.Controllable:GetName(), markerid) -end - ---- Create a name for a relocation move initiated by placing a marker. --- @param #ARTY self --- @param #number markerid ID of the placed marker. --- @return #string Name of relocation move. -function ARTY:_MarkMoveName(markerid) - return string.format("BATTERY=%s, Marked Relocation ID=%d", self.Controllable:GetName(), markerid) -end - ---- Create a name for a relocation move initiated by placing a marker. --- @param #ARTY self --- @param #sting name Name of the assignment. --- @return #string Name of the ARTY group or nil --- @return #number ID of the marked target or nil. --- @return #number ID of the marked relocation move or nil -function ARTY:_GetMarkIDfromName(name) - - -- keywords are split by "," - local keywords=self:_split(name, ",") - - local battery=nil - local markTID=nil - local markMID=nil - - for _,key in pairs(keywords) do - - local str=self:_split(key, "=") - local par=str[1] - local val=str[2] - - if par:find("BATTERY") then - battery=val - end - if par:find("Marked Target ID") then - markTID=tonumber(val) - end - if par:find("Marked Relocation ID") then - markMID=tonumber(val) - end - - end - - return battery, markTID, markMID -end - ---- Function called when a F10 map mark was removed. --- @param #ARTY self --- @param #table Event Event data. -function ARTY:_OnEventMarkRemove(Event) - - -- Get battery coalition and name. - local batterycoalition=self.Controllable:GetCoalition() - local batteryname=self.Controllable:GetName() - - if Event.text~=nil and Event.text:find("BATTERY") then - - local _cancelmove=false - local _canceltarget=false - local _name="" - local _id=nil - - if Event.text:find("Marked Relocation") then - _cancelmove=true - _name=self:_MarkMoveName(Event.idx) - _id=self:_GetMoveIndexByName(_name) - elseif Event.text:find("Marked Target") then - _canceltarget=true - _name=self:_MarkTargetName(Event.idx) - _id=self:_GetTargetIndexByName(_name) - else - return - end - - -- Check if there is a task which matches. - if _id==nil then - return - end - - -- Check if the coalition is the same or an authorization key has been defined. - if (batterycoalition==Event.coalition and self.markkey==nil) or self.markkey~=nil then - - -- Authentify key - local _validkey=self:_MarkerKeyAuthentification(Event.text) - - -- Check if we have the right coalition. - if _validkey then - - -- This should be the unique name of the target or move. - if _cancelmove then - if self.currentMove and self.currentMove.name==_name then - self:Arrived() - else - self:RemoveMove(_name) - end - elseif _canceltarget then - if self.currentTarget and self.currentTarget.name==_name then - self:CeaseFire(self.currentTarget) - else - self:RemoveTarget(_name) - end - end - - end - - end - - end -end - ---- Function called when a F10 map mark was changed. --- @param #ARTY self --- @param #table Event Event data. -function ARTY:_OnEventMarkChange(Event) - - -- Check if marker has a text and the "arty" keyword. - if Event.text~=nil and Event.text:lower():find("arty") then - - -- Get battery coalition and name. - local batterycoalition=self.Controllable:GetCoalition() - local batteryname=self.Controllable:GetName() - - -- Check if the coalition is the same or an authorization key has been defined. - if (batterycoalition==Event.coalition and self.markkey==nil) or self.markkey~=nil then - - -- Evaluate marker text and extract parameters. - local _assign=self:_Markertext(Event.text) - - -- Check if job is assigned to this ARTY group. Default is for all ARTY groups. - local _assigned=true - if #_assign.battery>0 then - _assigned=false - for _,bat in pairs(_assign.battery) do - self:T2(ARTY.id..string.format("Compare battery names %s=%s ==> %s",batteryname, bat, tostring(batteryname==bat))) - if batteryname==bat then - _assigned=true - end - end - end - - -- Check if ENGAGE or MOVE keywords were found. - if not (_assign.engage or _assign.move) or (not _assigned) then - return - end - - -- Check if the authorization key is required and if it is valid. - local _validkey=self:_MarkerKeyAuthentification(Event.text) - - -- Cancel current target. - if _validkey and _assign.cancelcurrent then - if _assign.move and self.currentMove then - self:Arrived() - end - if _assign.engage and self.currentTarget then - self:CeaseFire(self.currentTarget) - end - return - end - - -- We are meant. - if _validkey then - - -- Convert (wrong x-->z, z-->x) vec3 - local vec3={y=Event.pos.y, x=Event.pos.z, z=Event.pos.x} - - -- Get coordinate from vec3. - local _coord=COORDINATE:NewFromVec3(vec3) - - -- Remove old mark because it might contain confidential data such as the key. - -- Also I don't know who can see the mark which was created. - _coord:RemoveMark(Event.idx) - - -- Anticipate marker ID. - -- WARNING: Make sure, no marks are set until the COORDINATE:MarkToCoalition() is called or the target/move name will be wrong and target cannot be removed by deleting its marker. - local _id=UTILS._MarkID+1 - - if _assign.move then - - -- Create a new name. This determins the string we search when deleting a move! - local _name=self:_MarkMoveName(_id) - - local text=string.format("%s, received new relocation assignment.", batteryname) - text=text..string.format("\nCoordinates %s",_coord:ToStringLLDMS()) - MESSAGE:New(text, 10):ToCoalitionIf(batterycoalition, self.report or self.Debug) - - -- Assign a relocation of the arty group. - local _movename=self:AssignMoveCoord(_coord, _assign.time, _assign.speed, _assign.onroad, _assign.cancel,_name, true) - - if _movename~=nil then - local _mid=self:_GetMoveIndexByName(_movename) - local _move=self.moves[_mid] - - -- Create new target name. - local clock=tostring(self:_SecondsToClock(_move.time)) - local _markertext=_movename..string.format(", Time=%s, Speed=%d km/h, Use Roads=%s.", clock, _move.speed, tostring(_move.onroad)) - - -- Create a new mark. This will trigger the mark added event. - local _randomcoord=_coord:GetRandomCoordinateInRadius(100) - _randomcoord:MarkToCoalition(_markertext, batterycoalition, self.markreadonly or _assign.readonly) - end - - else - - -- Create a new name. - local _name=self:_MarkTargetName(_id) - - local text=string.format("%s, received new target assignment.", batteryname) - text=text..string.format("\nCoordinates %s",_coord:ToStringLLDMS()) - if _assign.time then - text=text..string.format("\nTime %s",_assign.time) - end - if _assign.prio then - text=text..string.format("\nPrio %d",_assign.prio) - end - if _assign.prio then - text=text..string.format("\nRadius %d m",_assign.radius) - end - if _assign.nshells then - text=text..string.format("\nShots %d",_assign.nshells) - end - if _assign.maxengage then - text=text..string.format("\nEngagements %d",_assign.maxengage) - end - if _assign.weapontype then - text=text..string.format("\nWeapon %s",self:_WeaponTypeName(_assign.weapontype)) - end - MESSAGE:New(text, 10):ToCoalitionIf(batterycoalition, self.report or self.Debug) - - -- Assign a new firing engagement. - -- Note, we set unique=true so this target gets only added once. - local _targetname=self:AssignTargetCoord(_coord,_assign.prio,_assign.radius,_assign.nshells,_assign.maxengage,_assign.time,_assign.weapontype, _name, true) - - if _targetname~=nil then - local _tid=self:_GetTargetIndexByName(_targetname) - local _target=self.targets[_tid] - - -- Create new target name. - local clock=tostring(self:_SecondsToClock(_target.time)) - local weapon=self:_WeaponTypeName(_target.weapontype) - local _markertext=_targetname..string.format(", Priority=%d, Radius=%d m, Shots=%d, Engagements=%d, Weapon=%s, Time=%s", _target.prio, _target.radius, _target.nshells, _target.maxengage, weapon, clock) - - -- Create a new mark. This will trigger the mark added event. - local _randomcoord=_coord:GetRandomCoordinateInRadius(250) - _randomcoord:MarkToCoalition(_markertext, batterycoalition, self.markreadonly or _assign.readonly) - end - end - end - - end - end - -end - --- After "Start" event. Initialized ROE and alarm state. Starts the event handler. -- @param #ARTY self function ARTY:_StatusReport() -- Get Ammo. local Nammo, Nshells, Nrockets, Nmissiles=self:GetAmmo() - local Nnukes - if self.Nukes==nil then - Nnukes=0 - else - Nnukes=self.Nukes - end + local Nnukes=self.Nukes + local Tnow=timer.getTime() local Clock=self:_SecondsToClock(timer.getAbsTime()) @@ -1651,115 +1178,6 @@ end -- Event Handling ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Model a nuclear blast/destruction by creating fires and destroy scenery. --- @param #ARTY self --- @param Core.Point#COORDINATE _coord Coordinate of the impact point (center of the blast). -function ARTY:_NuclearBlast(_coord) - - local S0=self.nukewarhead - local R0=self.nukerange - - -- Number of fires - local N0=self.nukefires - - -- Create an explosion at the last known position. - _coord:Explosion(S0) - - -- Huge fire at direct impact point. - --if self.nukefire then - _coord:BigSmokeAndFireHuge() - --end - - -- Create a table of fire coordinates within the demolition zone. - local _fires={} - for i=1,N0 do - local _fire=_coord:GetRandomCoordinateInRadius(R0) - local _dist=_fire:Get2DDistance(_coord) - table.insert(_fires, {distance=_dist, coord=_fire}) - end - - -- Sort scenery wrt to distance from impact point. - local _sort = function(a,b) return a.distance < b.distance end - table.sort(_fires,_sort) - - local function _explosion(R) - -- At R=R0 ==> explosion strength is 1% of S0 at impact point. - local alpha=math.log(100) - local strength=S0*math.exp(-alpha*R/R0) - self:T2(ARTY.id..string.format("Nuclear explosion strength s(%.1f m) = %.5f (s/s0=%.1f %%), alpha=%.3f", R, strength, strength/S0*100, alpha)) - return strength - end - - local function ignite(_fires) - for _,fire in pairs(_fires) do - local _fire=fire.coord --Core.Point#COORDINATE - - -- Get distance to impact and calc exponential explosion strength. - local R=_fire:Get2DDistance(_coord) - local S=_explosion(R) - self:T2(ARTY.id..string.format("Explosion r=%.1f, s=%.3f", R, S)) - - -- Get a random Big Smoke and fire object. - local _preset=math.random(0,7) - local _density=S/S0 --math.random()+0.1 - - _fire:BigSmokeAndFire(_preset,_density) - _fire:Explosion(S) - - end - end - - if self.nukefire==true then - ignite(_fires) - end - ---[[ - local ZoneNuke=ZONE_RADIUS:New("Nukezone", _coord:GetVec2(), 2000) - - -- Scan for Scenery objects. - ZoneNuke:Scan(Object.Category.SCENERY) - - -- Array with all possible hideouts, i.e. scenery objects in the vicinity of the group. - local scenery={} - - for SceneryTypeName, SceneryData in pairs(ZoneNuke:GetScannedScenery()) do - for SceneryName, SceneryObject in pairs(SceneryData) do - - local SceneryObject = SceneryObject -- Wrapper.Scenery#SCENERY - - -- Position of the scenery object. - local spos=SceneryObject:GetCoordinate() - - -- Distance from group to impact point. - local distance= spos:Get2DDistance(_coord) - - -- Place markers on every possible scenery object. - if self.Debug then - local MarkerID=spos:MarkToAll(string.format("%s scenery object %s", self.Controllable:GetName(), SceneryObject:GetTypeName())) - local text=string.format("%s scenery: %s, Coord %s", self.Controllable:GetName(), SceneryObject:GetTypeName(), SceneryObject:GetCoordinate():ToStringLLDMS()) - self:T2(SUPPRESSION.id..text) - end - - -- Add to table. - table.insert(scenery, {object=SceneryObject, distance=distance}) - - --SceneryObject:Destroy() - end - end - - -- Sort scenery wrt to distance from impact point. --- local _sort = function(a,b) return a.distance < b.distance end --- table.sort(scenery,_sort) - --- for _,object in pairs(scenery) do --- local sobject=object -- Wrapper.Scenery#SCENERY --- sobject:Destroy() --- end - -]] - -end - --- Eventhandler for shot event. -- @param #ARTY self -- @param Core.Event#EVENTDATA EventData @@ -1833,8 +1251,7 @@ function ARTY:_OnEventShot(EventData) self:T(ARTY.id..string.format("ARTY %s: Tracking of weapon starts in two seconds.", self.Controllable:GetName())) timer.scheduleFunction(_TrackWeapon, EventData.weapon, timer.getTime() + 2.0) end - - + -- Get current ammo. local _nammo,_nshells,_nrockets,_nmissiles=self:GetAmmo() @@ -1927,6 +1344,278 @@ function ARTY:_OnEventShot(EventData) end end +--- After "Start" event. Initialized ROE and alarm state. Starts the event handler. +-- @param #ARTY self +-- @param #table Event +function ARTY:onEvent(Event) + + if Event == nil or Event.idx == nil then + self:T3("Skipping onEvent. Event or Event.idx unknown.") + return true + end + + -- Set battery and coalition. + local batteryname=self.Controllable:GetName() + local batterycoalition=self.Controllable:GetCoalition() + + self:T(string.format("Event captured = %s", tostring(batteryname))) + self:T(string.format("Event id = %s", tostring(Event.id))) + self:T(string.format("Event time = %s", tostring(Event.time))) + self:T(string.format("Event idx = %s", tostring(Event.idx))) + self:T(string.format("Event coalition = %s", tostring(Event.coalition))) + self:T(string.format("Event group id = %s", tostring(Event.groupID))) + self:T(string.format("Event text = %s", tostring(Event.text))) + self:E({eventid=Event.id, vec3=Event.pos}) + if Event.initiator~=nil then + local _unitname=Event.initiator:getName() + self:T(string.format("Event ini unit name = %s", tostring(_unitname))) + end + + if Event.id==world.event.S_EVENT_MARK_ADDED then + self:E({event="S_EVENT_MARK_ADDED", battery=batteryname, vec3=Event.pos}) + + elseif Event.id==world.event.S_EVENT_MARK_CHANGE then + self:E({event="S_EVENT_MARK_CHANGE", battery=batteryname, vec3=Event.pos}) + + -- Handle event. + self:_OnEventMarkChange(Event) + + elseif Event.id==world.event.S_EVENT_MARK_REMOVED then + self:E({event="S_EVENT_MARK_REMOVED", battery=batteryname, vec3=Event.pos}) + + -- Hande event. + self:_OnEventMarkRemove(Event) + end + +end + +--- Function called when a F10 map mark was removed. +-- @param #ARTY self +-- @param #table Event Event data. +function ARTY:_OnEventMarkRemove(Event) + + -- Get battery coalition and name. + local batterycoalition=self.Controllable:GetCoalition() + local batteryname=self.Controllable:GetName() + + if Event.text~=nil and Event.text:find("BATTERY") then + + -- Init defaults. + local _cancelmove=false + local _canceltarget=false + local _name="" + local _id=nil + + -- Check for key phrases of relocation or engagements in marker text. If not, return. + if Event.text:find("Marked Relocation") then + _cancelmove=true + _name=self:_MarkMoveName(Event.idx) + _id=self:_GetMoveIndexByName(_name) + elseif Event.text:find("Marked Target") then + _canceltarget=true + _name=self:_MarkTargetName(Event.idx) + _id=self:_GetTargetIndexByName(_name) + else + return + end + + -- Check if there is a task which matches. + if _id==nil then + return + end + + -- Check if the coalition is the same or an authorization key has been defined. + if (batterycoalition==Event.coalition and self.markkey==nil) or self.markkey~=nil then + + -- Authentify key + local _validkey=self:_MarkerKeyAuthentification(Event.text) + + -- Check if we have the right coalition. + if _validkey then + + -- This should be the unique name of the target or move. + if _cancelmove then + if self.currentMove and self.currentMove.name==_name then + self.Controllable:ClearTasks() + self:Arrived() + else + self:RemoveMove(_name) + end + elseif _canceltarget then + if self.currentTarget and self.currentTarget.name==_name then + self:CeaseFire(self.currentTarget) + self:RemoveTarget(_name) + else + self:RemoveTarget(_name) + end + end + + end + end + end +end + +--- Function called when a F10 map mark was changed. This happens when a user enters text. +-- @param #ARTY self +-- @param #table Event Event data. +function ARTY:_OnEventMarkChange(Event) + + -- Check if marker has a text and the "arty" keyword. + if Event.text~=nil and Event.text:lower():find("arty") then + + -- Get battery coalition and name. + local batterycoalition=self.Controllable:GetCoalition() + local batteryname=self.Controllable:GetName() + + -- Check if the coalition is the same or an authorization key has been defined. + if (batterycoalition==Event.coalition and self.markkey==nil) or self.markkey~=nil then + + -- Evaluate marker text and extract parameters. + local _assign=self:_Markertext(Event.text) + + -- Check if job is assigned to this ARTY group. Default is for all ARTY groups. + local _assigned=true + if #_assign.battery>0 then + _assigned=false + for _,bat in pairs(_assign.battery) do + self:T2(ARTY.id..string.format("Compare battery names %s=%s ==> %s",batteryname, bat, tostring(batteryname==bat))) + if batteryname==bat then + _assigned=true + end + end + end + + -- We were not addressed. + if not _assigned then + return + end + + -- Check if ENGAGE or MOVE or REQUEST keywords were found. + if not (_assign.engage or _assign.move or _assign.request) then + return + end + + -- Check if the authorization key is required and if it is valid. + local _validkey=self:_MarkerKeyAuthentification(Event.text) + + -- Handle requests and return. + if _assign.request and _validkey then + if _assign.requestammo then + self:_MarkRequestAmmo() + end + -- Done! + return + end + + -- Cancel current target and return. + if _assign.cancelcurrent and _validkey then + if _assign.move and self.currentMove then + self.Controllable:ClearTasks() + self:Arrived() + end + if _assign.engage and self.currentTarget then + self:CeaseFire(self.currentTarget) + end + return + end + + -- Handle engagements and relocations. + if _validkey then + + -- Convert (wrong x-->z, z-->x) vec3 + local vec3={y=Event.pos.y, x=Event.pos.z, z=Event.pos.x} + + -- Get coordinate from vec3. + local _coord=COORDINATE:NewFromVec3(vec3) + + -- Remove old mark because it might contain confidential data such as the key. + -- Also I don't know who can see the mark which was created. + _coord:RemoveMark(Event.idx) + + -- Anticipate marker ID. + -- WARNING: Make sure, no marks are set until the COORDINATE:MarkToCoalition() is called or the target/move name will be wrong and target cannot be removed by deleting its marker. + local _id=UTILS._MarkID+1 + + if _assign.move then + + -- Create a new name. This determins the string we search when deleting a move! + local _name=self:_MarkMoveName(_id) + + local text=string.format("%s, received new relocation assignment.", batteryname) + text=text..string.format("\nCoordinates %s",_coord:ToStringLLDMS()) + MESSAGE:New(text, 10):ToCoalitionIf(batterycoalition, self.report or self.Debug) + + -- Assign a relocation of the arty group. + local _movename=self:AssignMoveCoord(_coord, _assign.time, _assign.speed, _assign.onroad, _assign.cancel,_name, true) + + if _movename~=nil then + local _mid=self:_GetMoveIndexByName(_movename) + local _move=self.moves[_mid] + + -- Create new target name. + local clock=tostring(self:_SecondsToClock(_move.time)) + local _markertext=_movename..string.format(", Time=%s, Speed=%d km/h, Use Roads=%s.", clock, _move.speed, tostring(_move.onroad)) + + -- Create a new mark. This will trigger the mark added event. + local _randomcoord=_coord:GetRandomCoordinateInRadius(100) + _randomcoord:MarkToCoalition(_markertext, batterycoalition, self.markreadonly or _assign.readonly) + else + local text=string.format("%s, relocation not possible.", batteryname) + MESSAGE:New(text, 10):ToCoalitionIf(batterycoalition, self.report or self.Debug) + end + + else + + -- Create a new name. + local _name=self:_MarkTargetName(_id) + + local text=string.format("%s, received new target assignment.", batteryname) + text=text..string.format("\nCoordinates %s",_coord:ToStringLLDMS()) + if _assign.time then + text=text..string.format("\nTime %s",_assign.time) + end + if _assign.prio then + text=text..string.format("\nPrio %d",_assign.prio) + end + if _assign.prio then + text=text..string.format("\nRadius %d m",_assign.radius) + end + if _assign.nshells then + text=text..string.format("\nShots %d",_assign.nshells) + end + if _assign.maxengage then + text=text..string.format("\nEngagements %d",_assign.maxengage) + end + if _assign.weapontype then + text=text..string.format("\nWeapon %s",self:_WeaponTypeName(_assign.weapontype)) + end + MESSAGE:New(text, 10):ToCoalitionIf(batterycoalition, self.report or self.Debug) + + -- Assign a new firing engagement. + -- Note, we set unique=true so this target gets only added once. + local _targetname=self:AssignTargetCoord(_coord,_assign.prio,_assign.radius,_assign.nshells,_assign.maxengage,_assign.time,_assign.weapontype, _name, true) + + if _targetname~=nil then + local _tid=self:_GetTargetIndexByName(_targetname) + local _target=self.targets[_tid] + + -- Create new target name. + local clock=tostring(self:_SecondsToClock(_target.time)) + local weapon=self:_WeaponTypeName(_target.weapontype) + local _markertext=_targetname..string.format(", Priority=%d, Radius=%d m, Shots=%d, Engagements=%d, Weapon=%s, Time=%s", _target.prio, _target.radius, _target.nshells, _target.maxengage, weapon, clock) + + -- Create a new mark. This will trigger the mark added event. + local _randomcoord=_coord:GetRandomCoordinateInRadius(250) + _randomcoord:MarkToCoalition(_markertext, batterycoalition, self.markreadonly or _assign.readonly) + end + end + end + + end + end + +end + --- Event handler for event Dead. -- @param #ARTY self -- @param Core.Event#EVENTDATA EventData @@ -2017,7 +1706,6 @@ function ARTY:onafterStatus(Controllable, From, Event, To) -- Get a commaned move to another location. local _move=self:_CheckMoves() - if (self:is("CombatReady") or self:is("Firing")) and _move then -- Group is combat ready or firing but we have a move. @@ -2049,7 +1737,7 @@ function ARTY:onafterStatus(Controllable, From, Event, To) end end - + -- Call status again in ~10 sec. self:__Status(self.StatusInterval) end @@ -2193,11 +1881,11 @@ function ARTY:onafterOpenFire(Controllable, From, Event, To, target) self:T(ARTY.id..text) MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report) - if self.Debug then - local _coord=target.coord --Core.Point#COORDINATE - local text=string.format("ARTY %s, Target %s, n=%d, weapon=%s", self.Controllable:GetName(), target.name, target.nshells, self:_WeaponTypeName(target.weapontype)) - _coord:MarkToAll(text) - end + --if self.Debug then + -- local _coord=target.coord --Core.Point#COORDINATE + -- local text=string.format("ARTY %s, Target %s, n=%d, weapon=%s", self.Controllable:GetName(), target.name, target.nshells, self:_WeaponTypeName(target.weapontype)) + -- _coord:MarkToAll(text) + --end -- Start firing. self:_FireAtCoord(target.coord, target.radius, target.nshells, target.weapontype) @@ -2226,7 +1914,7 @@ function ARTY:onafterCeaseFire(Controllable, From, Event, To, target) -- Get target array index. local id=self:_GetTargetIndexByName(target.name) - -- Increase engaged counter + -- We have a target. if id then -- Target was actually engaged. (Could happen that engagement was aborted while group was still aiming.) if self.Nshots>0 then @@ -2270,7 +1958,7 @@ function ARTY:onafterWinchester(Controllable, From, Event, To) -- Send message. local text=string.format("%s, winchester!", Controllable:GetName()) self:T(ARTY.id..text) - MESSAGE:New(text, 30):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) + MESSAGE:New(text, 10):ToCoalitionIf(Controllable:GetCoalition(), self.report or self.Debug) end @@ -2522,8 +2210,8 @@ function ARTY:onafterArrived(Controllable, From, Event, To) -- Set alarm state to auto. self.Controllable:OptionAlarmStateAuto() - -- Clear Tasks - self.Controllable:ClearTasks() + -- WARNING: calling ClearTasks() here causes CTD of DCS when move is over. Dont know why? combotask? + --self.Controllable:ClearTasks() -- Send message local text=string.format("%s, arrived at destination.", Controllable:GetName()) @@ -2532,10 +2220,10 @@ function ARTY:onafterArrived(Controllable, From, Event, To) -- Remove executed move from queue. if self.currentMove then - --self:RemoveMove(self.currentMove.name) - --self.currentMove=nil + self:RemoveMove(self.currentMove.name) + self.currentMove=nil end - + end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -2553,7 +2241,7 @@ function ARTY:onafterNewTarget(Controllable, From, Event, To, target) -- Debug message. local text=string.format("Adding new target %s.", target.name) - --MESSAGE:New(text, 30):ToAllIf(self.Debug) + MESSAGE:New(text, 5):ToAllIf(self.Debug) self:T(ARTY.id..text) end @@ -2570,7 +2258,7 @@ function ARTY:onafterNewMove(Controllable, From, Event, To, move) -- Debug message. local text=string.format("Adding new move %s.", move.name) - MESSAGE:New(text, 30):ToAllIf(self.Debug) + MESSAGE:New(text, 5):ToAllIf(self.Debug) self:T(ARTY.id..text) end @@ -2596,7 +2284,7 @@ function ARTY:onafterDead(Controllable, From, Event, To) -- Message. local text=string.format("%s, one of our units just died! %d units left.", self.Controllable:GetName(), nunits) - MESSAGE:New(text, 10):ToAllIf(self.Debug) + MESSAGE:New(text, 5):ToAllIf(self.Debug) self:T(ARTY.id..text) -- Go to stop state. @@ -2665,6 +2353,217 @@ function ARTY:_FireAtCoord(coord, radius, nshells, weapontype) group:SetTask(fire) end +--- Model a nuclear blast/destruction by creating fires and destroy scenery. +-- @param #ARTY self +-- @param Core.Point#COORDINATE _coord Coordinate of the impact point (center of the blast). +function ARTY:_NuclearBlast(_coord) + + local S0=self.nukewarhead + local R0=self.nukerange + + -- Number of fires + local N0=self.nukefires + + -- Create an explosion at the last known position. + _coord:Explosion(S0) + + -- Huge fire at direct impact point. + --if self.nukefire then + _coord:BigSmokeAndFireHuge() + --end + + -- Create a table of fire coordinates within the demolition zone. + local _fires={} + for i=1,N0 do + local _fire=_coord:GetRandomCoordinateInRadius(R0) + local _dist=_fire:Get2DDistance(_coord) + table.insert(_fires, {distance=_dist, coord=_fire}) + end + + -- Sort scenery wrt to distance from impact point. + local _sort = function(a,b) return a.distance < b.distance end + table.sort(_fires,_sort) + + local function _explosion(R) + -- At R=R0 ==> explosion strength is 1% of S0 at impact point. + local alpha=math.log(100) + local strength=S0*math.exp(-alpha*R/R0) + self:T2(ARTY.id..string.format("Nuclear explosion strength s(%.1f m) = %.5f (s/s0=%.1f %%), alpha=%.3f", R, strength, strength/S0*100, alpha)) + return strength + end + + local function ignite(_fires) + for _,fire in pairs(_fires) do + local _fire=fire.coord --Core.Point#COORDINATE + + -- Get distance to impact and calc exponential explosion strength. + local R=_fire:Get2DDistance(_coord) + local S=_explosion(R) + self:T2(ARTY.id..string.format("Explosion r=%.1f, s=%.3f", R, S)) + + -- Get a random Big Smoke and fire object. + local _preset=math.random(0,7) + local _density=S/S0 --math.random()+0.1 + + _fire:BigSmokeAndFire(_preset,_density) + _fire:Explosion(S) + + end + end + + if self.nukefire==true then + ignite(_fires) + end + +--[[ + local ZoneNuke=ZONE_RADIUS:New("Nukezone", _coord:GetVec2(), 2000) + + -- Scan for Scenery objects. + ZoneNuke:Scan(Object.Category.SCENERY) + + -- Array with all possible hideouts, i.e. scenery objects in the vicinity of the group. + local scenery={} + + for SceneryTypeName, SceneryData in pairs(ZoneNuke:GetScannedScenery()) do + for SceneryName, SceneryObject in pairs(SceneryData) do + + local SceneryObject = SceneryObject -- Wrapper.Scenery#SCENERY + + -- Position of the scenery object. + local spos=SceneryObject:GetCoordinate() + + -- Distance from group to impact point. + local distance= spos:Get2DDistance(_coord) + + -- Place markers on every possible scenery object. + if self.Debug then + local MarkerID=spos:MarkToAll(string.format("%s scenery object %s", self.Controllable:GetName(), SceneryObject:GetTypeName())) + local text=string.format("%s scenery: %s, Coord %s", self.Controllable:GetName(), SceneryObject:GetTypeName(), SceneryObject:GetCoordinate():ToStringLLDMS()) + self:T2(SUPPRESSION.id..text) + end + + -- Add to table. + table.insert(scenery, {object=SceneryObject, distance=distance}) + + --SceneryObject:Destroy() + end + end + + -- Sort scenery wrt to distance from impact point. +-- local _sort = function(a,b) return a.distance < b.distance end +-- table.sort(scenery,_sort) + +-- for _,object in pairs(scenery) do +-- local sobject=object -- Wrapper.Scenery#SCENERY +-- sobject:Destroy() +-- end + +]] + +end + +--- Route group to a certain point. +-- @param #ARTY self +-- @param Wrapper.Group#GROUP group Group to route. +-- @param Core.Point#COORDINATE ToCoord Coordinate where we want to go. +-- @param #number Speed Speed in km/h. +-- @param #boolean OnRoad If true, use (mainly) roads. +function ARTY:_Move(group, ToCoord, Speed, OnRoad) + + -- Clear all tasks. + group:ClearTasks() + group:OptionAlarmStateGreen() + group:OptionROEHoldFire() + + -- Set formation. + local formation = "Off Road" + + -- Default speed is 30 km/h. + Speed=Speed or 30 + + -- Current coordinates of group. + local cpini=group:GetCoordinate() + + -- Distance between current and final point. + local dist=cpini:Get2DDistance(ToCoord) + + -- Waypoint and task arrays. + local path={} + local task={} + + -- First waypoint is the current position of the group. + path[#path+1]=cpini:WaypointGround(Speed, formation) + task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, false) + + -- Route group on road if requested. + if OnRoad then + + -- Path on road (only first and last points) + local _first=cpini:GetClosestPointToRoad() + local _last=ToCoord:GetClosestPointToRoad() + + -- First point on road. + path[#path+1]=_first:WaypointGround(Speed, "On Road") + task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, false) + + -- Last point on road. + path[#path+1]=_last:WaypointGround(Speed, "On Road") + task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, false) + + end + + -- Last waypoint at ToCoord. + path[#path+1]=ToCoord:WaypointGround(Speed, formation) + task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, true) + + + -- Init waypoints of the group. + local Waypoints={} + + -- New points are added to the default route. + for i=1,#path do + table.insert(Waypoints, i, path[i]) + end + + -- Set task for all waypoints. + for i=1,#Waypoints do + group:SetTaskWaypoint(Waypoints[i], task[i]) + end + + -- Submit task and route group along waypoints. + group:Route(Waypoints) + +end + +--- Function called when group is passing a waypoint. +-- @param Wrapper.Group#GROUP group Group for which waypoint passing should be monitored. +-- @param #ARTY arty ARTY object. +-- @param #number i Waypoint number that has been reached. +-- @param #boolean final True if it is the final waypoint. +function ARTY._PassingWaypoint(group, arty, i, final) + + -- Debug message. + local text=string.format("%s, passing waypoint %d.", group:GetName(), i) + if final then + text=string.format("%s, arrived at destination.", group:GetName()) + end + arty:T(ARTY.id..text) + + --[[ + if final then + MESSAGE:New(text, 10):ToCoalitionIf(group:GetCoalition(), arty.Debug or arty.report) + else + MESSAGE:New(text, 10):ToAllIf(arty.Debug) + end + ]] + + -- Arrived event. + if final and arty.Controllable:GetName()==group:GetName() then + arty:Arrived() + end + +end + --- Relocate to another position, e.g. after an engagement to avoid couter strikes. -- @param #ARTY self function ARTY:_Relocate() @@ -2695,155 +2594,6 @@ function ARTY:_Relocate() end end ---- Sort targets with respect to priority and number of times it was already engaged. --- @param #ARTY self -function ARTY:_SortTargetQueuePrio() - self:F2() - - -- Sort results table wrt times they have already been engaged. - local function _sort(a, b) - return (a.engaged < b.engaged) or (a.engaged==b.engaged and a.prio < b.prio) - end - table.sort(self.targets, _sort) - - -- Debug output. - self:T3(ARTY.id.."Sorted targets wrt prio and number of engagements:") - for i=1,#self.targets do - local _target=self.targets[i] - self:T3(ARTY.id..string.format("Target %s", self:_TargetInfo(_target))) - end -end - ---- Sort array with respect to time. Array elements must have a .time entry. --- @param #ARTY self --- @param #table queue Array to sort. Should have elemnt .time. -function ARTY:_SortQueueTime(queue) - self:F3({queue=queue}) - - -- Sort targets w.r.t attack time. - local function _sort(a, b) - if a.time == nil and b.time == nil then - return false - end - if a.time == nil then - return false - end - if b.time == nil then - return true - end - return a.time < b.time - end - table.sort(queue, _sort) - - -- Debug output. - self:T3(ARTY.id.."Sorted queue wrt time:") - for i=1,#queue do - local _queue=queue[i] - local _time=tostring(_queue.time) - local _clock=tostring(self:_SecondsToClock(_queue.time)) - self:T3(ARTY.id..string.format("%s: time=%s, clock=%s", _queue.name, _time, _clock)) - end - -end - ---- Check all timed targets and return the target which should be attacked next. --- @param #ARTY self --- @return #table Target which is due to be attacked now. -function ARTY:_CheckTimedTargets() - self:F3() - - -- Current time. - local Tnow=timer.getAbsTime() - - -- Sort Targets wrt time. - self:_SortQueueTime(self.targets) - - for i=1,#self.targets do - local _target=self.targets[i] - - -- Debug info. - self:T3(ARTY.id..string.format("Check TIMED target %d: %s", i, self:_TargetInfo(_target))) - - -- Check if target has an attack time which has already passed. Also check that target is not under fire already and that it is in range. - if _target.time and Tnow>=_target.time and _target.underfire==false and self:_TargetInRange(_target) then - - -- Check if group currently has a target and whether its priorty is lower than the timed target. - if self.currentTarget then - if self.currentTarget.prio > _target.prio then - -- Current target under attack but has lower priority than this target. - self:T2(ARTY.id..string.format("Found TIMED HIGH PRIO target %s.", self:_TargetInfo(_target))) - return _target - end - else - -- No current target. - self:T2(ARTY.id..string.format("Found TIMED target %s.", self:_TargetInfo(_target))) - return _target - end - end - end - - return nil -end - ---- Check all moves and return the one which should be executed next. --- @param #ARTY self --- @return #table Move which is due. -function ARTY:_CheckMoves() - self:F3() - - -- Current time. - local Tnow=timer.getAbsTime() - - -- Sort Targets wrt time. - self:_SortQueueTime(self.moves) - - -- Check if we are currently firing. - local firing=false - if self.currentTarget then - firing=true - end - - for i=1,#self.moves do - local _move=self.moves[i] - - -- Check if time for move is reached. - if Tnow >= _move.time and (firing==false or _move.cancel) then - return _move - end - end - - return nil -end - ---- Check all normal (untimed) targets and return the target with the highest priority which has been engaged the fewest times. --- @param #ARTY self --- @return #table Target which is due to be attacked now or nil if no target could be found. -function ARTY:_CheckNormalTargets() - self:F3() - - -- Sort targets w.r.t. prio and number times engaged already. - self:_SortTargetQueuePrio() - - -- Loop over all sorted targets. - for i=1,#self.targets do - local _target=self.targets[i] - - -- Debug info. - self:T3(ARTY.id..string.format("Check NORMAL target %d: %s", i, self:_TargetInfo(_target))) - - -- Check that target no time, is not under fire currently and in range. - if _target.underfire==false and _target.time==nil and _target.maxengage > _target.engaged and self:_TargetInRange(_target) then - - -- Debug info. - self:T2(ARTY.id..string.format("Found NORMAL target %s", self:_TargetInfo(_target))) - - return _target - end - end - - return nil -end - --- Get the number of shells a unit or group currently has. For a group the ammo count of all units is summed up. -- @param #ARTY self -- @param #boolean display Display ammo table as message to all. Default false. @@ -3007,6 +2757,405 @@ function ARTY:GetAmmo(display) return nammo, nshells, nrockets, nmissiles end +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Mark Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Extract engagement assignments and parameters from mark text. +-- @param #ARTY self +-- @param #string text Marker text. +-- @return #boolean If true, authentification successful. +function ARTY:_MarkerKeyAuthentification(text) + + -- Set battery and coalition. + local batteryname=self.Controllable:GetName() + local batterycoalition=self.Controllable:GetCoalition() + + -- Get assignment. + local mykey=nil + if self.markkey~=nil then + + -- keywords are split by "," + local keywords=self:_split(text, ",") + for _,key in pairs(keywords) do + local s=self:_split(key, " ") + local val=s[2] + if key:lower():find("key") then + mykey=tonumber(val) + self:T(ARTY.id..string.format("Authorisation Key=%s.", val)) + end + end + + end + + -- Check if the authorization key is required and if it is valid. + local _validkey=true + + -- Check if group needs authorization. + if self.markkey~=nil then + -- Assume key is incorrect. + _validkey=false + + -- If key was found, check if matches. + if mykey~=nil then + _validkey=self.markkey==mykey + end + self:T2(ARTY.id..string.format("%s, authkey=%s == %s=playerkey ==> valid=%s", batteryname, tostring(self.markkey), tostring(mykey), tostring(_validkey))) + + -- Send message + local text="" + if mykey==nil then + text=string.format("%s, authorization required but did not receive a key!", batteryname) + elseif _validkey==false then + text=string.format("%s, authorization required but did receive an incorrect key (key=%s)!", batteryname, tostring(mykey)) + elseif _validkey==true then + text=string.format("%s, authentification successful!", batteryname) + end + MESSAGE:New(text, 10):ToCoalitionIf(batterycoalition, self.report or self.Debug) + end + + return _validkey +end + +--- Extract engagement assignments and parameters from mark text. +-- @param #ARTY self +-- @param #string text Marker text to be analyzed. +-- @return #table Table with assignment parameters, e.g. number of shots, radius, time etc. +function ARTY:_Markertext(text) + self:F(text) + + -- Assignment parameters. + local assignment={} + assignment.battery={} + assignment.move=false + assignment.engage=false + assignment.request=false + assignment.readonly=false + assignment.cancelcurrent=false + --assignment.time=nil + --assignment.nshells=nil + --assignment.prio=nil + --assignment.maxengage=nil + --assignment.radius=nil + --assignment.weapontype=nil + --assignment.speed=nil + --assignment.onroad=nil + --assignment.key=nil + + if text:lower():find("arty") then + if text:lower():find("engage") then + assignment.engage=true + elseif text:lower():find("move") then + assignment.move=true + elseif text:lower():find("request") then + assignment.request=true + else + self:E(ARTY.id.."ERROR: Neither ENGAGE nor MOVE keyword specified!") + return + end + + -- keywords are split by "," + local keywords=self:_split(text, ",") + + for _,key in pairs(keywords) do + + local s=self:_split(key, " ") + local val=s[2] + + -- Battery name, i.e. which ARTY group should fire. + if key:lower():find("battery") then + + local v=self:_split(key, '"') + + for i=2,#v,2 do + table.insert(assignment.battery, v[i]) + self:T2(ARTY.id..string.format("Key Battery=%s.", v[i])) + end + + elseif key:lower():find("time") then + + if val:lower():find("now") then + assignment.time=self:_SecondsToClock(timer.getTime0()+5) + else + assignment.time=val + end + self:T2(ARTY.id..string.format("Key Time=%s.", val)) + + elseif key:lower():find("shots") then + + assignment.nshells=tonumber(s[2]) + self:T(ARTY.id..string.format("Key Shots=%s.", val)) + + elseif key:lower():find("prio") then + + assignment.prio=tonumber(val) + self:T2(string.format("Key Prio=%s.", val)) + + elseif key:lower():find("maxengage") then + + assignment.maxengage=tonumber(val) + self:T2(ARTY.id..string.format("Key Maxengage=%s.", val)) + + elseif key:lower():find("radius") then + + assignment.radius=tonumber(val) + self:T2(ARTY.id..string.format("Key Radius=%s.", val)) + + elseif key:lower():find("weapon") then + + if val:lower():find("cannon") then + assignment.weapontype=ARTY.WeaponType.Cannon + elseif val:lower():find("rocket") then + assignment.weapontype=ARTY.WeaponType.Rockets + elseif val:lower():find("missile") then + assignment.weapontype=ARTY.WeaponType.GuidedMissile + elseif val:lower():find("nuke") then + assignment.weapontype=ARTY.WeaponType.TacticalNukes + else + assignment.weapontype=ARTY.WeaponType.Auto + end + self:T2(ARTY.id..string.format("Key Weapon=%s.", val)) + + elseif key:lower():find("speed") then + + assignment.speed=tonumber(val) + self:T2(ARTY.id..string.format("Key Speed=%s.", val)) + + elseif key:lower():find("on road") or key:lower():find("onroad") or key:lower():find("use road")then + + assignment.onroad=true + self:T2(ARTY.id..string.format("Key Onroad=true.")) + + elseif key:lower():find("irrevocable") or key:lower():find("readonly") then + assignment.readonly=true + self:T2(ARTY.id..string.format("Key Readonly=true.")) + + elseif key:lower():find("cancel current") then + assignment.cancelcurrent=true + self:T2(ARTY.id..string.format("Key Cancel Current=true.")) + elseif assignment.request and key:lower():find("ammo") then + assignment.requestammo=true + end + + end + else + self:T2(ARTY.id..string.format("This is NO arty command:\n%s", tostring(text))) + end + + return assignment +end + +--- Request ammo. +-- @param #ARTY self +function ARTY:_MarkRequestAmmo() + self:GetAmmo(true) +end + +--- Create a name for an engagement initiated by placing a marker. +-- @param #ARTY self +-- @param #number markerid ID of the placed marker. +-- @return #string Name of target engagement. +function ARTY:_MarkTargetName(markerid) + return string.format("BATTERY=%s, Marked Target ID=%d", self.Controllable:GetName(), markerid) +end + +--- Create a name for a relocation move initiated by placing a marker. +-- @param #ARTY self +-- @param #number markerid ID of the placed marker. +-- @return #string Name of relocation move. +function ARTY:_MarkMoveName(markerid) + return string.format("BATTERY=%s, Marked Relocation ID=%d", self.Controllable:GetName(), markerid) +end + +--- Create a name for a relocation move initiated by placing a marker. +-- @param #ARTY self +-- @param #string name Name of the assignment. +-- @return #string Name of the ARTY group or nil +-- @return #number ID of the marked target or nil. +-- @return #number ID of the marked relocation move or nil +function ARTY:_GetMarkIDfromName(name) + + -- keywords are split by "," + local keywords=self:_split(name, ",") + + local battery=nil + local markTID=nil + local markMID=nil + + for _,key in pairs(keywords) do + + local str=self:_split(key, "=") + local par=str[1] + local val=str[2] + + if par:find("BATTERY") then + battery=val + end + if par:find("Marked Target ID") then + markTID=tonumber(val) + end + if par:find("Marked Relocation ID") then + markMID=tonumber(val) + end + + end + + return battery, markTID, markMID +end + + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Helper Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Sort targets with respect to priority and number of times it was already engaged. +-- @param #ARTY self +function ARTY:_SortTargetQueuePrio() + self:F2() + + -- Sort results table wrt times they have already been engaged. + local function _sort(a, b) + return (a.engaged < b.engaged) or (a.engaged==b.engaged and a.prio < b.prio) + end + table.sort(self.targets, _sort) + + -- Debug output. + self:T3(ARTY.id.."Sorted targets wrt prio and number of engagements:") + for i=1,#self.targets do + local _target=self.targets[i] + self:T3(ARTY.id..string.format("Target %s", self:_TargetInfo(_target))) + end +end + +--- Sort array with respect to time. Array elements must have a .time entry. +-- @param #ARTY self +-- @param #table queue Array to sort. Should have elemnt .time. +function ARTY:_SortQueueTime(queue) + self:F3({queue=queue}) + + -- Sort targets w.r.t attack time. + local function _sort(a, b) + if a.time == nil and b.time == nil then + return false + end + if a.time == nil then + return false + end + if b.time == nil then + return true + end + return a.time < b.time + end + table.sort(queue, _sort) + + -- Debug output. + self:T3(ARTY.id.."Sorted queue wrt time:") + for i=1,#queue do + local _queue=queue[i] + local _time=tostring(_queue.time) + local _clock=tostring(self:_SecondsToClock(_queue.time)) + self:T3(ARTY.id..string.format("%s: time=%s, clock=%s", _queue.name, _time, _clock)) + end + +end + +--- Check all timed targets and return the target which should be attacked next. +-- @param #ARTY self +-- @return #table Target which is due to be attacked now. +function ARTY:_CheckTimedTargets() + self:F3() + + -- Current time. + local Tnow=timer.getAbsTime() + + -- Sort Targets wrt time. + self:_SortQueueTime(self.targets) + + for i=1,#self.targets do + local _target=self.targets[i] + + -- Debug info. + self:T3(ARTY.id..string.format("Check TIMED target %d: %s", i, self:_TargetInfo(_target))) + + -- Check if target has an attack time which has already passed. Also check that target is not under fire already and that it is in range. + if _target.time and Tnow>=_target.time and _target.underfire==false and self:_TargetInRange(_target) then + + -- Check if group currently has a target and whether its priorty is lower than the timed target. + if self.currentTarget then + if self.currentTarget.prio > _target.prio then + -- Current target under attack but has lower priority than this target. + self:T2(ARTY.id..string.format("Found TIMED HIGH PRIO target %s.", self:_TargetInfo(_target))) + return _target + end + else + -- No current target. + self:T2(ARTY.id..string.format("Found TIMED target %s.", self:_TargetInfo(_target))) + return _target + end + end + end + + return nil +end + +--- Check all moves and return the one which should be executed next. +-- @param #ARTY self +-- @return #table Move which is due. +function ARTY:_CheckMoves() + self:F3() + + -- Current time. + local Tnow=timer.getAbsTime() + + -- Sort Targets wrt time. + self:_SortQueueTime(self.moves) + + -- Check if we are currently firing. + local firing=false + if self.currentTarget then + firing=true + end + + for i=1,#self.moves do + local _move=self.moves[i] + + -- Check if time for move is reached. + if Tnow >= _move.time and (firing==false or _move.cancel) then + return _move + end + end + + return nil +end + +--- Check all normal (untimed) targets and return the target with the highest priority which has been engaged the fewest times. +-- @param #ARTY self +-- @return #table Target which is due to be attacked now or nil if no target could be found. +function ARTY:_CheckNormalTargets() + self:F3() + + -- Sort targets w.r.t. prio and number times engaged already. + self:_SortTargetQueuePrio() + + -- Loop over all sorted targets. + for i=1,#self.targets do + local _target=self.targets[i] + + -- Debug info. + self:T3(ARTY.id..string.format("Check NORMAL target %d: %s", i, self:_TargetInfo(_target))) + + -- Check that target no time, is not under fire currently and in range. + if _target.underfire==false and _target.time==nil and _target.maxengage > _target.engaged and self:_TargetInRange(_target) then + + -- Debug info. + self:T2(ARTY.id..string.format("Found NORMAL target %s", self:_TargetInfo(_target))) + + return _target + end + end + + return nil +end --- Check whether shooting started within a certain time (~5 min). If not, the current target is considered invalid and removed from the target list. -- @param #ARTY self @@ -3084,8 +3233,6 @@ function ARTY:_GetMoveIndexByName(name) return nil end - - --- Check if a name is unique. If not, a new unique name can be created by adding a running index #01, #02, ... -- @param #ARTY self -- @param #table givennames Table with entries of already given names. Must contain a .name item. @@ -3178,7 +3325,7 @@ function ARTY:_TargetInRange(target) -- Debug output. if not _inrange then self:T(ARTY.id..text) - MESSAGE:New(text, 10):ToCoalitionIf(self.Controllable:GetCoalition(), self.report or self.Debug) + MESSAGE:New(text, 5):ToCoalitionIf(self.Controllable:GetCoalition(), self.report or self.Debug) end -- Remove target if ARTY group cannot move. No change to be ever in range. @@ -3355,106 +3502,7 @@ function ARTY:_ClockToSeconds(clock) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Route group to a certain point. --- @param #ARTY self --- @param Wrapper.Group#GROUP group Group to route. --- @param Core.Point#COORDINATE ToCoord Coordinate where we want to go. --- @param #number Speed Speed in km/h. --- @param #boolean OnRoad If true, use (mainly) roads. -function ARTY:_Move(group, ToCoord, Speed, OnRoad) - - -- Clear all tasks. - group:ClearTasks() - group:OptionAlarmStateGreen() - group:OptionROEHoldFire() - - -- Set formation. - local formation = "Off road" - - -- Default speed is 30 km/h. - Speed=Speed or 30 - - -- Current coordinates of group. - local cpini=group:GetCoordinate() - - -- Distance between current and final point. - local dist=cpini:Get2DDistance(ToCoord) - - -- Waypoint and task arrays. - local path={} - local task={} - - -- First waypoint is the current position of the group. - path[#path+1]=cpini:WaypointGround(Speed, formation) - task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, false) - - -- Route group on road if requested. - if OnRoad then - - -- Path on road (only first and last points) - local _first=cpini:GetClosestPointToRoad() - local _last=ToCoord:GetClosestPointToRoad() - - -- First point on road. - path[#path+1]=_first:WaypointGround(Speed, "On Road") - task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, false) - - -- Last point on road. - path[#path+1]=_last:WaypointGround(Speed, "On Road") - task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, false) - - end - - -- Last waypoint at ToCoord. - path[#path+1]=ToCoord:WaypointGround(Speed, formation) - task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, true) - - - -- Init waypoints of the group. - local Waypoints={} - - -- New points are added to the default route. - for i=1,#path do - table.insert(Waypoints, i, path[i]) - end - - -- Set task for all waypoints. - for i=1,#Waypoints do - group:SetTaskWaypoint(Waypoints[i], task[i]) - end - - -- Submit task and route group along waypoints. - group:Route(Waypoints) - -end - ---- Function called when group is passing a waypoint. --- @param Wrapper.Group#GROUP group Group for which waypoint passing should be monitored. --- @param #ARTY arty ARTY object. --- @param #number i Waypoint number that has been reached. --- @param #boolean final True if it is the final waypoint. -function ARTY._PassingWaypoint(group, arty, i, final) - - -- Debug message. - local text=string.format("%s, passing waypoint %d.", group:GetName(), i) - if final then - text=string.format("%s, arrived at destination.", group:GetName()) - end - arty:T(ARTY.id..text) - - --[[ - if final then - MESSAGE:New(text, 10):ToCoalitionIf(group:GetCoalition(), arty.Debug or arty.report) - else - MESSAGE:New(text, 10):ToAllIf(arty.Debug) - end - ]] - - -- Arrived event. - if final and arty.Controllable:GetName()==group:GetName() then - arty:Arrived() - end - -end \ No newline at end of file diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 5e7a15ef9..c438e751c 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -355,9 +355,9 @@ function CONTROLLABLE:SetTask( DCSTask, WaitTime ) local function SetTask( Controller, DCSTask ) if self and self:IsAlive() then local Controller = self:_GetController() - self:I( "Before SetTask" ) + --self:I( "Before SetTask" ) Controller:setTask( DCSTask ) - self:I( "After SetTask" ) + --self:I( "After SetTask" ) else BASE:E( { DCSControllableName .. " is not alive anymore.", DCSTask = DCSTask } ) end From bf7523fa8530c09a35fd3a4bffaea4065beecdce Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sun, 3 Jun 2018 01:00:16 +0200 Subject: [PATCH 153/420] messages clearsceen --- Moose Development/Moose/Core/Message.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index e11f807ea..f496796a1 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -317,7 +317,7 @@ function MESSAGE:ToAll() if self.MessageDuration ~= 0 then self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration ) - trigger.action.outText( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration ) + trigger.action.outText( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration, self.ClearScreen ) end return self From 7b3f84f468db36e156ea5c7e334783a01ca1adee Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sun, 3 Jun 2018 08:05:42 +0200 Subject: [PATCH 154/420] orbit with speed --- Moose Development/Moose/AI/AI_Cargo_Helicopter.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index a92dd1e95..e8b7d24ce 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -254,7 +254,7 @@ function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordina local Distance = Coordinate:DistanceFromPointVec2( Helicopter:GetCoordinate() ) - if Distance > 500 then + if Distance > 2000 then self:__Queue( -10, Coordinate ) else @@ -348,7 +348,7 @@ function AI_CARGO_HELICOPTER:onafterOrbit( Helicopter, From, Event, To, Coordina Route[#Route+1] = WaypointTo local Tasks = {} - Tasks[#Tasks+1] = Helicopter:TaskOrbitCircle( math.random( 30, 80 ), 0, CoordinateTo:GetRandomCoordinateInRadius( 800, 500 ) ) + Tasks[#Tasks+1] = Helicopter:TaskOrbitCircle( math.random( 30, 80 ), 150, CoordinateTo:GetRandomCoordinateInRadius( 800, 500 ) ) Route[#Route].task = Helicopter:TaskCombo( Tasks ) Route[#Route+1] = WaypointTo @@ -641,7 +641,7 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin local Tasks = {} Tasks[#Tasks+1] = Helicopter:TaskFunction( "AI_CARGO_HELICOPTER._Deploy", self, Coordinate ) - Tasks[#Tasks+1] = Helicopter:TaskOrbitCircle( math.random( 30, 100 ), 0, CoordinateTo:GetRandomCoordinateInRadius( 800, 500 ) ) + Tasks[#Tasks+1] = Helicopter:TaskOrbitCircle( math.random( 30, 100 ), 150, CoordinateTo:GetRandomCoordinateInRadius( 800, 500 ) ) --Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() ) Route[#Route].task = Helicopter:TaskCombo( Tasks ) From bd7c822def1053379ae81c3af3ae8224513fae44 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sun, 3 Jun 2018 09:14:27 +0200 Subject: [PATCH 155/420] Documentation --- .../Moose/AI/AI_A2A_Dispatcher.lua | 19 +++++++++++ Moose Development/Moose/AI/AI_Balancer.lua | 9 +++++- .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 14 +++++++- .../AI/AI_Cargo_Dispatcher_Helicopter.lua | 2 +- Moose Development/Moose/AI/AI_Formation.lua | 32 ++++--------------- 5 files changed, 47 insertions(+), 29 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index 1dd660b49..7a017f854 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -1,5 +1,24 @@ --- **AI** - (R2.2) - Manages the process of an automatic A2A defense system based on an EWR network targets and coordinating CAP and GCI. -- +-- +-- Features: +-- +-- * Setup quickly an A2A defense system for a coalition. +-- * Setup (CAP) Control Air Patrols at defined zones to enhance your A2A defenses. +-- * Setup (GCI) Ground Control Intercept at defined airbases to enhance your A2A defenses. +-- * Define and use an EWR (Early Warning Radar) network. +-- * Define squadrons at airbases. +-- * Enable airbases for A2A defenses. +-- * Add different plane types to different squadrons. +-- * Add multiple squadrons to different airbases. +-- * Define different ranges to engage upon intruders. +-- * Establish an automatic in air refuel process for CAP using refuel tankers. +-- * Setup default settings for all squadrons and A2A defenses. +-- * Setup specific settings for specific squadrons. +-- * Quickly setup an A2A defense system using @{#AI_A2A_GCICAP}. +-- * Setup a more advanced defense system using @{#AI_A2A_DISPATCHER}. +-- +-- -- # QUICK START GUIDE -- -- There are basically two classes available to model an A2A defense system. diff --git a/Moose Development/Moose/AI/AI_Balancer.lua b/Moose Development/Moose/AI/AI_Balancer.lua index 845826703..607a22ecc 100644 --- a/Moose Development/Moose/AI/AI_Balancer.lua +++ b/Moose Development/Moose/AI/AI_Balancer.lua @@ -1,4 +1,11 @@ ---- **AI** -- (2.1) - Balance player slots with AI to create an engaging simulation environment, independent of the amount of players. +--- **AI** -- Balance player slots with AI to create an engaging simulation environment, independent of the amount of players. +-- +-- ** Features:** +-- +-- * Automatically spawn AI as a replacement of free player slots for a coalition. +-- * Make the AI to perform tasks. +-- * Define a maximum amount of AI to be active at the same time. +-- * Configure the behaviour of AI when a human joins a slot for which an AI is active. -- -- === -- diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index cb400cecc..cf4fda279 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -1,5 +1,17 @@ ---- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo using APCs. +--- **AI** -- Models the intelligent transportation of infantry and other cargo using APCs. -- +-- **Features:** +-- +-- * Quickly transport cargo to various deploy zones using ground vehicles (APCs, trucks ...). +-- * Various @{Cargo.Cargo#CARGO} types can be transported. These are infantry groups and crates. +-- * Define a list of deploy zones of various types to transport the cargo to. +-- * The vehicles follow the roads to ensure the fastest possible cargo transportation over the ground. +-- * Multiple vehicles can transport multiple cargo as one vehicle group. +-- * Multiple vehicle groups can be enabled as one collaborating transportation process. +-- * Infantry loaded as cargo, will unboard in case enemies are nearby and will help defending the vehicles. +-- * Different ranges can be setup for enemy defenses. +-- * Different options can be setup to tweak the cargo transporation behaviour. +-- -- === -- -- ### Author: **FlightControl** diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index 8a53f1069..eabc91dc4 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -1,4 +1,4 @@ ---- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo using Helicopters. +--- **AI** -- Models the intelligent transportation of infantry and other cargo using Helicopters. -- -- The @{#AI_CARGO_DISPATCHER_HELICOPTER} classes implements the dynamic dispatching of cargo transportation tasks for helicopters. -- diff --git a/Moose Development/Moose/AI/AI_Formation.lua b/Moose Development/Moose/AI/AI_Formation.lua index e262a67e4..af5805bb4 100644 --- a/Moose Development/Moose/AI/AI_Formation.lua +++ b/Moose Development/Moose/AI/AI_Formation.lua @@ -1,31 +1,11 @@ ---- **AI** -- (R2.2) - Build large airborne formations of aircraft. +--- **AI** -- Build large airborne formations of aircraft. -- --- === +-- **Features:** +-- +-- * Build in-air formations consisting of more than 40 aircraft as one group. +-- * Build different formation types. +-- * Assign a group leader that will guide the large formation path. -- --- AI_FORMATION makes AI @{Wrapper.Group}s fly in formation of various compositions. --- The AI_FORMATION class models formations in a different manner than the internal DCS formation logic!!! --- The purpose of the class is to: --- --- * Make formation building a process that can be managed while in flight, rather than a task. --- * Human players can guide formations, consisting of larget planes. --- * Build large formations (like a large bomber field). --- * Form formations that DCS does not support off the shelve. --- --- A few remarks: --- --- * Depending on the type of plane, the change in direction by the leader may result in the formation getting disentangled while in flight and needs to be rebuild. --- * Formations are vulnerable to collissions, but is depending on the type of plane, the distance between the planes and the speed and angle executed by the leader. --- * Formations may take a while to build up. --- --- As a result, the AI_FORMATION is not perfect, but is very useful to: --- --- * Model large formations when flying straight line. --- * Make humans guide a large formation, when the planes are wide from each other. --- --- There are the following types of classes defined: --- --- * @{#AI_FORMATION}: Create a formation from several @{GROUP}s. --- -- === -- -- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20Formation) From 6a71921270e316951bd322f28c6f5cce6cd99518 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sun, 3 Jun 2018 18:07:00 +0200 Subject: [PATCH 156/420] Documentation AI --- Moose Development/Moose/AI/AI_BAI.lua | 11 ++++++++++- Moose Development/Moose/AI/AI_CAP.lua | 12 ++++++++++-- Moose Development/Moose/AI/AI_CAS.lua | 11 ++++++++++- .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 6 +++--- Moose Development/Moose/AI/AI_Patrol.lua | 8 +++++++- 5 files changed, 40 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/AI/AI_BAI.lua b/Moose Development/Moose/AI/AI_BAI.lua index 0265e7113..42bccbf06 100644 --- a/Moose Development/Moose/AI/AI_BAI.lua +++ b/Moose Development/Moose/AI/AI_BAI.lua @@ -1,4 +1,13 @@ ---- **AI** -- (R2.1) - Manages the independent process of Battlefield Air Interdiction (bombing) for airplanes. +--- **AI** -- Peform Battlefield Area Interdiction (BAI) within an engagement zone. +-- +-- **Features:** +-- +-- * Hold and standby within a patrol zone. +-- * Engage upon command the assigned targets within an engagement zone. +-- * Loop the zone until all targets are eliminated. +-- * Trigger different events upon the results achieved. +-- * After combat, return to the patrol zone and hold. +-- * RTB when commanded or after out of fuel. -- -- === -- diff --git a/Moose Development/Moose/AI/AI_CAP.lua b/Moose Development/Moose/AI/AI_CAP.lua index e98f4f135..1ffdbc23b 100644 --- a/Moose Development/Moose/AI/AI_CAP.lua +++ b/Moose Development/Moose/AI/AI_CAP.lua @@ -1,4 +1,12 @@ ---- **AI** -- (R2.1) - Manages the independent process of Combat Air Patrol (CAP) for airplanes. +--- **AI** -- Perform Combat Air Patrolling (CAP) for airplanes. +-- +-- **Features:** +-- +-- * Patrol AI airplanes within a given zone. +-- * Trigger detected events when enemy airplanes are detected. +-- * Manage a fuel treshold to RTB on time. +-- * Engage the enemy when detected. +-- -- -- === -- @@ -31,7 +39,7 @@ -- @extends AI.AI_Patrol#AI_PATROL_ZONE --- Implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Controllable} or @{Wrapper.Group} +--- Implements the core functions to patrol a @{Zone} by an AI @{Wrapper.Controllable} or @{Wrapper.Group} -- and automatically engage any airborne enemies that are within a certain range or within a certain zone. -- -- ![Process](..\Presentations\AI_CAP\Dia3.JPG) diff --git a/Moose Development/Moose/AI/AI_CAS.lua b/Moose Development/Moose/AI/AI_CAS.lua index 7ddb55876..33e07849d 100644 --- a/Moose Development/Moose/AI/AI_CAS.lua +++ b/Moose Development/Moose/AI/AI_CAS.lua @@ -1,4 +1,13 @@ ---- **AI** -- (R2.1) - Manages the independent process of Close Air Support for airplanes. +--- **AI** -- Perform Close Air Support (CAS) near friendlies. +-- +-- **Features:** +-- +-- * Hold and standby within a patrol zone. +-- * Engage upon command the enemies within an engagement zone. +-- * Loop the zone until all enemies are eliminated. +-- * Trigger different events upon the results achieved. +-- * After combat, return to the patrol zone and hold. +-- * RTB when commanded or after fuel. -- -- === -- diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index cf4fda279..561125786 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -91,7 +91,7 @@ AI_CARGO_DISPATCHER_APC = { -- @param Core.Set#SET_GROUP SetAPC The collection of APC @{Wrapper.Group}s. -- @param Core.Set#SET_CARGO SetCargo The collection of @{Cargo} derived objects. -- @param Core.Set#SET_ZONE SetDeployZone The collection of deploy @{Zone}s, which are used to where the cargo will be deployed by the APCs. --- @param #number CombatRadius The cargo will be unloaded from the APC and engage the enemy if the enemy is within CombatRadius range. The radius is in meters, the default value is 500 meters. +-- @param DCS#Distance CombatRadius The cargo will be unloaded from the APC and engage the enemy if the enemy is within CombatRadius range. The radius is in meters, the default value is 500 meters. -- @return #AI_CARGO_DISPATCHER_APC -- @usage -- @@ -101,9 +101,9 @@ AI_CARGO_DISPATCHER_APC = { -- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() -- AICargoDispatcher = AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZone, 500 ) -- -function AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZones, CombatRadius ) +function AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZone, CombatRadius ) - local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZones ) ) -- #AI_CARGO_DISPATCHER_APC + local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone ) ) -- #AI_CARGO_DISPATCHER_APC self.CombatRadius = CombatRadius or 500 diff --git a/Moose Development/Moose/AI/AI_Patrol.lua b/Moose Development/Moose/AI/AI_Patrol.lua index c89ab36e0..1f70d0015 100644 --- a/Moose Development/Moose/AI/AI_Patrol.lua +++ b/Moose Development/Moose/AI/AI_Patrol.lua @@ -1,4 +1,10 @@ ---- **AI** -- (R2.1) - Manages the independent process of Air Patrol for airplanes. +--- **AI** -- Perform Air Patrolling for airplanes. +-- +-- **Features:** +-- +-- * Patrol AI airplanes within a given zone. +-- * Trigger detected events when enemy airplanes are detected. +-- * Manage a fuel treshold to RTB on time. -- -- === -- From 83fab80d881d64a3ea104ff587b4486600892d66 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sun, 3 Jun 2018 21:50:07 +0200 Subject: [PATCH 157/420] Updated documentation. --- Moose Development/Moose/Tasking/CommandCenter.lua | 11 +++++++++-- Moose Development/Moose/Tasking/Mission.lua | 10 +++++++++- .../Moose/Tasking/Task_A2A_Dispatcher.lua | 14 ++++++++++++-- .../Moose/Tasking/Task_A2G_Dispatcher.lua | 13 ++++++++++++- 4 files changed, 42 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index 131e8e4b9..e8f9204e4 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -1,5 +1,12 @@ ---- **Tasking** -- A COMMANDCENTER is the owner of multiple missions within MOOSE. --- A COMMANDCENTER governs multiple missions, the tasking and the reporting. +--- **Tasking** -- A command center governs multiple missions, and takes care of the reporting and communications. +-- +-- **Features:** +-- +-- * Govern multiple missions. +-- * Communicate to coalitions, groups. +-- * Assign tasks. +-- * Manage the menus. +-- * Manage reference zones. -- -- === -- diff --git a/Moose Development/Moose/Tasking/Mission.lua b/Moose Development/Moose/Tasking/Mission.lua index ced7f3d1a..c2f9f2de1 100644 --- a/Moose Development/Moose/Tasking/Mission.lua +++ b/Moose Development/Moose/Tasking/Mission.lua @@ -1,4 +1,12 @@ ---- **Tasking** -- A MISSION is the main owner of a Mission orchestration within MOOSE. +--- **Tasking** -- A mission models a goal to be achieved through the execution and completion of tasks by human players. +-- +-- **Features:** +-- +-- * A mission has a goal to be achieved, through the execution and completion of tasks of different categories by human players. +-- * A mission manages these tasks. +-- * A mission has a state, that indicates the fase of the mission. +-- * A mission has a menu structure, that facilitates mission reports and tasking menus. +-- * A mission can assign a task to a player. -- -- === -- diff --git a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua index ddbb7b148..8f85ef7d2 100644 --- a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua @@ -1,7 +1,17 @@ ---- **Tasking** - The TASK_A2A_DISPATCHER creates and manages player TASK_A2A tasks based on detected targets. +--- **Tasking** - Dynamically allocates A2A tasks to human players, based on detected airborne targets through an EWR network. -- --- Implement the dynamic dispatching of tasks upon groups of detected units determined a @{Set} of EWR installation groups. +-- **Features:** -- +-- * Dynamically assign tasks to human players based on detected targets. +-- * Dynamically change the tasks as the tactical situation evolves during the mission. +-- * Dynamically assign (CAP) Control Air Patrols tasks for human players to perform CAP. +-- * Dynamically assign (GCI) Ground Control Intercept tasks for human players to perform GCI. +-- * Dynamically assign Engage tasks for human players to engage on close-by airborne bogeys. +-- * Define and use an EWR (Early Warning Radar) network. +-- * Define different ranges to engage upon intruders. +-- * Keep task achievements. +-- * Score task achievements. +-- -- === -- -- ### Author: **FlightControl** diff --git a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua index d9dae1ab4..13cd2c5e7 100644 --- a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua @@ -1,5 +1,16 @@ ---- **Tasking** - The TASK\_A2G\_DISPATCHER dispatches A2G Tasks to Players based on enemy location detection. +--- **Tasking** -- Dynamically allocates A2G tasks to human players, based on detected ground targets through reconnaissance. -- +-- **Features:** +-- +-- * Dynamically assign tasks to human players based on detected targets. +-- * Dynamically change the tasks as the tactical situation evolves during the mission. +-- * Dynamically assign (CAS) Close Air Support tasks for human players. +-- * Dynamically assign (BAI) Battlefield Air Interdiction tasks for human players. +-- * Dynamically assign (SEAD) Supression of Enemy Air Defense tasks for human players to eliminate G2A missile threats. +-- * Define and use an EWR (Early Warning Radar) network. +-- * Define different ranges to engage upon intruders. +-- * Keep task achievements. +-- * Score task achievements.-- -- === -- -- ### Author: **FlightControl** From 2d43af7c0d98b8389c9e3e5cb38f051c1a4f4429 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sun, 3 Jun 2018 22:02:26 +0200 Subject: [PATCH 158/420] docu --- Moose Development/Moose/AI/AI_Balancer.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/AI/AI_Balancer.lua b/Moose Development/Moose/AI/AI_Balancer.lua index 607a22ecc..1b18bf79d 100644 --- a/Moose Development/Moose/AI/AI_Balancer.lua +++ b/Moose Development/Moose/AI/AI_Balancer.lua @@ -1,6 +1,6 @@ --- **AI** -- Balance player slots with AI to create an engaging simulation environment, independent of the amount of players. -- --- ** Features:** +-- **Features:** -- -- * Automatically spawn AI as a replacement of free player slots for a coalition. -- * Make the AI to perform tasks. From 0a34cfdafae50c17cb77807e36499339e961f566 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sun, 3 Jun 2018 23:38:15 +0200 Subject: [PATCH 159/420] ARTY v0.9.91 - Added automatic relocation if not in firing range. - Added first version of ARTY DB with artillery unit parameters min/max firing range. --- Moose Development/Moose/Core/Message.lua | 11 +- .../Moose/Functional/Artillery.lua | 269 ++++++++++++++++-- .../Moose/Functional/Suppression.lua | 2 +- 3 files changed, 264 insertions(+), 18 deletions(-) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 4ca1d4bc3..b50f52ddb 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -66,7 +66,7 @@ MESSAGE.Type = { -- @param #string MessageText is the text of the Message. -- @param #number MessageDuration is a number in seconds of how long the MESSAGE should be shown on the display panel. -- @param #string MessageCategory (optional) is a string expressing the "category" of the Message. The category will be shown as the first text in the message followed by a ": ". --- @param #boolean ClearScreen (optional) Clear all previous messages. +-- @param #boolean ClearScreen (optional) Clear all previous messages if true. -- @return #MESSAGE -- @usage -- -- Create a series of new Messages. @@ -147,6 +147,15 @@ end +--- Clears all previous messages from the screen before the new message is displayed. Not that this must come before all functions starting with ToX(), e.g. ToAll(), ToGroup() etc. +-- @param #MESSAGE self +-- @return #MESSAGE +function MESSAGE:Clear() + self:F() + self.ClearScreen=true + return self +end + --- Sends a MESSAGE to a Client Group. Note that the Group needs to be defined within the ME with the skillset "Client" or "Player". diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index ba42d1c14..70a513e2e 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -88,6 +88,9 @@ -- @field #boolean markallow If true, Players are allowed to assign targets and moves for ARTY group by placing markers on the F10 map. Default is false. -- @field #number markkey Authorization key. Only player who know this key can assign targets and moves via markers on the F10 map. Default no authorization required. -- @field #boolean markreadonly Marks for targets are readonly and cannot be removed by players. Default is false. +-- @field #boolean autorelocate ARTY group will automatically move to within the max/min firing range. +-- @field #number autorelocatemaxdist Max distance [m] the ARTY group will travel to get within firing range. Default 50000 m = 50 km. +-- @field #boolean autorelocateonroad ARTY group will use mainly road to automatically get within firing range. Default is false. -- @extends Core.Fsm#FSM_CONTROLLABLE --- Enables mission designers easily to assign targets for artillery units. Since the implementation is based on a Finite State Model (FSM), the mission designer can @@ -218,6 +221,7 @@ -- * @{#ARTY.WeaponType}.UnguidedAny: Any unguided weapon (cannons or rockes) will be used. -- * @{#ARTY.WeaponType}.GuidedMissile: Any guided missiles are used during the attack. Corresponding ammo type are missiles and can be defined by @{#ARTY.SetMissileTypes}. -- * @{#ARTY.WeaponType}.CruiseMissile: Only cruise missiles are used during the attack. Corresponding ammo type are missiles and can be defined by @{#ARTY.SetMissileTypes}. +-- * @{#ARTY.WeaponType}.TacticalNukes: Use tactical nuclear shells. This works only with units that have shells and is described below. -- -- ## Assigning Moves -- The ARTY group can be commanded to move. This is done by the @{#ARTY.AssignMoveCoord}(*coord*,*time*,*speed*,*onroad*,*cancel*,*name*) function. @@ -258,13 +262,15 @@ -- -- ## Tactical Nukes -- --- ARTY groups that can fire shells can also be used to fire tactical nukes. This is simply achieved by setting the weapon type to **ARTY.WeaponType.TacticalNukes** in the +-- ARTY groups that can fire shells can also be used to fire tactical nukes. This is achieved by setting the weapon type to **ARTY.WeaponType.TacticalNukes** in the -- @{#ARTY.AssignTargetCoord}() function. +-- +-- By default, they group does not have any nukes available. To give the group the ability the function @{#ARTY.SetTacNukeShells}(*n*) can be used. +-- This supplies the group with *n* nuclear shells, where *n* is restricted to the number of conventional shells the group can carry. +-- Note that the group must always have convenctional shells left in order to fire a nuclear shell. -- -- The default explostion strength is 0.075 kilo tons TNT. The can be changed with the @{#ARTY.SetTacNukeWarhead}(*strength*), where *strength* is given in kilo tons TNT. --- --- By default, all available conventional shells can be used as nuclear shells. However, it is possible to restrict the number with the @{#ARTY.SetTacNukeShells}(*n*) function --- to only have *n* nuclear shells available. Note that the group must always have convenctional shells left in order to fire a nuclear shell. +-- -- -- ## Assignments via Markers on F10 Map -- @@ -438,6 +444,9 @@ ARTY={ markallow=false, markkey=nil, markreadonly=false, + autorelocate=false, + autorelocatemaxdist=50000, + autorelocateonroad=false, } --- Weapong type ID. http://wiki.hoggit.us/view/DCS_enum_weapon_flag @@ -453,13 +462,54 @@ ARTY.WeaponType={ TacticalNukes=666, } +--- Database of common artillery unit properties. +-- @list db +ARTY.db={ + ["2B11 mortar"] = { + minrange = 500, + maxrange = 7000, + }, + ["SAU 2-C9"] = { + minrange = 500, + maxrange = 7000, + }, + ["SPH M109 Paladin"] = { + minrange = 300, + maxrange = 22000, + }, + ["SAU Gvozdika"] = { + minrange = 300, + maxrange = 15000, + }, + ["SAU Akatsia"] = { + minrange = 300, + maxrange = 17000, + }, + ["SAU Msta"] = { + minrange = 300, + maxrange = 23500, + }, + ["MLRS M270"] = { + minrange = 10000, + maxrange = 32000, + }, + ["Grad-URAL"] = { + minrange = 5000, + maxrange = 19000, + }, + ["Smerch"] = { + minrange = 20000, + maxrange = 70000, + } +} + --- Some ID to identify who we are in output of the DCS.log file. -- @field #string id ARTY.id="ARTY | " --- Arty script version. -- @field #string version -ARTY.version="0.9.9" +ARTY.version="0.9.91" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -826,6 +876,21 @@ function ARTY:SetRearmingPlace(coord) self.RearmingPlaceCoord=coord end +--- Set automatic relocation of ARTY group if a target is assigned which is out of range. The unit will drive automatically towards or away from the target to be in max/min firing range. +-- @param #ARTY self +-- @param #number maxdistance (Optional) The maximum distance in km the group will travel to get within firing range. Default is 50 km. No automatic relocation is performed if targets are assigned which are further away. +-- @param #boolean onroad (Optional) If true, ARTY group uses roads whenever possible. Default false, i.e. group will move in a straight line to the assigned coordinate. +function ARTY:SetAutomaticRelocate(maxdistance, onroad) + self:F({distance=maxdistance, onroad=onroad}) + self.autorelocate=true + self.autorelocatemaxdist=maxdistance or 50 + self.autorelocatemaxdist=self.autorelocatemaxdist*1000 + if onroad==nil then + onroad=false + end + self.autorelocateonroad=onroad +end + --- Report messages of ARTY group turned on. This is the default. -- @param #ARTY self function ARTY:SetReportON() @@ -1045,6 +1110,18 @@ function ARTY:onafterStart(Controllable, From, Event, To) self.Nukes0=0 end + -- Check if we have and arty type that is in the DB. + local _dbproperties=self:_CheckDB(self.DisplayName) + self:T({dbproperties=_dbproperties}) + if _dbproperties~=nil then + for property,value in pairs(_dbproperties) do + self:T({property=property, value=value}) + --if self[property]==nil then + self[property]=value + --end + end + end + local text=string.format("\n******************************************************\n") text=text..string.format("Arty group = %s\n", Controllable:GetName()) text=text..string.format("Artillery attribute = %s\n", tostring(self.IsArtillery)) @@ -1079,6 +1156,9 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("Relocate after fire = %s\n", tostring(self.relocateafterfire)) text=text..string.format("Relocate min dist. = %d m\n", self.relocateRmin) text=text..string.format("Relocate max dist. = %d m\n", self.relocateRmax) + text=text..string.format("Auto move in range = %s\n", tostring(self.autorelocate)) + text=text..string.format("Auto move dist. max = %.1f km\n", self.autorelocatemaxdist/1000) + text=text..string.format("Auto move on road = %s\n", tostring(self.autorelocateonroad)) text=text..string.format("Marker assignments = %s\n", tostring(self.markallow)) text=text..string.format("Marker auth. key = %s\n", tostring(self.markkey)) text=text..string.format("Marker readonly = %s\n", tostring(self.markreadonly)) @@ -1129,6 +1209,20 @@ function ARTY:onafterStart(Controllable, From, Event, To) self:__Status(self.StatusInterval) end +--- Check the DB for properties of the specified artillery unit type. +-- @param #ARTY self +-- @return #table Properties of the requested artillery type. Returns nil if no matching DB entry could be found. +function ARTY:_CheckDB(displayname) + for _type,_properties in pairs(ARTY.db) do + self:T({type=_type, properties=_properties}) + if _type==displayname then + self:T({type=_type, properties=_properties}) + return _properties + end + end + return nil +end + --- After "Start" event. Initialized ROE and alarm state. Starts the event handler. -- @param #ARTY self function ARTY:_StatusReport() @@ -1209,7 +1303,7 @@ function ARTY:_OnEventShot(EventData) -- Debug output. local text=string.format("%s, fired shot %d of %d with weapon %s on target %s.", self.Controllable:GetName(), self.Nshots, self.currentTarget.nshells, _weaponName, self.currentTarget.name) self:T(ARTY.id..text) - MESSAGE:New(text, 5):ToAllIf(self.report or self.Debug) + MESSAGE:New(text, 5):Clear():ToAllIf(self.report or self.Debug) -- Last known position of the weapon fired. local _lastpos={x=0, y=0, z=0} @@ -1484,7 +1578,7 @@ function ARTY:_OnEventMarkChange(Event) end end end - + -- We were not addressed. if not _assigned then return @@ -1696,9 +1790,12 @@ function ARTY:onafterStatus(Controllable, From, Event, To) -- Check that firing started after ~5 min. If not, target is removed. self:_CheckShootingStarted() end + + + -- Check if targets are in range and update target.inrange value. + self:_CheckTargetsInRange() - - -- Get a valid timed target if it is due to be attacked. + -- Get a valid timed target if it is due to be attacked. local _timedTarget=self:_CheckTimedTargets() -- Get a valid normal target (one that is not timed). @@ -2239,6 +2336,9 @@ end function ARTY:onafterNewTarget(Controllable, From, Event, To, target) self:_EventFromTo("onafterNewTarget", Event, From, To) + -- Check if target is in range. + --local _inrange, _toofar, _tooclose=self:_TargetInRange(target, self.report or self.Debug) + -- Debug message. local text=string.format("Adding new target %s.", target.name) MESSAGE:New(text, 5):ToAllIf(self.Debug) @@ -2831,6 +2931,7 @@ function ARTY:_Markertext(text) assignment.engage=false assignment.request=false assignment.readonly=false + assignment.canceltarget=false assignment.cancelcurrent=false --assignment.time=nil --assignment.nshells=nil @@ -2881,10 +2982,10 @@ function ARTY:_Markertext(text) end self:T2(ARTY.id..string.format("Key Time=%s.", val)) - elseif key:lower():find("shots") then + elseif key:lower():find("shot") then assignment.nshells=tonumber(s[2]) - self:T(ARTY.id..string.format("Key Shots=%s.", val)) + self:T(ARTY.id..string.format("Key Shot=%s.", val)) elseif key:lower():find("prio") then @@ -2927,12 +3028,20 @@ function ARTY:_Markertext(text) self:T2(ARTY.id..string.format("Key Onroad=true.")) elseif key:lower():find("irrevocable") or key:lower():find("readonly") then + assignment.readonly=true self:T2(ARTY.id..string.format("Key Readonly=true.")) + + elseif key:lower():find("canceltarget") then + + assignment.canceltarget=true + self:T2(ARTY.id..string.format("Key Cancel Target (before move)=true.")) - elseif key:lower():find("cancel current") then + elseif key:lower():find("cancelcurrent") then + assignment.cancelcurrent=true self:T2(ARTY.id..string.format("Key Cancel Current=true.")) + elseif assignment.request and key:lower():find("ammo") then assignment.requestammo=true end @@ -3059,6 +3168,122 @@ function ARTY:_SortQueueTime(queue) end +--- Heading from point a to point b in degrees. +--@param #ARTY self +--@param Core.Point#COORDINATE a Coordinate. +--@param Core.Point#COORDINATE b Coordinate. +--@return #number angle Angle from a to b in degrees. +function ARTY:_GetHeading(a, b) + local dx = b.x-a.x + local dy = b.z-a.z + local angle = math.deg(math.atan2(dy,dx)) + if angle < 0 then + angle = 360 + angle + end + return angle +end + +--- Check all targets whether they are in range. +-- @param #ARTY self +function ARTY:_CheckTargetsInRange() + + for i=1,#self.targets do + local _target=self.targets[i] + + self:T(ARTY.id..string.format("Before: Target %s - in range = %s", _target.name, tostring(_target.inrange))) + + -- Check if target is in range. + local _inrange,_toofar,_tooclose=self:_TargetInRange(_target) + self:T(ARTY.id..string.format("Inbetw: Target %s - in range = %s, toofar = %s, tooclose = %s", _target.name, tostring(_target.inrange), tostring(_toofar), tostring(_tooclose))) + + -- Init default for assigning moves into range. + local _movetowards=false + local _moveaway=false + + if _target.inrange==nil then + + -- First time the check is performed. We call the function again and send a message. + _target.inrange,_toofar,_tooclose=self:_TargetInRange(_target, self.report or self.Debug) + + -- Send group towards/away from target. + if _toofar then + _movetowards=true + elseif _tooclose then + _moveaway=true + end + + elseif _target.inrange==true then + + -- Target was in range at previous check... + + if _toofar then --...but is now too far away. + _movetowards=true + elseif _tooclose then --...but is now too close. + _moveaway=true + end + + elseif _target.inrange==false then + + -- Target was out of range at previous check. + + if _inrange then + -- Inform coalition that target is now in range. + local text=string.format("%s, target %s is now in range.", self.Controllable:GetName(), _target.name) + self:T(ARTY.id..text) + MESSAGE:New(text,10):ToCoalitionIf(self.Controllable:GetCoalition(), self.report or self.Debug) + end + + end + + -- Assign a relocation command so that the unit will be in range of the requested target. + if self.autorelocate and (_movetowards or _moveaway) then + + -- Get current position. + local _from=self.Controllable:GetCoordinate() + local _dist=_from:Get2DDistance(_target.coord) + + if _dist<=self.autorelocatemaxdist then + + local _tocoord --Core.Point#COORDINATE + local _name="" + local _safetymargin=500 + + if _movetowards then + + -- Target was in range on previous check but now we are too far away. + local _waytogo=_dist-self.maxrange+_safetymargin + local _heading=self:_GetHeading(_from,_target.coord) + _tocoord=_from:Translate(_waytogo, _heading) + _name=string.format("Relocation to within max firing range of target %s", _target.name) + + elseif _moveaway then + + -- Target was in range on previous check but now we are too far away. + local _waytogo=_dist-self.minrange+_safetymargin + local _heading=self:_GetHeading(_target.coord,_from) + _tocoord=_from:Translate(_waytogo, _heading) + _name=string.format("Relocation to within min firing range of target %s", _target.name) + + end + + -- Send info message. + MESSAGE:New(_name.." assigned.", 10):ToCoalitionIf(self.Controllable:GetCoalition(), self.report or self.Debug) + + -- Assign relocation move. + self:AssignMoveCoord(_tocoord, nil, nil, self.autorelocateonroad, false, _name, true) + + end + + end + + -- Update value. + _target.inrange=_inrange + + self:T(ARTY.id..string.format("After: Target %s - in range = %s", _target.name, tostring(_target.inrange))) + + end +end + --- Check all timed targets and return the target which should be attacked next. -- @param #ARTY self -- @return #table Target which is due to be attacked now. @@ -3303,37 +3528,49 @@ end --- Check if target is in range. -- @param #ARTY self -- @param #table target Target table. +-- @param #boolean message (Optional) If true, send a message to the coalition if the target is not in range. Default is no message is send. -- @return #boolean True if target is in range, false otherwise. -function ARTY:_TargetInRange(target) +-- @return #boolean True if ARTY group is too far away from the target, i.e. distance > max firing range. +-- @return #boolean True if ARTY group is too close to the target, i.e. distance < min finring range. +function ARTY:_TargetInRange(target, message) self:F3(target) + + -- Default is no message. + if message==nil then + message=false + end -- Distance between ARTY group and target. local _dist=self.Controllable:GetCoordinate():Get2DDistance(target.coord) -- Assume we are in range. local _inrange=true + local _tooclose=false + local _toofar=false local text="" if _dist < self.minrange then _inrange=false + _tooclose=true text=string.format("%s, target is out of range. Distance of %.1f km is below min range of %.1f km.", self.Controllable:GetName(), _dist/1000, self.minrange/1000) elseif _dist > self.maxrange then _inrange=false + _toofar=true text=string.format("%s, target is out of range. Distance of %.1f km is greater than max range of %.1f km.", self.Controllable:GetName(), _dist/1000, self.maxrange/1000) end -- Debug output. if not _inrange then self:T(ARTY.id..text) - MESSAGE:New(text, 5):ToCoalitionIf(self.Controllable:GetCoalition(), self.report or self.Debug) + MESSAGE:New(text, 5):ToCoalitionIf(self.Controllable:GetCoalition(), (self.report and message) or (self.Debug and message)) end - -- Remove target if ARTY group cannot move. No change to be ever in range. + -- Remove target if ARTY group cannot move, e.g. Mortas. No chance to be ever in range. if self.SpeedMax<1 and _inrange==false then self:RemoveTarget(target.name) end - return _inrange + return _inrange,_toofar,_tooclose end --- Get the weapon type name, which should be used to attack the target. diff --git a/Moose Development/Moose/Functional/Suppression.lua b/Moose Development/Moose/Functional/Suppression.lua index c0d2278ef..805008e45 100644 --- a/Moose Development/Moose/Functional/Suppression.lua +++ b/Moose Development/Moose/Functional/Suppression.lua @@ -1725,7 +1725,7 @@ end --@param Core.Point#COORDINATE a Coordinate. --@param Core.Point#COORDINATE b Coordinate. --@return #number angle Angle from a to b in degrees. -function SUPPRESSION:_Heading(a, b, distance) +function SUPPRESSION:_Heading(a, b) local dx = b.x-a.x local dy = b.z-a.z local angle = math.deg(math.atan2(dy,dx)) From 8b667071b7a76e34ac11f2c5bfe7f18625af543a Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Tue, 5 Jun 2018 00:28:52 +0200 Subject: [PATCH 160/420] ARTY v0.9.92 - adjusted DB - other minor fixes and improvements --- .../Moose/Functional/Artillery.lua | 138 ++++++++++++------ .../Moose/Wrapper/Controllable.lua | 6 +- 2 files changed, 98 insertions(+), 46 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 70a513e2e..5bec76c49 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -59,7 +59,7 @@ -- @field #string Type Type of the ARTY group. -- @field #string DisplayName Extended type name of the ARTY group. -- @field #number IniGroupStrength Inital number of units in the ARTY group. --- @field #boolean IsArtillery If true, ARTY group has attribute "Artillery". +-- @field #boolean IsArtillery If true, ARTY group has attribute "Artillery". This is automatically derived from the DCS descriptor table. -- @field #number SpeedMax Maximum speed of ARTY group in km/h. This is determined from the DCS descriptor table. -- @field #number Speed Default speed in km/h the ARTY group moves at. Maximum speed possible is 80% of maximum speed the group can do. -- @field #number RearmingDistance Safe distance in meters between ARTY group and rearming group or place at which rearming is possible. Default 100 m. @@ -286,11 +286,12 @@ -- * *time* Time for which which the engagement is schedules, e.g. 08:42. Default is as soon as possible. -- * *prio* Priority of the engagement as number between 1 (high prio) and 100 (low prio). Default is 50. -- * *shots* Number of shots (shells, rockets or missiles) fired at each engagement. Default is 5. --- * *engage* Number of times the target is engaged. Default is 1. +-- * *maxengage* Number of times the target is engaged. Default is 1. -- * *radius* Scattering radius of the fired shots in meters. Default is 100 m. -- * *weapon* Type of weapon to be used. Valid parameters are *cannon*, *rocket*, *missile*, *nuke*. Default is automatic selection. -- * *battery* Name of the ARTY group that the target is assigned to. Note that the name is case sensitive and has to be given in quotation marks. Default is all ARTY groups of the right coalition. -- * *key* A number to authorize the target assignment. Only specifing the correct number will trigger an engagement. +-- * *readonly* Marker cannot be deleted by users any more. Hence, assignment cannot be cancelled by removing the marker. -- -- Here are examples of valid marker texts: -- arty engage! @@ -307,9 +308,11 @@ -- -- * *time* Time for which which the relocation/move is schedules, e.g. 08:42. Default is as soon as possible. -- * *speed* The speed in km/h the group will drive at. Default is 70% of its max possible speed. --- * *onroad* Group will use mainly roads. Default is off, i.e. it will go in a straight line from its current position to the assigned coordinate. --- * *cancel* Group will cancel all running firing engagements and immidiately start to move. Default is that group will wait until is current assignment is over. +-- * *on road* Group will use mainly roads. Default is off, i.e. it will go in a straight line from its current position to the assigned coordinate. +-- * *canceltarget* Group will cancel all running firing engagements and immidiately start to move. Default is that group will wait until is current assignment is over. -- * *battery* Name of the ARTY group that the relocation is assigned to. +-- * *key* A number to authorize the target assignment. Only specifing the correct number will trigger an engagement. +-- * *readonly* Marker cannot be deleted by users any more. Hence, assignment cannot be cancelled by removing the marker. -- -- Here are some examples: -- arty move! time 23:45, speed 50, onroad, cancel @@ -431,7 +434,7 @@ ARTY={ ammorockets={}, ammomissiles={}, Nshots=0, - minrange=100, + minrange=300, maxrange=1000000, nukewarhead=75000, Nukes=nil, @@ -465,42 +468,61 @@ ARTY.WeaponType={ --- Database of common artillery unit properties. -- @list db ARTY.db={ - ["2B11 mortar"] = { - minrange = 500, - maxrange = 7000, + ["2B11 mortar"] = { -- type "2B11 mortar" + minrange = 500, -- correct? + maxrange = 7000, -- 7 km + reloadtime = 30, -- 30 sec }, - ["SAU 2-C9"] = { - minrange = 500, - maxrange = 7000, + ["SPH 2S1 Gvozdika"] = { -- type "SAU Gvozdika" + minrange = 300, -- correct? + maxrange = 15000, -- 15 km + reloadtime = nil, -- unknown }, - ["SPH M109 Paladin"] = { - minrange = 300, - maxrange = 22000, + ["SPH 2S19 Msta"] = { --type "SAU Msta", alias "2S19 Msta" + minrange = 300, -- correct? + maxrange = 23500, -- 23.5 km + reloadtime = nil, -- unknown }, - ["SAU Gvozdika"] = { - minrange = 300, - maxrange = 15000, + ["SPH 2S3 Akatsia"] = { -- type "SAU Akatsia", alias "2S3 Akatsia" + minrange = 300, -- correct? + maxrange = 17000, -- 17 km + reloadtime = nil, -- unknown }, - ["SAU Akatsia"] = { - minrange = 300, - maxrange = 17000, + ["SPH 2S9 Nona"] = { --type "SAU 2-C9" + minrange = 500, -- correct? + maxrange = 7000, -- 7 km + reloadtime = nil, -- unknown }, - ["SAU Msta"] = { - minrange = 300, - maxrange = 23500, + ["SPH M109 Paladin"] = { -- type "M-109", alias "M109" + minrange = 300, -- correct? + maxrange = 22000, -- 22 km + reloadtime = nil, -- unknown }, - ["MLRS M270"] = { - minrange = 10000, - maxrange = 32000, + ["SpGH Dana"] = { -- type "SpGH_Dana" + minrange = 300, -- correct? + maxrange = 18700, -- 18.7 km + reloadtime = nil, -- unknown }, - ["Grad-URAL"] = { - minrange = 5000, - maxrange = 19000, + ["MLRS BM-21 Grad"] = { --type "Grad-URAL", alias "MLRS BM-21 Grad" + minrange = 5000, -- 5 km + maxrange = 19000, -- 19 km + reloadtime = 420, -- 7 min + }, + ["MLRS 9K57 Uragan BM-27"] = { -- type "Uragan_BM-27" + minrange = 11500, -- 11.5 km + maxrange = 35800, -- 35.8 km + reloadtime = 840, -- 14 min + }, + ["MLRS 9A52 Smerch"] = { -- type "Smerch" + minrange = 20000, -- 20 km + maxrange = 70000, -- 70 km + reloadtime = 2160, -- 36 min + }, + ["MLRS M270"] = { --type "MRLS", alias "M270 MRLS" + minrange = 10000, -- 10 km + maxrange = 32000, -- 32 km + reloadtime = 540, -- 9 min }, - ["Smerch"] = { - minrange = 20000, - maxrange = 70000, - } } --- Some ID to identify who we are in output of the DCS.log file. @@ -509,7 +531,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #string version -ARTY.version="0.9.91" +ARTY.version="0.9.92" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -759,8 +781,9 @@ function ARTY:AssignMoveCoord(coord, time, speed, onroad, cancel, name, unique) -- Get max speed of group. local speedmax=self.Controllable:GetSpeedMax() - -- Default speed is 70% of max speed. + -- Set speed. if speed then + -- Make sure, given speed is less than max phycially possible speed of group. speed=math.min(speed, speedmax) elseif self.Speed then speed=self.Speed @@ -1640,7 +1663,7 @@ function ARTY:_OnEventMarkChange(Event) MESSAGE:New(text, 10):ToCoalitionIf(batterycoalition, self.report or self.Debug) -- Assign a relocation of the arty group. - local _movename=self:AssignMoveCoord(_coord, _assign.time, _assign.speed, _assign.onroad, _assign.cancel,_name, true) + local _movename=self:AssignMoveCoord(_coord, _assign.time, _assign.speed, _assign.onroad, _assign.canceltarget,_name, true) if _movename~=nil then local _mid=self:_GetMoveIndexByName(_movename) @@ -2566,7 +2589,7 @@ end -- @param #ARTY self -- @param Wrapper.Group#GROUP group Group to route. -- @param Core.Point#COORDINATE ToCoord Coordinate where we want to go. --- @param #number Speed Speed in km/h. +-- @param #number Speed (Optional) Speed in km/h. Default is 70% of max speed the group can do. -- @param #boolean OnRoad If true, use (mainly) roads. function ARTY:_Move(group, ToCoord, Speed, OnRoad) @@ -2578,8 +2601,14 @@ function ARTY:_Move(group, ToCoord, Speed, OnRoad) -- Set formation. local formation = "Off Road" - -- Default speed is 30 km/h. - Speed=Speed or 30 + -- Get max speed of group. + local SpeedMax=group:GetSpeedMax() + + -- Set speed. + Speed=Speed or SpeedMax*0.7 + + -- Make sure, we do not go above max speed possible. + Speed=math.min(Speed, SpeedMax) -- Current coordinates of group. local cpini=group:GetCoordinate() @@ -2757,11 +2786,30 @@ function ARTY:GetAmmo(display) -- Get the weapon category: shell=0, missile=1, rocket=2, bomb=3 local Category=ammotable[w].desc.category + -- Get missile category: Weapon.MissileCategory AAM=1, SAM=2, BM=3, ANTI_SHIP=4, CRUISE=5, OTHER=6 local MissileCategory=nil if Category==Weapon.Category.MISSILE then MissileCategory=ammotable[w].desc.missileCategory end + local function missilecat(n) + local cat="unknown" + if n==1 then + cat="air-to-air" + elseif n==2 then + cat="surface-to-air" + elseif n==3 then + cat="ballistic" + elseif n==4 then + cat="anti-ship" + elseif n==5 then + cat="cruise" + elseif n==6 then + cat="other" + end + return cat + end + -- Check for correct shell type. local _gotshell=false if #self.ammoshells>0 then @@ -2812,7 +2860,7 @@ function ARTY:GetAmmo(display) nshells=nshells+Nammo -- Debug info. - text=text..string.format("- %d shells of type %s (category=%d, missile category=%s)\n", Nammo, Tammo, Category, tostring(MissileCategory)) + text=text..string.format("- %d shells of type %s\n", Nammo, Tammo) elseif _gotrocket then @@ -2820,15 +2868,17 @@ function ARTY:GetAmmo(display) nrockets=nrockets+Nammo -- Debug info. - text=text..string.format("- %d rockets of type %s (category=%d, missile category=%s)\n", Nammo, Tammo, Category, tostring(MissileCategory)) + text=text..string.format("- %d rockets of type %s\n", Nammo, Tammo) elseif _gotmissile then - -- Add up all rockets. - nmissiles=nmissiles+Nammo + -- Add up all cruise missiles (category 5) + if MissileCategory==5 then + nmissiles=nmissiles+Nammo + end -- Debug info. - text=text..string.format("- %d missiles of type %s (category=%d, missile category=%s)\n", Nammo, Tammo, Category, tostring(MissileCategory)) + text=text..string.format("- %d %s missiles of type %s\n", Nammo, missilecat(MissileCategory), Tammo) else diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 8e52ecb58..d539dd098 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -2651,8 +2651,10 @@ function CONTROLLABLE:OptionAlarmStateGreen() if self:IsGround() then Controller:setOption( AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.GREEN ) - elseif self:IsShip() then - Controller:setOption( AI.Option.Naval.id.ALARM_STATE, AI.Option.Naval.val.ALARM_STATE.GREEN ) + elseif self:IsShip() then + -- AI.Option.Naval.id.ALARM_STATE does not seem to exist! + --Controller:setOption( AI.Option.Naval.id.ALARM_STATE, AI.Option.Naval.val.ALARM_STATE.GREEN ) + Controller:setOption( AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.GREEN ) end return self From a2790f500c9c6f32a5909491d59aec202e8793b5 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 6 Jun 2018 00:51:59 +0200 Subject: [PATCH 161/420] ARTY v0.9.93 added new mark parameters LLDMS coordinate assignment --- Moose Development/Moose/Core/Point.lua | 22 + .../Moose/Functional/Artillery.lua | 388 ++++++++++++------ 2 files changed, 283 insertions(+), 127 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index bf20bc91b..543bce752 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -251,6 +251,28 @@ do -- COORDINATE return { x = self.x, y = self.z } end + --- Returns the coordinate from the latitude and longitude given in decimal degrees. + -- @param #COORDINATE self + -- @param #number latitude Latitude in decimal degrees. + -- @param #number longitude Longitude in decimal degrees. + -- @param #number altitude (Optional) Altitude in meters. Default is the land height at the coordinate. + -- @return #COORDINATE + function COORDINATE:NewFromLLDD( latitude, longitude, altitude) + + -- Returns a point from latitude and longitude in the vec3 format. + local vec3=coord.LLtoLO(latitude, longitude) + + -- Convert vec3 to coordinate object. + local _coord=self:NewFromVec3(vec3) + + -- Adjust height + if altitude==nil then + _coord.y=altitude + end + + return _coord + end + --- Returns if the 2 coordinates are at the same 2D position. -- @param #COORDINATE self diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 5bec76c49..48d2ab37c 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -531,7 +531,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #string version -ARTY.version="0.9.92" +ARTY.version="0.9.93" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1139,9 +1139,7 @@ function ARTY:onafterStart(Controllable, From, Event, To) if _dbproperties~=nil then for property,value in pairs(_dbproperties) do self:T({property=property, value=value}) - --if self[property]==nil then self[property]=value - --end end end @@ -1475,14 +1473,13 @@ function ARTY:onEvent(Event) local batteryname=self.Controllable:GetName() local batterycoalition=self.Controllable:GetCoalition() - self:T(string.format("Event captured = %s", tostring(batteryname))) - self:T(string.format("Event id = %s", tostring(Event.id))) - self:T(string.format("Event time = %s", tostring(Event.time))) - self:T(string.format("Event idx = %s", tostring(Event.idx))) - self:T(string.format("Event coalition = %s", tostring(Event.coalition))) - self:T(string.format("Event group id = %s", tostring(Event.groupID))) - self:T(string.format("Event text = %s", tostring(Event.text))) - self:E({eventid=Event.id, vec3=Event.pos}) + self:T2(string.format("Event captured = %s", tostring(batteryname))) + self:T2(string.format("Event id = %s", tostring(Event.id))) + self:T2(string.format("Event time = %s", tostring(Event.time))) + self:T2(string.format("Event idx = %s", tostring(Event.idx))) + self:T2(string.format("Event coalition = %s", tostring(Event.coalition))) + self:T2(string.format("Event group id = %s", tostring(Event.groupID))) + self:T2(string.format("Event text = %s", tostring(Event.text))) if Event.initiator~=nil then local _unitname=Event.initiator:getName() self:T(string.format("Event ini unit name = %s", tostring(_unitname))) @@ -1589,6 +1586,11 @@ function ARTY:_OnEventMarkChange(Event) -- Evaluate marker text and extract parameters. local _assign=self:_Markertext(Event.text) + + -- Check if ENGAGE or MOVE or REQUEST keywords were found. + if _assign==nil or not (_assign.engage or _assign.move or _assign.request) then + return + end -- Check if job is assigned to this ARTY group. Default is for all ARTY groups. local _assigned=true @@ -1606,11 +1608,6 @@ function ARTY:_OnEventMarkChange(Event) if not _assigned then return end - - -- Check if ENGAGE or MOVE or REQUEST keywords were found. - if not (_assign.engage or _assign.move or _assign.request) then - return - end -- Check if the authorization key is required and if it is valid. local _validkey=self:_MarkerKeyAuthentification(Event.text) @@ -1620,7 +1617,16 @@ function ARTY:_OnEventMarkChange(Event) if _assign.requestammo then self:_MarkRequestAmmo() end - -- Done! + if _assign.requestmoves then + self:_MarkRequestMoves() + end + if _assign.requesttargets then + self:_MarkRequestTargets() + end + if _assign.requestrearming then + self:Rearm() + end + -- Requests Done ==> End of story! return end @@ -1649,6 +1655,11 @@ function ARTY:_OnEventMarkChange(Event) -- Also I don't know who can see the mark which was created. _coord:RemoveMark(Event.idx) + -- Coordinate was given in text, e.g. as lat, long. + if _assign.coord then + _coord=_assign.coord + end + -- Anticipate marker ID. -- WARNING: Make sure, no marks are set until the COORDINATE:MarkToCoalition() is called or the target/move name will be wrong and target cannot be removed by deleting its marker. local _id=UTILS._MarkID+1 @@ -2594,7 +2605,7 @@ end function ARTY:_Move(group, ToCoord, Speed, OnRoad) -- Clear all tasks. - group:ClearTasks() + --group:ClearTasks() group:OptionAlarmStateGreen() group:OptionROEHoldFire() @@ -2983,122 +2994,147 @@ function ARTY:_Markertext(text) assignment.readonly=false assignment.canceltarget=false assignment.cancelcurrent=false - --assignment.time=nil - --assignment.nshells=nil - --assignment.prio=nil - --assignment.maxengage=nil - --assignment.radius=nil - --assignment.weapontype=nil - --assignment.speed=nil - --assignment.onroad=nil - --assignment.key=nil - if text:lower():find("arty") then - if text:lower():find("engage") then - assignment.engage=true - elseif text:lower():find("move") then - assignment.move=true - elseif text:lower():find("request") then - assignment.request=true - else - self:E(ARTY.id.."ERROR: Neither ENGAGE nor MOVE keyword specified!") - return - end + -- Check for correct keywords. + if text:lower():find("arty engage") or text:lower():find("arty attack") then + assignment.engage=true + elseif text:lower():find("arty move") or text:lower():find("arty relocate") then + assignment.move=true + elseif text:lower():find("arty request") then + assignment.request=true + else + self:E(ARTY.id..'ERROR: Neither "ARTY ENGAGE" nor "ARTY MOVE" nor "ARTY RELOCATE" nor "ARTY REQUEST" keyword specified!') + return nil + end - -- keywords are split by "," - local keywords=self:_split(text, ",") + -- keywords are split by "," + local keywords=self:_split(text, ",") + + for _,key in pairs(keywords) do - for _,key in pairs(keywords) do + local s=self:_split(key, " ") + local val=s[2] + + -- Battery name, i.e. which ARTY group should fire. + if key:lower():find("battery") then + + local v=self:_split(key, '"') + + for i=2,#v,2 do + table.insert(assignment.battery, v[i]) + self:T2(ARTY.id..string.format("Key Battery=%s.", v[i])) + end + + elseif key:lower():find("time") then - local s=self:_split(key, " ") - local val=s[2] + if val:lower():find("now") then + assignment.time=self:_SecondsToClock(timer.getTime0()+2) + else + assignment.time=val + end + self:T2(ARTY.id..string.format("Key Time=%s.", val)) + + elseif key:lower():find("shot") then - -- Battery name, i.e. which ARTY group should fire. - if key:lower():find("battery") then + assignment.nshells=tonumber(s[2]) + self:T(ARTY.id..string.format("Key Shot=%s.", val)) + + elseif key:lower():find("prio") then + + assignment.prio=tonumber(val) + self:T2(string.format("Key Prio=%s.", val)) + + elseif key:lower():find("maxengage") then + + assignment.maxengage=tonumber(val) + self:T2(ARTY.id..string.format("Key Maxengage=%s.", val)) + + elseif key:lower():find("radius") then + + assignment.radius=tonumber(val) + self:T2(ARTY.id..string.format("Key Radius=%s.", val)) + + elseif key:lower():find("weapon") then + + if val:lower():find("cannon") then + assignment.weapontype=ARTY.WeaponType.Cannon + elseif val:lower():find("rocket") then + assignment.weapontype=ARTY.WeaponType.Rockets + elseif val:lower():find("missile") then + assignment.weapontype=ARTY.WeaponType.GuidedMissile + elseif val:lower():find("nuke") then + assignment.weapontype=ARTY.WeaponType.TacticalNukes + else + assignment.weapontype=ARTY.WeaponType.Auto + end + self:T2(ARTY.id..string.format("Key Weapon=%s.", val)) + + elseif key:lower():find("speed") then + + assignment.speed=tonumber(val) + self:T2(ARTY.id..string.format("Key Speed=%s.", val)) + + elseif key:lower():find("on road") or key:lower():find("onroad") or key:lower():find("use road")then + + assignment.onroad=true + self:T2(ARTY.id..string.format("Key Onroad=true.")) + + elseif key:lower():find("irrevocable") or key:lower():find("readonly") then + + assignment.readonly=true + self:T2(ARTY.id..string.format("Key Readonly=true.")) + + elseif key:lower():find("canceltarget") then + + assignment.canceltarget=true + self:T2(ARTY.id..string.format("Key Cancel Target (before move)=true.")) + + elseif key:lower():find("cancelcurrent") then + + assignment.cancelcurrent=true + self:T2(ARTY.id..string.format("Key Cancel Current=true.")) + + elseif assignment.request and key:lower():find("rearm") then + + assignment.requestrearming=true + self:T2(ARTY.id..string.format("Key Request Rearming=true.")) + + elseif assignment.request and key:lower():find("ammo") then + + assignment.requestammo=true + self:T2(ARTY.id..string.format("Key Request Ammo=true.")) + + elseif assignment.request and key:lower():find("target") then + + assignment.requesttargets=true + self:T2(ARTY.id..string.format("Key Request Targets=true.")) + + elseif assignment.request and (key:lower():find("move") or key:lower():find("relocation")) then + + assignment.requestmoves=true + self:T2(ARTY.id..string.format("Key Request Moves=true.")) + + elseif key:lower():find("lldms") then + + local _flat = "%d+:%d+:%d+%s*[N,S]" + local _flon = "%d+:%d+:%d+%s*[W,E]" + local _lat=key:match(_flat) + local _lon=key:match(_flon) + self:T2(ARTY.id..string.format("Key LLDMS: lat=%s, long=%s", _lat,_lon)) + + if _lat and _lon then + + -- Convert DMS string to DD numbers format. + local _latitude, _longitude=self:_LLDMS2DD(_lat, _lon) + self:T2(ARTY.id..string.format("Key LLDMS: lat=%.3f, long=%.3f", _latitude,_longitude)) - local v=self:_split(key, '"') - - for i=2,#v,2 do - table.insert(assignment.battery, v[i]) - self:T2(ARTY.id..string.format("Key Battery=%s.", v[i])) + -- Convert LL to coordinate object. + if _latitude and _longitude then + assignment.coord=COORDINATE:NewFromLLDD(_latitude,_longitude) end - elseif key:lower():find("time") then - - if val:lower():find("now") then - assignment.time=self:_SecondsToClock(timer.getTime0()+5) - else - assignment.time=val - end - self:T2(ARTY.id..string.format("Key Time=%s.", val)) - - elseif key:lower():find("shot") then - - assignment.nshells=tonumber(s[2]) - self:T(ARTY.id..string.format("Key Shot=%s.", val)) - - elseif key:lower():find("prio") then - - assignment.prio=tonumber(val) - self:T2(string.format("Key Prio=%s.", val)) - - elseif key:lower():find("maxengage") then - - assignment.maxengage=tonumber(val) - self:T2(ARTY.id..string.format("Key Maxengage=%s.", val)) - - elseif key:lower():find("radius") then - - assignment.radius=tonumber(val) - self:T2(ARTY.id..string.format("Key Radius=%s.", val)) - - elseif key:lower():find("weapon") then - - if val:lower():find("cannon") then - assignment.weapontype=ARTY.WeaponType.Cannon - elseif val:lower():find("rocket") then - assignment.weapontype=ARTY.WeaponType.Rockets - elseif val:lower():find("missile") then - assignment.weapontype=ARTY.WeaponType.GuidedMissile - elseif val:lower():find("nuke") then - assignment.weapontype=ARTY.WeaponType.TacticalNukes - else - assignment.weapontype=ARTY.WeaponType.Auto - end - self:T2(ARTY.id..string.format("Key Weapon=%s.", val)) - - elseif key:lower():find("speed") then - - assignment.speed=tonumber(val) - self:T2(ARTY.id..string.format("Key Speed=%s.", val)) - - elseif key:lower():find("on road") or key:lower():find("onroad") or key:lower():find("use road")then - - assignment.onroad=true - self:T2(ARTY.id..string.format("Key Onroad=true.")) - - elseif key:lower():find("irrevocable") or key:lower():find("readonly") then - - assignment.readonly=true - self:T2(ARTY.id..string.format("Key Readonly=true.")) - - elseif key:lower():find("canceltarget") then - - assignment.canceltarget=true - self:T2(ARTY.id..string.format("Key Cancel Target (before move)=true.")) - - elseif key:lower():find("cancelcurrent") then - - assignment.cancelcurrent=true - self:T2(ARTY.id..string.format("Key Cancel Current=true.")) - - elseif assignment.request and key:lower():find("ammo") then - assignment.requestammo=true - end - - end - else - self:T2(ARTY.id..string.format("This is NO arty command:\n%s", tostring(text))) + end + end end return assignment @@ -3110,6 +3146,44 @@ function ARTY:_MarkRequestAmmo() self:GetAmmo(true) end +--- Request Moves. +-- @param #ARTY self +function ARTY:_MarkRequestMoves() + local text=string.format("%s, relocations:", self.Controllable:GetName()) + if self.currentMove then + text=text..string.format("\n- %s", self:_MoveInfo(self.currentMove)) + else + text=text..string.format("\n- no current relocation") + end + if #self.moves>0 then + for _,move in pairs(self.moves) do + text=text..string.format("\n- %s", self:_MoveInfo(move)) + end + else + text=text..string.format("\n- no more relocations") + end + MESSAGE:New(text, 20):Clear():ToCoalition(self.Controllable:GetCoalition()) +end + +--- Request Targets. +-- @param #ARTY self +function ARTY:_MarkRequestTargets() + local text=string.format("%s, targets:", self.Controllable:GetName()) + if self.currentTarget then + text=text..string.format("\n- %s", self:_TargetInfo(self.currentTarget)) + else + text=text..string.format("\n- no current target") + end + if #self.targets>0 then + for _,target in pairs(self.targets) do + text=text..string.format("\n- %s", self:_TargetInfo(target)) + end + else + text=text..string.format("\n- no more targets") + end + MESSAGE:New(text, 20):Clear():ToCoalition(self.Controllable:GetCoalition()) +end + --- Create a name for an engagement initiated by placing a marker. -- @param #ARTY self -- @param #number markerid ID of the placed marker. @@ -3320,7 +3394,7 @@ function ARTY:_CheckTargetsInRange() MESSAGE:New(_name.." assigned.", 10):ToCoalitionIf(self.Controllable:GetCoalition(), self.report or self.Debug) -- Assign relocation move. - self:AssignMoveCoord(_tocoord, nil, nil, self.autorelocateonroad, false, _name, true) + self:AssignMoveCoord(_tocoord, nil, nil, self.autorelocateonroad, false, _name, true) end @@ -3716,6 +3790,66 @@ function ARTY:_MoveInfo(move) return string.format("%s: time=%s, speed=%d, onroad=%s, cancel=%s", move.name, _clock, move.speed, tostring(move.onroad), tostring(move.cancel)) end +--- Convert Latitude and Lontigude from DMS to DD. +-- @param #ARTY self +-- @param #string l1 Latitude or longitude as string in the format DD:MM:SS N/S/W/E +-- @param #string l2 Latitude or longitude as string in the format DD:MM:SS N/S/W/E +-- @return #number Latitude in decimal degree format. +-- @return #number Longitude in decimal degree format. +function ARTY:_LLDMS2DD(l1,l2) + self:F2(l1,l2) + + -- Make an array of lat and long. + local _latlong={l1,l2} + + local _latitude=nil + local _longitude=nil + + for _,ll in pairs(_latlong) do + + -- Format is expected as "DD:MM:SS" or "D:M:S". + local _format = "%d+:%d+:%d+" + local _ldms=ll:match(_format) + + if ldms then + + -- Split DMS to degrees, minutes and seconds. + local _dms=self:_split(_ldms, ":") + local _deg=tonumber(_dms[1]) + local _min=tonumber(_dms[2]) + local _sec=tonumber(_dms[3]) + + -- Convert DMS to DD. + local function DMS2DD(d,m,s) + return d+m/60+s/3600 + end + + -- Detect with hemisphere is meant. + if ll:match("N") then + _latitude=DMS2DD(_deg,_min,_sec) + elseif ll:match("S") then + _latitude=-DMS2DD(_deg,_min,_sec) + elseif ll:match("W") then + _longitude=-DMS2DD(_deg,_min,_sec) + elseif ll:match("E") then + _longitude=DMS2DD(_deg,_min,_sec) + end + + -- Debug text. + local text=string.format("DMS %02d Deg %02d min %02d sec",_deg,_min,_sec) + self:T2(ARTY.id..text) + + end + end + + -- Debug text. + local text=string.format("\nLatitude %.3f", _latitude) + text=text..string.format("\nLongitude %.3f", _longitude) + self:T2(ARTY.id..text) + + return _latitude,_longitude +end + --- Convert time in seconds to hours, minutes and seconds. -- @param #ARTY self -- @param #number seconds Time in seconds. From 38a03f4cbc06fff4204fe015f9fdf3f4d378cd69 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 6 Jun 2018 22:57:04 +0200 Subject: [PATCH 162/420] ARTY v0.9.94 Bug fixes and improvements. --- .../Moose/Functional/Artillery.lua | 164 +++++++++++++----- 1 file changed, 117 insertions(+), 47 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 48d2ab37c..7920bb774 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -277,8 +277,8 @@ -- Targets and relocations can be assigned by players via placing a mark on the F10 map. The marker text must contain certain keywords. -- -- This feature can be turned on with the @{#ARTY.SetMarkAssignmentsOn}(*key*). The parameter *key* is optional. When set, it can be used as PIN, i.e. only --- player who know the correct key are able to assign targets or relocations. Default behavior is that all players belonging to the same coalition as the --- ARTY group are able to assign targets and moves. +-- players who know the correct key are able to assign and cancel targets or relocations. Default behavior is that all players belonging to the same coalition as the +-- ARTY group are able to assign targets and moves without a key. -- -- ### Target Assignments -- A new target can be assigned by writing **arty engage** in the marker text. This can be followed by a comma separated lists of optional keywords and parameters: @@ -297,7 +297,8 @@ -- arty engage! -- arty engage! shots 20, prio 10, time 08:15, weapon cannons -- arty engage! battery "Blue Paladin 1" "Blue MRLS 1", shots 10, time 10:15 --- arty engage! battery "Blue Paladin 1", key 666 +-- arty engage! battery "Blue MRLS 1", key 666 +-- arty engage, battery "Paladin Alpha", weapon nukes, shots 1, time 20:15 -- -- Note that the keywords and parameters are case insensitve. Only exception are the battery group names. These must be exactly the same as the names of the goups defined -- in the mission editor. @@ -315,9 +316,33 @@ -- * *readonly* Marker cannot be deleted by users any more. Hence, assignment cannot be cancelled by removing the marker. -- -- Here are some examples: --- arty move! time 23:45, speed 50, onroad, cancel --- arty move! battery "Blue Paladin", onroad --- arty move, cancel, speed 10, onroad +-- arty move +-- arty move! time 23:45, speed 50, on road +-- arty move! battery "Blue Paladin" +-- arty move, battery "Blue MRLS", canceltarget, speed 10, on road +-- +-- ### Coordinate Independent Commands +-- +-- There are a couple of commands, which are independent of the position where the marker is placed. +-- These commands are +-- arty move, cancelcurrent +-- which will cancel the current relocation movement. Of course, this can be combined with the *battery* keyword to address a certain battery. +-- Same goes for targets, e.g. +-- arty engage, battery "Paladin Alpha", cancelcurrent +-- which will cancel all running firing tasks. +-- +-- ### General Requests +-- +-- Marks can also be to send requests to the ARTY group. This is done by the keyword **arty request**, which can have the keywords +-- * *target* All assigned targets are reported. +-- * *move* All assigned relocation moves are reported. +-- * *ammo* Current ammunition status is reported. +-- +-- For example +-- arty request, ammo +-- arty request, battery "Paladin Bravo", targets +-- arty request, battery "MRLS Charly", move +-- -- -- ## Fine Tuning -- @@ -414,6 +439,7 @@ ARTY={ Nmissiles0=0, Nukes0=0, FullAmmo=0, + defaultROE="weapon_hold", StatusInterval=10, WaitForShotTime=300, DCSdesc=nil, @@ -531,7 +557,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #string version -ARTY.version="0.9.93" +ARTY.version="0.9.94" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -556,6 +582,8 @@ ARTY.version="0.9.93" -- TODO: Improve handling of special weapons. When winchester if using selected weapons? -- TODO: Handle rearming for ships. -- TODO: Make coordinate after rearming general, i.e. also work after the group has moved to anonther location. +-- TODO: Add set commands via markers. E.g. set rearming place. +-- TODO: Test stationary types like mortas ==> rearming etc. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1216,6 +1244,9 @@ function ARTY:onafterStart(Controllable, From, Event, To) self:T(ARTY.id..text) end + -- Set default ROE to weapon hold. + self.Controllable:OptionROEHoldFire() + -- Add event handler. self:HandleEvent(EVENTS.Shot, self._OnEventShot) self:HandleEvent(EVENTS.Dead, self._OnEventDead) @@ -1246,7 +1277,13 @@ end --- After "Start" event. Initialized ROE and alarm state. Starts the event handler. -- @param #ARTY self -function ARTY:_StatusReport() +-- @param #boolean display (Optional) If true, send message to coalition. Default false. +function ARTY:_StatusReport(display) + + -- Set default. + if display==nil then + display=false + end -- Get Ammo. local Nammo, Nshells, Nrockets, Nmissiles=self:GetAmmo() @@ -1286,6 +1323,7 @@ function ARTY:_StatusReport() end text=text..string.format("******************************************************") env.info(ARTY.id..text) + MESSAGE:New(text, 20):Clear():ToCoalitionIf(self.Controllable:GetCoalition(), display) end @@ -1623,6 +1661,9 @@ function ARTY:_OnEventMarkChange(Event) if _assign.requesttargets then self:_MarkRequestTargets() end + if _assign.requeststatus then + self:_MarkRequestStatus() + end if _assign.requestrearming then self:Rearm() end @@ -1646,6 +1687,8 @@ function ARTY:_OnEventMarkChange(Event) if _validkey then -- Convert (wrong x-->z, z-->x) vec3 + -- TODO: This needs to be "fixed", once DCS gives the correct numbers for x and z. + -- local vec3={y=Event.pos.y, x=Event.pos.x, z=Event.pos.z} local vec3={y=Event.pos.y, x=Event.pos.z, z=Event.pos.x} -- Get coordinate from vec3. @@ -1807,6 +1850,7 @@ function ARTY:onafterStatus(Controllable, From, Event, To) if self:is("Rearmed") then local distance=self.Controllable:GetCoordinate():Get2DDistance(self.InitialCoord) self:T2(ARTY.id..string.format("%s: Rearmed. Distance ARTY to InitalCoord = %d m", Controllable:GetName(), distance)) + -- Check that ARTY group is back and set it to combat ready. if distance <= self.RearmingDistance then self:T2(ARTY.id..string.format("%s: Rearmed ==> CombatReady", Controllable:GetName())) self:CombatReady() @@ -2062,6 +2106,9 @@ function ARTY:onafterCeaseFire(Controllable, From, Event, To, target) self:RemoveTarget(target.name) end + -- Set ROE to weapon hold. + self.Controllable:OptionROEHoldFire() + -- Clear tasks. self.Controllable:ClearTasks() @@ -2105,6 +2152,14 @@ end function ARTY:onbeforeRearm(Controllable, From, Event, To) self:_EventFromTo("onbeforeRearm", Event, From, To) + local _rearmed=self:_CheckRearmed() + if _rearmed then + self:T(ARTY.id..string.format("%s, group is already armed to the teeth. Rearming request denied!", self.Controllable:GetName())) + return false + else + self:T(ARTY.id..string.format("%s, group might be rearmed.", self.Controllable:GetName())) + end + -- Check if a reaming unit or rearming place was specified. if self.RearmingGroup and self.RearmingGroup:IsAlive() then return true @@ -2128,6 +2183,9 @@ function ARTY:onafterRearm(Controllable, From, Event, To) -- Coordinate of ARTY unit. local coordARTY=self.Controllable:GetCoordinate() + -- Remember current coordinates so that we find our way back home. + self.InitialCoord=coordARTY + -- Coordinate of rearming group. local coordRARM=nil if self.RearmingGroup then @@ -2152,7 +2210,9 @@ function ARTY:onafterRearm(Controllable, From, Event, To) -- Route ARTY group to rearming place. if dA > self.RearmingDistance then - self:Move(self:_VicinityCoord(self.RearmingPlaceCoord, self.RearmingDistance/4, self.RearmingDistance/2), self.Speed, self.RearmingArtyOnRoad) + local _tocoord=self:_VicinityCoord(self.RearmingPlaceCoord, self.RearmingDistance/4, self.RearmingDistance/2) + self:AssignMoveCoord(_tocoord, nil, self.Speed, self.RearmingArtyOnRoad, false, "Relocate to rearming place", true) + --self:Move(, self.Speed, self.RearmingArtyOnRoad) end -- Route Rearming group to rearming place. @@ -2193,7 +2253,8 @@ function ARTY:onafterRearm(Controllable, From, Event, To) -- Route ARTY group to rearming place. if dA > self.RearmingDistance then - self:Move(self:_VicinityCoord(self.RearmingPlaceCoord), self.Speed, self.RearmingArtyOnRoad) + local _tocoord=self:_VicinityCoord(self.RearmingPlaceCoord) + self:AssignMoveCoord(_tocoord, nil, self.Speed, self.RearmingArtyOnRoad, false, "Relocate to rearming place", true) end end @@ -2222,7 +2283,8 @@ function ARTY:onafterRearmed(Controllable, From, Event, To) -- Route ARTY group back to where it came from (if distance is > 100 m). local d1=self.Controllable:GetCoordinate():Get2DDistance(self.InitialCoord) if d1 > self.RearmingDistance then - self:Move(self.InitialCoord, self.Speed, self.RearmingArtyOnRoad) + --self:Move(self.InitialCoord, self.Speed, self.RearmingArtyOnRoad) + self:AssignMoveCoord(self.InitialCoord, nil, self.Speed, self.RearmingArtyOnRoad, false, "After rearm back to initial pos", true) end -- Route unit back to where it came from (if distance is > 100 m). @@ -2370,9 +2432,6 @@ end function ARTY:onafterNewTarget(Controllable, From, Event, To, target) self:_EventFromTo("onafterNewTarget", Event, From, To) - -- Check if target is in range. - --local _inrange, _toofar, _tooclose=self:_TargetInRange(target, self.report or self.Debug) - -- Debug message. local text=string.format("Adding new target %s.", target.name) MESSAGE:New(text, 5):ToAllIf(self.Debug) @@ -2569,7 +2628,7 @@ function ARTY:_NuclearBlast(_coord) -- Distance from group to impact point. local distance= spos:Get2DDistance(_coord) - -- Place markers on every possible scenery object. + -- Place markers on every possible scenery object. if self.Debug then local MarkerID=spos:MarkToAll(string.format("%s scenery object %s", self.Controllable:GetName(), SceneryObject:GetTypeName())) local text=string.format("%s scenery: %s, Coord %s", self.Controllable:GetName(), SceneryObject:GetTypeName(), SceneryObject:GetCoordinate():ToStringLLDMS()) @@ -3009,6 +3068,7 @@ function ARTY:_Markertext(text) -- keywords are split by "," local keywords=self:_split(text, ",") + self:T({keywords=keywords}) for _,key in pairs(keywords) do @@ -3025,7 +3085,7 @@ function ARTY:_Markertext(text) self:T2(ARTY.id..string.format("Key Battery=%s.", v[i])) end - elseif key:lower():find("time") then + elseif (assignment.engage or assignment.move) and key:lower():find("time") then if val:lower():find("now") then assignment.time=self:_SecondsToClock(timer.getTime0()+2) @@ -3034,27 +3094,27 @@ function ARTY:_Markertext(text) end self:T2(ARTY.id..string.format("Key Time=%s.", val)) - elseif key:lower():find("shot") then + elseif assignment.engage and key:lower():find("shot") then assignment.nshells=tonumber(s[2]) self:T(ARTY.id..string.format("Key Shot=%s.", val)) - elseif key:lower():find("prio") then + elseif assignment.engage and key:lower():find("prio") then assignment.prio=tonumber(val) self:T2(string.format("Key Prio=%s.", val)) - elseif key:lower():find("maxengage") then + elseif assignment.engage and key:lower():find("maxengage") then assignment.maxengage=tonumber(val) self:T2(ARTY.id..string.format("Key Maxengage=%s.", val)) - elseif key:lower():find("radius") then + elseif assignment.engage and key:lower():find("radius") then assignment.radius=tonumber(val) self:T2(ARTY.id..string.format("Key Radius=%s.", val)) - elseif key:lower():find("weapon") then + elseif assignment.engage and key:lower():find("weapon") then if val:lower():find("cannon") then assignment.weapontype=ARTY.WeaponType.Cannon @@ -3069,12 +3129,12 @@ function ARTY:_Markertext(text) end self:T2(ARTY.id..string.format("Key Weapon=%s.", val)) - elseif key:lower():find("speed") then + elseif assignment.move and key:lower():find("speed") then assignment.speed=tonumber(val) self:T2(ARTY.id..string.format("Key Speed=%s.", val)) - elseif key:lower():find("on road") or key:lower():find("onroad") or key:lower():find("use road")then + elseif assignment.move and (key:lower():find("on road") or key:lower():find("onroad") or key:lower():find("use road")) then assignment.onroad=true self:T2(ARTY.id..string.format("Key Onroad=true.")) @@ -3084,12 +3144,12 @@ function ARTY:_Markertext(text) assignment.readonly=true self:T2(ARTY.id..string.format("Key Readonly=true.")) - elseif key:lower():find("canceltarget") then + elseif assignment.move and key:lower():find("canceltarget") then assignment.canceltarget=true self:T2(ARTY.id..string.format("Key Cancel Target (before move)=true.")) - elseif key:lower():find("cancelcurrent") then + elseif (assignment.engage or assignment.move) and key:lower():find("cancelcurrent") then assignment.cancelcurrent=true self:T2(ARTY.id..string.format("Key Cancel Current=true.")) @@ -3108,7 +3168,12 @@ function ARTY:_Markertext(text) assignment.requesttargets=true self:T2(ARTY.id..string.format("Key Request Targets=true.")) - + + elseif assignment.request and key:lower():find("status") then + + assignment.requeststatus=true + self:T2(ARTY.id..string.format("Key Request Status=true.")) + elseif assignment.request and (key:lower():find("move") or key:lower():find("relocation")) then assignment.requestmoves=true @@ -3140,27 +3205,32 @@ function ARTY:_Markertext(text) return assignment end ---- Request ammo. +--- Request ammo via mark. -- @param #ARTY self function ARTY:_MarkRequestAmmo() self:GetAmmo(true) end +--- Request status via mark. +-- @param #ARTY self +function ARTY:_MarkRequestStatus() + self:_StatusReport(true) +end + --- Request Moves. -- @param #ARTY self function ARTY:_MarkRequestMoves() local text=string.format("%s, relocations:", self.Controllable:GetName()) - if self.currentMove then - text=text..string.format("\n- %s", self:_MoveInfo(self.currentMove)) - else - text=text..string.format("\n- no current relocation") - end if #self.moves>0 then for _,move in pairs(self.moves) do - text=text..string.format("\n- %s", self:_MoveInfo(move)) + if self.currentMove and move.name == self.currentMove.name then + text=text..string.format("\n- %s (current)", self:_MoveInfo(move)) + else + text=text..string.format("\n- %s", self:_MoveInfo(move)) + end end else - text=text..string.format("\n- no more relocations") + text=text..string.format("\n- no queued relocations") end MESSAGE:New(text, 20):Clear():ToCoalition(self.Controllable:GetCoalition()) end @@ -3169,17 +3239,16 @@ end -- @param #ARTY self function ARTY:_MarkRequestTargets() local text=string.format("%s, targets:", self.Controllable:GetName()) - if self.currentTarget then - text=text..string.format("\n- %s", self:_TargetInfo(self.currentTarget)) - else - text=text..string.format("\n- no current target") - end if #self.targets>0 then for _,target in pairs(self.targets) do - text=text..string.format("\n- %s", self:_TargetInfo(target)) + if self.currentTarget and target.name == self.currentTarget.name then + text=text..string.format("\n- %s (current)", self:_TargetInfo(target)) + else + text=text..string.format("\n- %s", self:_TargetInfo(target)) + end end else - text=text..string.format("\n- no more targets") + text=text..string.format("\n- no queued targets") end MESSAGE:New(text, 20):Clear():ToCoalition(self.Controllable:GetCoalition()) end @@ -3200,7 +3269,7 @@ function ARTY:_MarkMoveName(markerid) return string.format("BATTERY=%s, Marked Relocation ID=%d", self.Controllable:GetName(), markerid) end ---- Create a name for a relocation move initiated by placing a marker. +--- Get the marker ID from the assigned task name. -- @param #ARTY self -- @param #string name Name of the assignment. -- @return #string Name of the ARTY group or nil @@ -3552,14 +3621,14 @@ function ARTY:_GetTargetIndexByName(name) for i=1,#self.targets do local targetname=self.targets[i].name - self:T(ARTY.id..string.format("Have target with name %s. Index = %d", targetname, i)) + self:T3(ARTY.id..string.format("Have target with name %s. Index = %d", targetname, i)) if targetname==name then - self:T(ARTY.id..string.format("Found target with name %s. Index = %d", name, i)) + self:T2(ARTY.id..string.format("Found target with name %s. Index = %d", name, i)) return i end end - self:E(ARTY.id..string.format("ERROR: Target with name %s could not be found!", name)) + self:T2(ARTY.id..string.format("WARNING: Target with name %s could not be found. (This can happen.)", name)) return nil end @@ -3572,13 +3641,14 @@ function ARTY:_GetMoveIndexByName(name) for i=1,#self.moves do local movename=self.moves[i].name + self:T3(ARTY.id..string.format("Have move with name %s. Index = %d", movename, i)) if movename==name then self:T2(ARTY.id..string.format("Found move with name %s. Index = %d", name, i)) return i end end - self:E(ARTY.id..string.format("ERROR: Move with name %s could not be found!", name)) + self:T2(ARTY.id..string.format("WARNING: Move with name %s could not be found. (This can happen.)", name)) return nil end @@ -3739,7 +3809,7 @@ function ARTY:_VicinityCoord(coord, rmin, rmax) local vec2=coord:GetRandomVec2InRadius(rmax, rmin) local pops=COORDINATE:NewFromVec2(vec2) -- Debug info. - self:T(ARTY.id..string.format("Vicinity distance = %d (rmin=%d, rmax=%d)", pops:Get2DDistance(coord), rmin, rmax)) + self:T3(ARTY.id..string.format("Vicinity distance = %d (rmin=%d, rmax=%d)", pops:Get2DDistance(coord), rmin, rmax)) return pops end From 8b3d7ebf048309e2b7757c726f12c1a16dd2efbf Mon Sep 17 00:00:00 2001 From: alexproust <38035263+alexproust@users.noreply.github.com> Date: Thu, 7 Jun 2018 12:57:10 +0200 Subject: [PATCH 163/420] Update Routines.lua Correction MessageToBlue function --- Moose Development/Moose/Utilities/Routines.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Utilities/Routines.lua b/Moose Development/Moose/Utilities/Routines.lua index 285f270d7..c63359c2e 100644 --- a/Moose Development/Moose/Utilities/Routines.lua +++ b/Moose Development/Moose/Utilities/Routines.lua @@ -2290,7 +2290,7 @@ end function MessageToBlue( MsgText, MsgTime, MsgName ) --trace.f() - MESSAGE:New( MsgText, MsgTime, "To Blue Coalition" ):ToCoalition( coalition.side.RED ) + MESSAGE:New( MsgText, MsgTime, "To Blue Coalition" ):ToCoalition( coalition.side.BLUE ) end function getCarrierHeight( CarrierGroup ) From c6fc571c957b73acd0f4677f08df86f76d831edf Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Fri, 8 Jun 2018 00:00:41 +0200 Subject: [PATCH 164/420] ARTY adjusted docu some improvemens --- Moose Development/Moose/Core/Point.lua | 25 ++- .../Moose/Functional/Artillery.lua | 143 +++++++++++------- 2 files changed, 103 insertions(+), 65 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 543bce752..b7688ad82 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -957,26 +957,25 @@ do -- COORDINATE -- The first point is the closest point on road of the given coordinate. The last point is the closest point on road of the ToCoord. Hence, the coordinate itself and the final ToCoord are not necessarily included in the path. -- @param #COORDINATE self -- @param #COORDINATE ToCoord Coordinate of destination. - -- @return #table Table of coordinates on road. + -- @return #table Table of coordinates on road. If no path on road can be found, nil is returned. function COORDINATE:GetPathOnRoad(ToCoord) -- DCS API function returning a table of vec2. local path = land.findPathOnRoads("roads", self.x, self.z, ToCoord.x, ToCoord.z) - --Path[#Path+1]=COORDINATE:NewFromVec2(path[1]) - --Path[#Path+1]=COORDINATE:NewFromVec2(path[#path]) - --Path[#Path+1]=self:GetClosestPointToRoad() - --Path[#Path+1]=ToCoord:GetClosestPointToRoad() - -- I've removed this stuff because it severely slows down DCS in case of paths with a lot of segments. - -- Just the beginning and the end point is sufficient. - local Path={} - --Path[#Path+1]=self - for i, v in ipairs(path) do - Path[#Path+1]=COORDINATE:NewFromVec2(v) - end - --Path[#Path+1]=ToCoord + if path then + --Path[#Path+1]=self + for i, v in ipairs(path) do + Path[#Path+1]=COORDINATE:NewFromVec2(v) + end + --Path[#Path+1]=ToCoord + else + -- There are cases where no path on road can be found. + return nil + end + return Path end diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 7920bb774..fde0f379c 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -2,17 +2,20 @@ -- -- === -- --- The ARTY class can be used to easily assign and manage targets for artillery units. +-- The ARTY class can be used to easily assign and manage targets for artillery units using an advanced queueing system. -- -- ## Features: -- -- * Multiple targets can be assigned. No restriction on number of targets. -- * Targets can be given a priority. Engagement of targets is executed a according to their priority. -- * Engagements can be scheduled, i.e. will be executed at a certain time of the day. +-- * Multiple relocations of the group can be assigned and scheduled via queueing system. -- * Special weapon types can be selected for each attack, e.g. cruise missiles for Naval units. --- * Automatic rearming once the artillery is out of ammo. +-- * Automatic rearming once the artillery is out of ammo (optional). +-- * Automatic relocation after each firing engagement to prevent counter strikes (optional). +-- * Automatic relocation movements to get the battery within firing range (optional). +-- * Simulation of tactical nuclear shells. -- * New targets can be added during the mission, e.g. when they are detected by recon units. --- * Modeling of tactical nuclear shells. -- * Targets and relocations can be assigned by placing markers on the F10 map. -- * Finite state machine implementation. Mission designer can interact when certain events occur. -- @@ -276,31 +279,31 @@ -- -- Targets and relocations can be assigned by players via placing a mark on the F10 map. The marker text must contain certain keywords. -- --- This feature can be turned on with the @{#ARTY.SetMarkAssignmentsOn}(*key*). The parameter *key* is optional. When set, it can be used as PIN, i.e. only +-- This feature can be turned on with the @{#ARTY.SetMarkAssignmentsOn}(*key*, *readonly*). The parameter *key* is optional. When set, it can be used as PIN, i.e. only -- players who know the correct key are able to assign and cancel targets or relocations. Default behavior is that all players belonging to the same coalition as the -- ARTY group are able to assign targets and moves without a key. -- -- ### Target Assignments --- A new target can be assigned by writing **arty engage** in the marker text. This can be followed by a comma separated lists of optional keywords and parameters: +-- A new target can be assigned by writing **arty engage** in the marker text. This can be followed by a **comma separated list** of optional keywords and parameters: -- -- * *time* Time for which which the engagement is schedules, e.g. 08:42. Default is as soon as possible. --- * *prio* Priority of the engagement as number between 1 (high prio) and 100 (low prio). Default is 50. +-- * *prio* Priority of the engagement as number between 1 (high prio) and 100 (low prio). Default is 50, i.e. medium priority. -- * *shots* Number of shots (shells, rockets or missiles) fired at each engagement. Default is 5. -- * *maxengage* Number of times the target is engaged. Default is 1. -- * *radius* Scattering radius of the fired shots in meters. Default is 100 m. -- * *weapon* Type of weapon to be used. Valid parameters are *cannon*, *rocket*, *missile*, *nuke*. Default is automatic selection. --- * *battery* Name of the ARTY group that the target is assigned to. Note that the name is case sensitive and has to be given in quotation marks. Default is all ARTY groups of the right coalition. +-- * *battery* Name of the ARTY group that the target is assigned to. Note that **the name is case sensitive** and has to be given in quotation marks. Default is all ARTY groups of the right coalition. -- * *key* A number to authorize the target assignment. Only specifing the correct number will trigger an engagement. --- * *readonly* Marker cannot be deleted by users any more. Hence, assignment cannot be cancelled by removing the marker. +-- * *readonly* The marker is readonly and cannot be deleted by users. Hence, assignment cannot be cancelled by removing the marker. -- -- Here are examples of valid marker texts: --- arty engage! --- arty engage! shots 20, prio 10, time 08:15, weapon cannons --- arty engage! battery "Blue Paladin 1" "Blue MRLS 1", shots 10, time 10:15 --- arty engage! battery "Blue MRLS 1", key 666 +-- arty engage +-- arty engage, shots 20, prio 10, time 08:15, weapon cannons +-- arty engage, battery "Blue Paladin 1" "Blue MRLS 1", shots 10, time 10:15 +-- arty engage, battery "Blue MRLS 1", key 666 -- arty engage, battery "Paladin Alpha", weapon nukes, shots 1, time 20:15 -- --- Note that the keywords and parameters are case insensitve. Only exception are the battery group names. These must be exactly the same as the names of the goups defined +-- Note that the keywords and parameters are *case insensitve*. Only exception are the battery group names. These must be exactly the same as the names of the goups defined -- in the mission editor. -- -- ### Relocation Assignments @@ -317,8 +320,8 @@ -- -- Here are some examples: -- arty move --- arty move! time 23:45, speed 50, on road --- arty move! battery "Blue Paladin" +-- arty move, time 23:45, speed 50, on road +-- arty move, battery "Blue Paladin" -- arty move, battery "Blue MRLS", canceltarget, speed 10, on road -- -- ### Coordinate Independent Commands @@ -334,6 +337,7 @@ -- ### General Requests -- -- Marks can also be to send requests to the ARTY group. This is done by the keyword **arty request**, which can have the keywords +-- -- * *target* All assigned targets are reported. -- * *move* All assigned relocation moves are reported. -- * *ammo* Current ammunition status is reported. @@ -348,6 +352,8 @@ -- -- The mission designer has a few options to tailor the ARTY object according to his needs. -- +-- * @{#ARTY.SetAutomaticRelocate}(*maxdist*, *onroad*) lets the ARTY group automatically move to within firing range if a current target is outside the min/max firing range. The +-- optional parameter *maxdist* is the maximum distance im km the group will move. If the distance is greater no relocation is performed. Default is 50 km. -- * @{#ARTY.SetRelocateAfterEngagement}() will cause the ARTY group to change its position after each firing assignment. -- * @{#ARTY.SetRelocateDistance}(*rmax*, *rmin*) sets the max/min distance for relocation of the group. Default distance is randomly between 300 and 800 m. -- * @{#ARTY.RemoveAllTargets}() removes all targets from the target queue. @@ -1423,36 +1429,7 @@ function ARTY:_OnEventShot(EventData) local _weapontype=self:_WeaponTypeName(self.currentTarget.weapontype) self:T(ARTY.id..string.format("Group %s ammo: total=%d, shells=%d, rockets=%d, missiles=%d", self.Controllable:GetName(), _nammo, _nshells, _nrockets, _nmissiles)) self:T(ARTY.id..string.format("Group %s uses weapontype %s for current target.", self.Controllable:GetName(), _weapontype)) - - -- Special weapon type requested ==> Check if corresponding ammo is empty. - local _partlyoutofammo=false - if self.currentTarget.weapontype==ARTY.WeaponType.Cannon and _nshells==0 then - - self:T(ARTY.id..string.format("Group %s, cannons requested but shells empty.", self.Controllable:GetName())) - _partlyoutofammo=true - - elseif self.currentTarget.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes<=0 then - - self:T(ARTY.id..string.format("Group %s, tactical nukes requested but nukes empty.", self.Controllable:GetName())) - _partlyoutofammo=true - - elseif self.currentTarget.weapontype==ARTY.WeaponType.Rockets and _nrockets==0 then - - self:T(ARTY.id..string.format("Group %s, rockets requested but rockets empty.", self.Controllable:GetName())) - _partlyoutofammo=true - - elseif self.currentTarget.weapontype==ARTY.WeaponType.UnguidedAny and _nshells+_nrockets==0 then - - self:T(ARTY.id..string.format("Group %s, unguided weapon requested but shells AND rockets empty.", self.Controllable:GetName())) - _partlyoutofammo=true - - elseif (self.currentTarget.weapontype==ARTY.WeaponType.GuidedMissile or self.currentTarget.weapontype==ARTY.WeaponType.CruiseMissile or self.currentTarget.weapontype==ARTY.WeaponType.AntiShipMissile) and _nmissiles==0 then - - self:T(ARTY.id..string.format("Group %s, guided, anti-ship or cruise missiles requested but all missiles empty.", self.Controllable:GetName())) - _partlyoutofammo=true - - end - + -- Check if number of shots reached max. local _ceasefire=false local _relocate=false @@ -1469,6 +1446,10 @@ function ARTY:_OnEventShot(EventData) end end + -- Check if we are partly out of ammo. + -- TODO: move this to status. + local _partlyoutofammo=self:_CheckOutOfAmmo() + -- Check if we are (partly) out of ammo. if _outofammo or _partlyoutofammo then _ceasefire=true @@ -1497,6 +1478,51 @@ function ARTY:_OnEventShot(EventData) end end +--- Check if group is (partly) out of ammo. +-- @param #ARTY self +-- @return @boolean True if any target in the queue requests a weapon type that is null. +function ARTY:_CheckOutOfAmmo() + + -- Get current ammo. + local _nammo,_nshells,_nrockets,_nmissiles=self:GetAmmo() + + -- Special weapon type requested ==> Check if corresponding ammo is empty. + local _partlyoutofammo=false + + for _,Target in pairs(self.targets) do + + if Target.weapontype==ARTY.WeaponType.Cannon and _nshells==0 then + + self:T(ARTY.id..string.format("Group %s, cannons requested but shells empty.", self.Controllable:GetName())) + _partlyoutofammo=true + + elseif Target.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes<=0 then + + self:T(ARTY.id..string.format("Group %s, tactical nukes requested but nukes empty.", self.Controllable:GetName())) + _partlyoutofammo=true + + elseif Target.weapontype==ARTY.WeaponType.Rockets and _nrockets==0 then + + self:T(ARTY.id..string.format("Group %s, rockets requested but rockets empty.", self.Controllable:GetName())) + _partlyoutofammo=true + + elseif Target.weapontype==ARTY.WeaponType.UnguidedAny and _nshells+_nrockets==0 then + + self:T(ARTY.id..string.format("Group %s, unguided weapon requested but shells AND rockets empty.", self.Controllable:GetName())) + _partlyoutofammo=true + + elseif (Target.weapontype==ARTY.WeaponType.GuidedMissile or Target.weapontype==ARTY.WeaponType.CruiseMissile or Target.weapontype==ARTY.WeaponType.AntiShipMissile) and _nmissiles==0 then + + self:T(ARTY.id..string.format("Group %s, guided, anti-ship or cruise missiles requested but all missiles empty.", self.Controllable:GetName())) + _partlyoutofammo=true + + end + + end + + return _partlyoutofammo +end + --- After "Start" event. Initialized ROE and alarm state. Starts the event handler. -- @param #ARTY self -- @param #table Event @@ -2319,15 +2345,15 @@ function ARTY:_CheckRearmed() -- Rearming status in per cent. local _rearmpc=nammo/self.FullAmmo*100 - -- Send message. + -- Send message if rearming > 1% complete if _rearmpc>1 then - local text=string.format("%s, rearming %d %% complete.", self.Controllable:GetName(), _rearmpc) + local text=string.format("%s, rearming %d %% complete. nammo=%d , fullammo=%d", self.Controllable:GetName(), _rearmpc, nammo, self.FullAmmo) self:T(ARTY.id..text) MESSAGE:New(text, 10):ToCoalitionIf(self.Controllable:GetCoalition(), self.report or self.Debug) end -- Return if ammo is full. - if nammo==self.FullAmmo then + if nammo>=self.FullAmmo then return true else return false @@ -2664,7 +2690,7 @@ end function ARTY:_Move(group, ToCoord, Speed, OnRoad) -- Clear all tasks. - --group:ClearTasks() + group:ClearTasks() group:OptionAlarmStateGreen() group:OptionROEHoldFire() @@ -2681,7 +2707,7 @@ function ARTY:_Move(group, ToCoord, Speed, OnRoad) Speed=math.min(Speed, SpeedMax) -- Current coordinates of group. - local cpini=group:GetCoordinate() + local cpini=group:GetCoordinate() -- Core.Point#COORDINATE -- Distance between current and final point. local dist=cpini:Get2DDistance(ToCoord) @@ -2698,8 +2724,17 @@ function ARTY:_Move(group, ToCoord, Speed, OnRoad) if OnRoad then -- Path on road (only first and last points) - local _first=cpini:GetClosestPointToRoad() - local _last=ToCoord:GetClosestPointToRoad() + --local _first=cpini:GetClosestPointToRoad() + --local _last=ToCoord:GetClosestPointToRoad() + + local _pathonroad=cpini:GetPathOnRoad(ToCoord) + local _first=_pathonroad[1] + local _last=_pathonroad[#_pathonroad] + + if self.Debug then + _first:SmokeBlue() + _last:SmokeBlue() + end -- First point on road. path[#path+1]=_first:WaypointGround(Speed, "On Road") @@ -2715,6 +2750,10 @@ function ARTY:_Move(group, ToCoord, Speed, OnRoad) path[#path+1]=ToCoord:WaypointGround(Speed, formation) task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, true) + if self.Debug then + cpini:SmokeBlue() + ToCoord:SmokeBlue() + end -- Init waypoints of the group. local Waypoints={} From 403f22bd2b1d82bc14110bcc814521f585ab8f37 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sat, 9 Jun 2018 18:33:20 +0200 Subject: [PATCH 165/420] ARTY v0.9.95 Reworked rearming behavior for selected weapons. Many other improvements. --- .../Moose/Functional/Artillery.lua | 519 +++++++++++------- 1 file changed, 319 insertions(+), 200 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index fde0f379c..aa9a043c2 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -354,8 +354,8 @@ -- -- * @{#ARTY.SetAutomaticRelocate}(*maxdist*, *onroad*) lets the ARTY group automatically move to within firing range if a current target is outside the min/max firing range. The -- optional parameter *maxdist* is the maximum distance im km the group will move. If the distance is greater no relocation is performed. Default is 50 km. --- * @{#ARTY.SetRelocateAfterEngagement}() will cause the ARTY group to change its position after each firing assignment. --- * @{#ARTY.SetRelocateDistance}(*rmax*, *rmin*) sets the max/min distance for relocation of the group. Default distance is randomly between 300 and 800 m. +-- * @{#ARTY.SetRelocateAfterEngagement}(*rmax*, *rmin*) will cause the ARTY group to change its position after each firing assignment. +-- Optional parameters *rmax*, *rmin* define the max/min distance for relocation of the group. Default distance is randomly between 300 and 800 m. -- * @{#ARTY.RemoveAllTargets}() removes all targets from the target queue. -- * @{#ARTY.RemoveTarget}(*name*) deletes the target with *name* from the target queue. -- * @{#ARTY.SetMaxFiringRange}(*range*) defines the maximum firing range. Targets further away than this distance are not engaged. @@ -563,7 +563,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #string version -ARTY.version="0.9.94" +ARTY.version="0.9.95" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -811,18 +811,15 @@ function ARTY:AssignMoveCoord(coord, time, speed, onroad, cancel, name, unique) -- Default is current time if no time was specified. time=time or self:_SecondsToClock(timer.getAbsTime()) - - -- Get max speed of group. - local speedmax=self.Controllable:GetSpeedMax() - + -- Set speed. if speed then - -- Make sure, given speed is less than max phycially possible speed of group. - speed=math.min(speed, speedmax) + -- Make sure, given speed is less than max physiaclly possible speed of group. + speed=math.min(speed, self.SpeedMax) elseif self.Speed then - speed=self.Speed + speed=self.Speed else - speed=speedmax*0.7 + speed=self.SpeedMax*0.7 end -- Default is off road. @@ -1096,19 +1093,10 @@ end --- Set relocate after firing. Group will find a new location after each engagement. Default is off -- @param #ARTY self --- @param #number switch (Optional) If true, activate relocation. If false, deactivate relocation. -function ARTY:SetRelocateAfterEngagement(switch) - if switch==nil then - switch=true - end - self.relocateafterfire=switch -end - ---- Set relocation distance. --- @param #ARTY self -- @param #number rmax (Optional) Max distance in meters, the group will move to relocate. Default is 800 m. -- @param #number rmin (Optional) Min distance in meters, the group will move to relocate. Default is 300 m. -function ARTY:SetRelocateDistance(rmax, rmin) +function ARTY:SetRelocateAfterEngagement(rmax, rmin) + self.relocateafterfire=true self.relocateRmax=rmax or 800 self.relocateRmin=rmin or 300 end @@ -1221,6 +1209,10 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("Targets:\n") for _, target in pairs(self.targets) do text=text..string.format("- %s\n", self:_TargetInfo(target)) + local possible=self:_CheckWeaponTypePossible(target) + if not possible then + self:E(ARTY.id..string.format("WARNING: Selected weapon type %s is not possible", self:_WeaponTypeName(target.weapontype))) + end if self.Debug then local zone=ZONE_RADIUS:New(target.name, target.coord:GetVec2(), target.radius) zone:BoundZone(180, coalition.side.NEUTRAL) @@ -1414,46 +1406,55 @@ function ARTY:_OnEventShot(EventData) -- Get current ammo. local _nammo,_nshells,_nrockets,_nmissiles=self:GetAmmo() - -- Decrease available nukes. + -- Decrease available nukes because we just fired one. if self.currentTarget.weapontype==ARTY.WeaponType.TacticalNukes then self.Nukes=self.Nukes-1 end + -- Check if we are completely out of ammo. local _outofammo=false - if _nammo==0 then + if _nammo==0 then self:T(ARTY.id..string.format("Group %s completely out of ammo.", self.Controllable:GetName())) _outofammo=true end + -- Check if we are out of ammo of the weapon type used for this target. + -- Note that should not happen because we only open fire with the available number of shots. + local _partlyoutofammo=self:_CheckOutOfAmmo({self.currentTarget}) + -- Weapon type name for current target. local _weapontype=self:_WeaponTypeName(self.currentTarget.weapontype) self:T(ARTY.id..string.format("Group %s ammo: total=%d, shells=%d, rockets=%d, missiles=%d", self.Controllable:GetName(), _nammo, _nshells, _nrockets, _nmissiles)) self:T(ARTY.id..string.format("Group %s uses weapontype %s for current target.", self.Controllable:GetName(), _weapontype)) - -- Check if number of shots reached max. + local _ceasefire=false local _relocate=false + + -- Check if number of shots reached max. if self.Nshots >= self.currentTarget.nshells then + + -- Debug message local text=string.format("Group %s stop firing on target %s.", self.Controllable:GetName(), self.currentTarget.name) self:T(ARTY.id..text) MESSAGE:New(text, 5):ToAllIf(self.Debug) -- Cease fire. _ceasefire=true - - if self.relocateafterfire then - _relocate=true - end + + -- Relocate if enabled. + _relocate=self.relocateafterfire end - -- Check if we are partly out of ammo. - -- TODO: move this to status. - local _partlyoutofammo=self:_CheckOutOfAmmo() - -- Check if we are (partly) out of ammo. if _outofammo or _partlyoutofammo then _ceasefire=true - end + end + + -- Relocate position. + if _relocate then + self:_Relocate() + end -- Cease fire on current target. if _ceasefire then @@ -1462,26 +1463,22 @@ function ARTY:_OnEventShot(EventData) -- Group is out of ammo (or partly and can rearm) ==> Winchester (==> Rearm). if _outofammo or (_partlyoutofammo and self.RearmingGroup ~=nil) then - self:Winchester() - return + --self:Winchester() + --return end - -- Relocate position - if _relocate then - self:_Relocate() - end - else - self:E(ARTY.id..string.format("ERROR: No current target for group %s?!", self.Controllable:GetName())) + self:E(ARTY.id..string.format("WARNING: No current target for group %s?!", self.Controllable:GetName())) end end end end ---- Check if group is (partly) out of ammo. +--- Check if group is (partly) out of ammo of a special weapon type. -- @param #ARTY self --- @return @boolean True if any target in the queue requests a weapon type that is null. -function ARTY:_CheckOutOfAmmo() +-- @param #table targets Table of targets. +-- @return @boolean True if any target requests a weapon type that is empty. +function ARTY:_CheckOutOfAmmo(targets) -- Get current ammo. local _nammo,_nshells,_nrockets,_nmissiles=self:GetAmmo() @@ -1489,31 +1486,46 @@ function ARTY:_CheckOutOfAmmo() -- Special weapon type requested ==> Check if corresponding ammo is empty. local _partlyoutofammo=false - for _,Target in pairs(self.targets) do + for _,Target in pairs(targets) do - if Target.weapontype==ARTY.WeaponType.Cannon and _nshells==0 then + if Target.weapontype==ARTY.WeaponType.Auto and _nammo==0 then + + self:T(ARTY.id..string.format("Group %s, auto weapon requested for target %s but all ammo is empty.", self.Controllable:GetName(), Target.name)) + _partlyoutofammo=true + + elseif Target.weapontype==ARTY.WeaponType.Cannon and _nshells==0 then - self:T(ARTY.id..string.format("Group %s, cannons requested but shells empty.", self.Controllable:GetName())) + self:T(ARTY.id..string.format("Group %s, cannons requested for target %s but shells empty.", self.Controllable:GetName(), Target.name)) _partlyoutofammo=true elseif Target.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes<=0 then - self:T(ARTY.id..string.format("Group %s, tactical nukes requested but nukes empty.", self.Controllable:GetName())) + self:T(ARTY.id..string.format("Group %s, tactical nukes requested for target %s but nukes empty.", self.Controllable:GetName(), Target.name)) _partlyoutofammo=true elseif Target.weapontype==ARTY.WeaponType.Rockets and _nrockets==0 then - self:T(ARTY.id..string.format("Group %s, rockets requested but rockets empty.", self.Controllable:GetName())) + self:T(ARTY.id..string.format("Group %s, rockets requested for target %s but rockets empty.", self.Controllable:GetName(), Target.name)) _partlyoutofammo=true elseif Target.weapontype==ARTY.WeaponType.UnguidedAny and _nshells+_nrockets==0 then - self:T(ARTY.id..string.format("Group %s, unguided weapon requested but shells AND rockets empty.", self.Controllable:GetName())) + self:T(ARTY.id..string.format("Group %s, unguided weapon requested for target %s but shells AND rockets empty.", self.Controllable:GetName(), Target.name)) _partlyoutofammo=true - elseif (Target.weapontype==ARTY.WeaponType.GuidedMissile or Target.weapontype==ARTY.WeaponType.CruiseMissile or Target.weapontype==ARTY.WeaponType.AntiShipMissile) and _nmissiles==0 then + elseif Target.weapontype==ARTY.WeaponType.GuidedMissile and _nmissiles==0 then - self:T(ARTY.id..string.format("Group %s, guided, anti-ship or cruise missiles requested but all missiles empty.", self.Controllable:GetName())) + self:T(ARTY.id..string.format("Group %s, guided missiles requested for target %s but all missiles empty.", self.Controllable:GetName(), Target.name)) + _partlyoutofammo=true + + elseif Target.weapontype==ARTY.WeaponType.CruiseMissile and _nmissiles==0 then + + self:T(ARTY.id..string.format("Group %s, cruise missiles requested for target %s but all missiles empty.", self.Controllable:GetName(), Target.name)) + _partlyoutofammo=true + + elseif Target.weapontype==ARTY.WeaponType.AntiShipMissile and _nmissiles==0 then + + self:T(ARTY.id..string.format("Group %s, anti-ship missiles requested for target %s but all missiles empty.", self.Controllable:GetName(), Target.name)) _partlyoutofammo=true end @@ -1614,16 +1626,22 @@ function ARTY:_OnEventMarkRemove(Event) -- This should be the unique name of the target or move. if _cancelmove then if self.currentMove and self.currentMove.name==_name then + -- We do clear tasks here because in Arrived() it can cause a CTD if the group did actually arrive! self.Controllable:ClearTasks() + -- Current move is removed here. In contrast to RemoveTarget() there are is no maxengage parameter. self:Arrived() else + -- Remove move from queue self:RemoveMove(_name) end elseif _canceltarget then if self.currentTarget and self.currentTarget.name==_name then + -- Cease fire. self:CeaseFire(self.currentTarget) + -- We still need to remove the target, because there might be more planned engagements (maxengage>1). self:RemoveTarget(_name) else + -- Remove target from queue self:RemoveTarget(_name) end end @@ -1852,13 +1870,7 @@ function ARTY:onafterStatus(Controllable, From, Event, To) self:_StatusReport() end - -- Group is out of ammo. - if self:is("OutOfAmmo") then - self:T2(ARTY.id..string.format("%s: OutOfAmmo. ==> Rearm", Controllable:GetName())) - self:Rearm() - end - - -- Group is out of moving. + -- Group on the move. if self:is("Moving") then self:T2(ARTY.id..string.format("%s: Moving", Controllable:GetName())) end @@ -1895,10 +1907,23 @@ function ARTY:onafterStatus(Controllable, From, Event, To) self:_CheckShootingStarted() end - -- Check if targets are in range and update target.inrange value. self:_CheckTargetsInRange() + -- Check if selected weapon type for target is possible at all. E.g. request rockets for Paladin. + local notpossible={} + for i=1,#self.targets do + local _target=self.targets[i] + local possible=self:_CheckWeaponTypePossible(_target) + if not possible then + table.insert(notpossible, _target.name) + end + end + for _,targetname in pairs(notpossible) do + self:E(ARTY.id..string.format("%s: Removing target %s because requested weapon is not possible with this type of unit.", self.Controllable:GetName(), targetname)) + self:RemoveTarget(targetname) + end + -- Get a valid timed target if it is due to be attacked. local _timedTarget=self:_CheckTimedTargets() @@ -1908,36 +1933,55 @@ function ARTY:onafterStatus(Controllable, From, Event, To) -- Get a commaned move to another location. local _move=self:_CheckMoves() - if (self:is("CombatReady") or self:is("Firing")) and _move then - -- Group is combat ready or firing but we have a move. - self:T2(ARTY.id..string.format("%s: CombatReady/Firing ==> Move", Controllable:GetName())) + if _move then -- Command to move. - self.currentMove=_move - self:Move(_move.coord, _move.speed, _move.onroad) + self:Move(_move) - elseif self:is("CombatReady") or (self:is("Firing") and _timedTarget) then - -- Group is combat ready or firing but we have a high prio timed target. - self:T2(ARTY.id..string.format("%s: CombatReady or Firing+Timed Target ==> OpenFire", Controllable:GetName())) + elseif _timedTarget then - -- Engage target. - if _timedTarget then - - -- Cease fire on current target first. - if self.currentTarget then - self:CeaseFire(self.currentTarget) - end - - -- Open fire on timed target. - self:OpenFire(_timedTarget) - - elseif _normalTarget then - - -- Open fire on normal target. - self:OpenFire(_normalTarget) - + -- Cease fire on current target first. + if self.currentTarget then + self:CeaseFire(self.currentTarget) end + + -- Open fire on timed target. + self:OpenFire(_timedTarget) + + elseif _normalTarget then + + -- Open fire on normal target. + self:OpenFire(_normalTarget) + end + + -- Get ammo. + local nammo, nshells, nrockets, nmissiles=self:GetAmmo() + + -- Check if we have a target in the queue for which weapons are still available. + local gotsome=false + if #self.targets>0 then + for i=1,#self.targets do + local _target=self.targets[i] + if self:_CheckWeaponTypeAvailable(_target)>0 then + gotsome=true + end + end + else + -- No targets in the queue. + gotsome=true + end + + -- No ammo available. Either completely blank or only queued targets for ammo which is out. + if (nammo==0 or not gotsome) and not (self:is("Moving") or self:is("Rearming") or self:is("OutOfAmmo")) then + self:Winchester() + end + + -- Group is out of ammo. + if self:is("OutOfAmmo") then + self:T2(ARTY.id..string.format("%s: OutOfAmmo ==> Rearm ==> Rearming", Controllable:GetName())) + self:Rearm() + end -- Call status again in ~10 sec. self:__Status(self.StatusInterval) @@ -1985,33 +2029,16 @@ function ARTY:onbeforeOpenFire(Controllable, From, Event, To, target) -- Deny transition. return false end - - -- Get ammo. - local Nammo, Nshells, Nrockets, Nmissiles=self:GetAmmo() - local nfire=Nammo - if target.weapontype==ARTY.WeaponType.Auto then - nfire=Nammo - elseif target.weapontype==ARTY.WeaponType.Cannon then - nfire=Nshells - elseif target.weapontype==ARTY.WeaponType.TacticalNukes then - nfire=self.Nukes - elseif target.weapontype==ARTY.WeaponType.Rockets then - nfire=Nrockets - elseif target.weapontype==ARTY.WeaponType.UnguidedAny then - nfire=Nshells+Nrockets - elseif target.weapontype==ARTY.WeaponType.GuidedMissile then - nfire=Nmissiles - elseif target.weapontype==ARTY.WeaponType.CruiseMissile then - nfire=Nmissiles - elseif target.weapontype==ARTY.WeaponType.AntiShipMissile then - nfire=Nmissiles - end + + -- Get the number of available shells, rockets or missiles requested for this target. + local nfire=self:_CheckWeaponTypeAvailable(target) -- Adjust if less than requested ammo is left. target.nshells=math.min(target.nshells, nfire) -- No ammo left ==> deny transition. if target.nshells<1 then + local text=string.format("%s, no ammo left to engage target %s with selected weapon type %s.") return false end @@ -2237,8 +2264,7 @@ function ARTY:onafterRearm(Controllable, From, Event, To) -- Route ARTY group to rearming place. if dA > self.RearmingDistance then local _tocoord=self:_VicinityCoord(self.RearmingPlaceCoord, self.RearmingDistance/4, self.RearmingDistance/2) - self:AssignMoveCoord(_tocoord, nil, self.Speed, self.RearmingArtyOnRoad, false, "Relocate to rearming place", true) - --self:Move(, self.Speed, self.RearmingArtyOnRoad) + self:AssignMoveCoord(_tocoord, nil, nil, self.RearmingArtyOnRoad, false, "REARMING MOVE TO REARMING PLACE", true) end -- Route Rearming group to rearming place. @@ -2280,7 +2306,7 @@ function ARTY:onafterRearm(Controllable, From, Event, To) -- Route ARTY group to rearming place. if dA > self.RearmingDistance then local _tocoord=self:_VicinityCoord(self.RearmingPlaceCoord) - self:AssignMoveCoord(_tocoord, nil, self.Speed, self.RearmingArtyOnRoad, false, "Relocate to rearming place", true) + self:AssignMoveCoord(_tocoord, nil, nil, self.RearmingArtyOnRoad, false, "REARMING MOVE TO REARMING PLACE", true) end end @@ -2307,10 +2333,9 @@ function ARTY:onafterRearmed(Controllable, From, Event, To) self.Nukes=self.Nukes0 -- Route ARTY group back to where it came from (if distance is > 100 m). - local d1=self.Controllable:GetCoordinate():Get2DDistance(self.InitialCoord) - if d1 > self.RearmingDistance then - --self:Move(self.InitialCoord, self.Speed, self.RearmingArtyOnRoad) - self:AssignMoveCoord(self.InitialCoord, nil, self.Speed, self.RearmingArtyOnRoad, false, "After rearm back to initial pos", true) + local dist=self.Controllable:GetCoordinate():Get2DDistance(self.InitialCoord) + if dist > self.RearmingDistance then + self:AssignMoveCoord(self.InitialCoord, nil, nil, self.RearmingArtyOnRoad, false, "REARMING MOVE REARMING COMPLETE", true) end -- Route unit back to where it came from (if distance is > 100 m). @@ -2352,7 +2377,7 @@ function ARTY:_CheckRearmed() MESSAGE:New(text, 10):ToCoalitionIf(self.Controllable:GetCoalition(), self.report or self.Debug) end - -- Return if ammo is full. + -- Return if ammo is full. Strangely, I got the case that a Paladin got one more shell than it can max carry, i.e. 40 not 39 when rearming when it still had some ammo left. if nammo>=self.FullAmmo then return true else @@ -2369,20 +2394,27 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. +-- @param #table move Table containing the move parameters. -- @param Core.Point#COORDINATE ToCoord Coordinate to which the ARTY group should move. -- @param #boolean OnRoad If true group should move on road mainly. -- @return #boolean If true, proceed to onafterMove. -function ARTY:onbeforeMove(Controllable, From, Event, To, ToCoord, OnRoad) +function ARTY:onbeforeMove(Controllable, From, Event, To, move) self:_EventFromTo("onbeforeMove", Event, From, To) -- Check if group can actually move... - if self.SpeedMax==0 then + if self.SpeedMax<1 then return false end - -- Cease fire first. + -- Check if group is engaging. if self.currentTarget then - self:CeaseFire(self.currentTarget) + if move.cancel then + -- Cancel current target. + self:CeaseFire(self.currentTarget) + else + -- We should not cancel. + return false + end end return true @@ -2394,10 +2426,8 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param Core.Point#COORDINATE ToCoord Coordinate to which the ARTY group should move. --- @param #number Speed Speed in km/h at which the grou p should move. --- @param #boolean OnRoad If true group should move on road mainly. -function ARTY:onafterMove(Controllable, From, Event, To, ToCoord, Speed, OnRoad) +-- @param #table move Table containing the move parameters. +function ARTY:onafterMove(Controllable, From, Event, To, move) self:_EventFromTo("onafterMove", Event, From, To) -- Set alarm state to green and ROE to weapon hold. @@ -2405,15 +2435,18 @@ function ARTY:onafterMove(Controllable, From, Event, To, ToCoord, Speed, OnRoad) self.Controllable:OptionROEHoldFire() -- Take care of max speed. - local _Speed=math.min(Speed, self.SpeedMax) + local _Speed=math.min(move.speed, self.SpeedMax) -- Smoke coordinate if self.Debug then - ToCoord:SmokeRed() + move.coord:SmokeRed() end + + -- Set current move. + self.currentMove=move -- Route group to coodinate. - self:_Move(self.Controllable, ToCoord, _Speed, OnRoad) + self:_Move(self.Controllable, move.coord, move.speed, move.onroad) end @@ -2454,7 +2487,6 @@ end -- @param #string Event Event. -- @param #string To To state. -- @param #table target Array holding the target parameters. --- @return #boolean If true, proceed to onafterOpenfire. function ARTY:onafterNewTarget(Controllable, From, Event, To, target) self:_EventFromTo("onafterNewTarget", Event, From, To) @@ -2471,7 +2503,6 @@ end -- @param #string Event Event. -- @param #string To To state. -- @param #table move Array holding the move parameters. --- @return #boolean If true, proceed to onafterOpenfire. function ARTY:onafterNewMove(Controllable, From, Event, To, move) self:_EventFromTo("onafterNewTarget", Event, From, To) @@ -2688,6 +2719,7 @@ end -- @param #number Speed (Optional) Speed in km/h. Default is 70% of max speed the group can do. -- @param #boolean OnRoad If true, use (mainly) roads. function ARTY:_Move(group, ToCoord, Speed, OnRoad) + self:F2({group=group:GetName(), Speed=Speed, OnRoad=OnRoad}) -- Clear all tasks. group:ClearTasks() @@ -2722,28 +2754,31 @@ function ARTY:_Move(group, ToCoord, Speed, OnRoad) -- Route group on road if requested. if OnRoad then - - -- Path on road (only first and last points) - --local _first=cpini:GetClosestPointToRoad() - --local _last=ToCoord:GetClosestPointToRoad() + -- Get path on road. local _pathonroad=cpini:GetPathOnRoad(ToCoord) - local _first=_pathonroad[1] - local _last=_pathonroad[#_pathonroad] + + -- Check if we actually got a path. There are situations where nil is returned. In that case, we go directly. + if _pathonroad then + + -- Just take the first and last point. + local _first=_pathonroad[1] + local _last=_pathonroad[#_pathonroad] - if self.Debug then - _first:SmokeBlue() - _last:SmokeBlue() + if self.Debug then + _first:SmokeGreen() + _last:SmokeGreen() + end + + -- First point on road. + path[#path+1]=_first:WaypointGround(Speed, "On Road") + task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, false) + + -- Last point on road. + path[#path+1]=_last:WaypointGround(Speed, "On Road") + task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, false) end - -- First point on road. - path[#path+1]=_first:WaypointGround(Speed, "On Road") - task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, false) - - -- Last point on road. - path[#path+1]=_last:WaypointGround(Speed, "On Road") - task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, false) - end -- Last waypoint at ToCoord. @@ -2828,7 +2863,7 @@ function ARTY:_Relocate() -- Assign relocation. if _gotit then - self:AssignMoveCoord(_new, nil, nil, false, false) + self:AssignMoveCoord(_new, nil, nil, false, false, "RELOCATION MOVE AFTER FIRING") end end @@ -2901,30 +2936,13 @@ function ARTY:GetAmmo(display) MissileCategory=ammotable[w].desc.missileCategory end - local function missilecat(n) - local cat="unknown" - if n==1 then - cat="air-to-air" - elseif n==2 then - cat="surface-to-air" - elseif n==3 then - cat="ballistic" - elseif n==4 then - cat="anti-ship" - elseif n==5 then - cat="cruise" - elseif n==6 then - cat="other" - end - return cat - end -- Check for correct shell type. local _gotshell=false if #self.ammoshells>0 then -- User explicitly specified the valid type(s) of shells. for _,_type in pairs(self.ammoshells) do - if string.match(Tammo, _type) then + if string.match(Tammo, _type) and Category==Weapon.Category.SHELL then _gotshell=true end end @@ -2938,7 +2956,7 @@ function ARTY:GetAmmo(display) local _gotrocket=false if #self.ammorockets>0 then for _,_type in pairs(self.ammorockets) do - if string.match(Tammo, _type) then + if string.match(Tammo, _type) and Category==Weapon.Category.ROCKET then _gotrocket=true end end @@ -2952,7 +2970,7 @@ function ARTY:GetAmmo(display) local _gotmissile=false if #self.ammomissiles>0 then for _,_type in pairs(self.ammomissiles) do - if string.match(Tammo,_type) then + if string.match(Tammo,_type) and Category==Weapon.Category.MISSILE then _gotmissile=true end end @@ -2982,12 +3000,12 @@ function ARTY:GetAmmo(display) elseif _gotmissile then -- Add up all cruise missiles (category 5) - if MissileCategory==5 then + if MissileCategory==Weapon.MissileCategory.CRUISE then nmissiles=nmissiles+Nammo end -- Debug info. - text=text..string.format("- %d %s missiles of type %s\n", Nammo, missilecat(MissileCategory), Tammo) + text=text..string.format("- %d %s missiles of type %s\n", Nammo, self:_MissileCategoryName(MissileCategory), Tammo) else @@ -3016,6 +3034,29 @@ function ARTY:GetAmmo(display) return nammo, nshells, nrockets, nmissiles end +--- Returns a name of a missile category. +-- @param #ARTY self +-- @param #number categorynumber Number of missile category from weapon missile category enumerator. See https://wiki.hoggitworld.com/view/DCS_Class_Weapon +-- @return #string Missile category name. +function ARTY:_MissileCategoryName(categorynumber) + local cat="unknown" + if categorynumber==Weapon.MissileCategory.AAM then + cat="air-to-air" + elseif categorynumber==Weapon.MissileCategory.SAM then + cat="surface-to-air" + elseif categorynumber==Weapon.MissileCategory.BM then + cat="ballistic" + elseif categorynumber==Weapon.MissileCategory.ANTI_SHIP then + cat="anti-ship" + elseif categorynumber==Weapon.MissileCategory.CRUISE then + cat="cruise" + elseif categorynumber==Weapon.MissileCategory.OTHER then + cat="other" + end + return cat +end + + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Mark Functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -3111,6 +3152,7 @@ function ARTY:_Markertext(text) for _,key in pairs(keywords) do + -- Split keyphrase by space. First one is the key and second, ... the parameter(s) until the next comma. local s=self:_split(key, " ") local val=s[2] @@ -3516,6 +3558,40 @@ function ARTY:_CheckTargetsInRange() end end +--- Check all normal (untimed) targets and return the target with the highest priority which has been engaged the fewest times. +-- @param #ARTY self +-- @return #table Target which is due to be attacked now or nil if no target could be found. +function ARTY:_CheckNormalTargets() + self:F3() + + -- Sort targets w.r.t. prio and number times engaged already. + self:_SortTargetQueuePrio() + + -- No target engagements if rearming! + if self:is("Rearming") then + return nil + end + + -- Loop over all sorted targets. + for i=1,#self.targets do + local _target=self.targets[i] + + -- Debug info. + self:T3(ARTY.id..string.format("Check NORMAL target %d: %s", i, self:_TargetInfo(_target))) + + -- Check that target no time, is not under fire currently and in range. + if _target.underfire==false and _target.time==nil and _target.maxengage > _target.engaged and self:_TargetInRange(_target) and self:_CheckWeaponTypeAvailable(_target)>0 then + + -- Debug info. + self:T2(ARTY.id..string.format("Found NORMAL target %s", self:_TargetInfo(_target))) + + return _target + end + end + + return nil +end + --- Check all timed targets and return the target which should be attacked next. -- @param #ARTY self -- @return #table Target which is due to be attacked now. @@ -3528,6 +3604,11 @@ function ARTY:_CheckTimedTargets() -- Sort Targets wrt time. self:_SortQueueTime(self.targets) + -- No target engagements if rearming! + if self:is("Rearming") then + return nil + end + for i=1,#self.targets do local _target=self.targets[i] @@ -3535,7 +3616,7 @@ function ARTY:_CheckTimedTargets() self:T3(ARTY.id..string.format("Check TIMED target %d: %s", i, self:_TargetInfo(_target))) -- Check if target has an attack time which has already passed. Also check that target is not under fire already and that it is in range. - if _target.time and Tnow>=_target.time and _target.underfire==false and self:_TargetInRange(_target) then + if _target.time and Tnow>=_target.time and _target.underfire==false and self:_TargetInRange(_target) and self:_CheckWeaponTypeAvailable(_target)>0 then -- Check if group currently has a target and whether its priorty is lower than the timed target. if self.currentTarget then @@ -3550,6 +3631,7 @@ function ARTY:_CheckTimedTargets() return _target end end + end return nil @@ -3572,12 +3654,18 @@ function ARTY:_CheckMoves() if self.currentTarget then firing=true end - + + -- Loop over all moves in queue. for i=1,#self.moves do + + -- Shortcut. local _move=self.moves[i] - -- Check if time for move is reached. - if Tnow >= _move.time and (firing==false or _move.cancel) then + if string.find(_move.name, "REARMING MOVE") and ((self.currentMove and self.currentMove.name~=_move.name) or self.currentMove==nil) then + -- We got an rearming assignment which has priority. + return _move + elseif (Tnow >= _move.time) and (firing==false or _move.cancel) and (not self.currentMove) and (not self:is("Rearming")) then + -- Time for move is reached and maybe current target should be cancelled. return _move end end @@ -3585,35 +3673,6 @@ function ARTY:_CheckMoves() return nil end ---- Check all normal (untimed) targets and return the target with the highest priority which has been engaged the fewest times. --- @param #ARTY self --- @return #table Target which is due to be attacked now or nil if no target could be found. -function ARTY:_CheckNormalTargets() - self:F3() - - -- Sort targets w.r.t. prio and number times engaged already. - self:_SortTargetQueuePrio() - - -- Loop over all sorted targets. - for i=1,#self.targets do - local _target=self.targets[i] - - -- Debug info. - self:T3(ARTY.id..string.format("Check NORMAL target %d: %s", i, self:_TargetInfo(_target))) - - -- Check that target no time, is not under fire currently and in range. - if _target.underfire==false and _target.time==nil and _target.maxengage > _target.engaged and self:_TargetInRange(_target) then - - -- Debug info. - self:T2(ARTY.id..string.format("Found NORMAL target %s", self:_TargetInfo(_target))) - - return _target - end - end - - return nil -end - --- Check whether shooting started within a certain time (~5 min). If not, the current target is considered invalid and removed from the target list. -- @param #ARTY self function ARTY:_CheckShootingStarted() @@ -3691,6 +3750,66 @@ function ARTY:_GetMoveIndexByName(name) return nil end +--- Check if a selected weapon type is available for this target, i.e. if the current amount of ammo of this weapon type is currently available. +-- @param #ARTY self +-- @param #boolean target Target array data structure. +-- @return #number Amount of shells, rockets or missiles available of the weapon type selected for the target. +function ARTY:_CheckWeaponTypeAvailable(target) + + -- Get current ammo of group. + local Nammo, Nshells, Nrockets, Nmissiles=self:GetAmmo() + + -- Check if enough ammo is there for the selected weapon type. + local nfire=Nammo + if target.weapontype==ARTY.WeaponType.Auto then + nfire=Nammo + elseif target.weapontype==ARTY.WeaponType.Cannon then + nfire=Nshells + elseif target.weapontype==ARTY.WeaponType.TacticalNukes then + nfire=self.Nukes + elseif target.weapontype==ARTY.WeaponType.Rockets then + nfire=Nrockets + elseif target.weapontype==ARTY.WeaponType.UnguidedAny then + nfire=Nshells+Nrockets + elseif target.weapontype==ARTY.WeaponType.GuidedMissile then + nfire=Nmissiles + elseif target.weapontype==ARTY.WeaponType.CruiseMissile then + nfire=Nmissiles + elseif target.weapontype==ARTY.WeaponType.AntiShipMissile then + nfire=Nmissiles + end + + return nfire +end +--- Check if a selected weapon type is in principle possible for this group. The current amount of ammo might be zero but the group still can be rearmed at a later point in time. +-- @param #ARTY self +-- @param #boolean target Target array data structure. +-- @return #boolean True if the group can carry this weapon type, false otherwise. +function ARTY:_CheckWeaponTypePossible(target) + + -- Check if enough ammo is there for the selected weapon type. + local possible=false + if target.weapontype==ARTY.WeaponType.Auto then + possible=self.Nammo0>0 + elseif target.weapontype==ARTY.WeaponType.Cannon then + possible=self.Nshells0>0 + elseif target.weapontype==ARTY.WeaponType.TacticalNukes then + possible=self.Nukes0>0 + elseif target.weapontype==ARTY.WeaponType.Rockets then + possible=self.Nrockets0>0 + elseif target.weapontype==ARTY.WeaponType.UnguidedAny then + possible=self.Nshells0+self.Nrockets0>0 + elseif target.weapontype==ARTY.WeaponType.GuidedMissile then + possible=self.Nmissiles0>0 + elseif target.weapontype==ARTY.WeaponType.CruiseMissile then + possible=self.Nmissiles0>0 + elseif target.weapontype==ARTY.WeaponType.AntiShipMissile then + possible=self.Nmissiles0>0 + end + + return possible +end + --- Check if a name is unique. If not, a new unique name can be created by adding a running index #01, #02, ... -- @param #ARTY self -- @param #table givennames Table with entries of already given names. Must contain a .name item. From e8ff1534273d35d9f8689af8d4c53339d288476b Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sun, 10 Jun 2018 00:05:55 +0200 Subject: [PATCH 166/420] ARTY v0.9.96 Improved marker logic. --- .../Moose/Functional/Artillery.lua | 366 +++++++++--------- 1 file changed, 191 insertions(+), 175 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index aa9a043c2..27e1a2d6a 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -55,7 +55,6 @@ -- @field #number Nrockets0 Initial amount of rockets of the whole group. -- @field #number Nmissiles0 Initial amount of missiles of the whole group. -- @field #number Nukes0 Initial amount of tactical nukes of the whole group. Default is 0. --- @field #number FullAmmo Full amount of all ammunition taking the number of alive units into account. -- @field #number StatusInterval Update interval in seconds between status updates. Default 10 seconds. -- @field #number WaitForShotTime Max time in seconds to wait until fist shot event occurs after target is assigned. If time is passed without shot, the target is deleted. Default is 300 seconds. -- @field #table DCSdesc DCS descriptors of the ARTY group. @@ -63,6 +62,8 @@ -- @field #string DisplayName Extended type name of the ARTY group. -- @field #number IniGroupStrength Inital number of units in the ARTY group. -- @field #boolean IsArtillery If true, ARTY group has attribute "Artillery". This is automatically derived from the DCS descriptor table. +-- @field #boolean ismobile If true, ARTY group can move. +-- @field #string alias Name of the ARTY group. -- @field #number SpeedMax Maximum speed of ARTY group in km/h. This is determined from the DCS descriptor table. -- @field #number Speed Default speed in km/h the ARTY group moves at. Maximum speed possible is 80% of maximum speed the group can do. -- @field #number RearmingDistance Safe distance in meters between ARTY group and rearming group or place at which rearming is possible. Default 100 m. @@ -221,8 +222,6 @@ -- * @{#ARTY.WeaponType}.Auto: Automatic weapon selection by the DCS logic. This is the default setting. -- * @{#ARTY.WeaponType}.Cannon: Only cannons are used during the attack. Corresponding ammo type are shells and can be defined by @{#ARTY.SetShellTypes}. -- * @{#ARTY.WeaponType}.Rockets: Only unguided are used during the attack. Corresponding ammo type are rockets/nurs and can be defined by @{#ARTY.SetRocketTypes}. --- * @{#ARTY.WeaponType}.UnguidedAny: Any unguided weapon (cannons or rockes) will be used. --- * @{#ARTY.WeaponType}.GuidedMissile: Any guided missiles are used during the attack. Corresponding ammo type are missiles and can be defined by @{#ARTY.SetMissileTypes}. -- * @{#ARTY.WeaponType}.CruiseMissile: Only cruise missiles are used during the attack. Corresponding ammo type are missiles and can be defined by @{#ARTY.SetMissileTypes}. -- * @{#ARTY.WeaponType}.TacticalNukes: Use tactical nuclear shells. This works only with units that have shells and is described below. -- @@ -294,6 +293,8 @@ -- * *weapon* Type of weapon to be used. Valid parameters are *cannon*, *rocket*, *missile*, *nuke*. Default is automatic selection. -- * *battery* Name of the ARTY group that the target is assigned to. Note that **the name is case sensitive** and has to be given in quotation marks. Default is all ARTY groups of the right coalition. -- * *key* A number to authorize the target assignment. Only specifing the correct number will trigger an engagement. +-- * *lldms* Specify the coordinates in Lat/Long degrees, minutes and seconds format. The actual location of the marker is unimportant here. The group will engage the coordinates given in the lldms keyword. +-- Format is DD:MM:SS[N,S] DD:MM:SS[W,E]. See example below. This can be useful when coordinates in this format are obtained from elsewhere. -- * *readonly* The marker is readonly and cannot be deleted by users. Hence, assignment cannot be cancelled by removing the marker. -- -- Here are examples of valid marker texts: @@ -302,6 +303,7 @@ -- arty engage, battery "Blue Paladin 1" "Blue MRLS 1", shots 10, time 10:15 -- arty engage, battery "Blue MRLS 1", key 666 -- arty engage, battery "Paladin Alpha", weapon nukes, shots 1, time 20:15 +-- arty engage, lldms 41:51:00N 41:47:58E -- -- Note that the keywords and parameters are *case insensitve*. Only exception are the battery group names. These must be exactly the same as the names of the goups defined -- in the mission editor. @@ -316,6 +318,8 @@ -- * *canceltarget* Group will cancel all running firing engagements and immidiately start to move. Default is that group will wait until is current assignment is over. -- * *battery* Name of the ARTY group that the relocation is assigned to. -- * *key* A number to authorize the target assignment. Only specifing the correct number will trigger an engagement. +-- * *lldms* Specify the coordinates in Lat/Long degrees, minutes and seconds format. The actual location of the marker is unimportant. The group will move to the coordinates given in the lldms keyword. +-- Format is DD:MM:SS[N,S] DD:MM:SS[W,E]. See example below. -- * *readonly* Marker cannot be deleted by users any more. Hence, assignment cannot be cancelled by removing the marker. -- -- Here are some examples: @@ -323,18 +327,9 @@ -- arty move, time 23:45, speed 50, on road -- arty move, battery "Blue Paladin" -- arty move, battery "Blue MRLS", canceltarget, speed 10, on road +-- arty move, lldms 41:51:00N 41:47:58E -- --- ### Coordinate Independent Commands --- --- There are a couple of commands, which are independent of the position where the marker is placed. --- These commands are --- arty move, cancelcurrent --- which will cancel the current relocation movement. Of course, this can be combined with the *battery* keyword to address a certain battery. --- Same goes for targets, e.g. --- arty engage, battery "Paladin Alpha", cancelcurrent --- which will cancel all running firing tasks. --- --- ### General Requests +-- ### Requests -- -- Marks can also be to send requests to the ARTY group. This is done by the keyword **arty request**, which can have the keywords -- @@ -347,6 +342,17 @@ -- arty request, battery "Paladin Bravo", targets -- arty request, battery "MRLS Charly", move -- +-- The actual location of the marker is irrelevant for these requests. +-- +-- ### Cancel +-- +-- Current actions can be cancelled by the keyword **arty cancel**. Actions that can be cancelled are current engagements, relocations and rearming assignments. +-- +-- For example +-- arty cancel, target, battery "Paladin Bravo" +-- arty cancel, move +-- arty cancel, rearming, battery "MRLS Charly" +-- -- -- ## Fine Tuning -- @@ -444,13 +450,13 @@ ARTY={ Nrockets0=0, Nmissiles0=0, Nukes0=0, - FullAmmo=0, - defaultROE="weapon_hold", StatusInterval=10, WaitForShotTime=300, DCSdesc=nil, Type=nil, DisplayName=nil, + alias=nil, + ismobile=true, IniGroupStrength=0, IsArtillery=nil, RearmingDistance=100, @@ -490,10 +496,10 @@ ARTY.WeaponType={ Auto=1073741822, Cannon=805306368, Rockets=30720, - UnguidedAny=805339120, - GuidedMissile=268402688, + --UnguidedAny=805339120, + --GuidedMissile=268402688, CruiseMissile=2097152, - AntiShipMissile=65536, + --AntiShipMissile=65536, TacticalNukes=666, } @@ -563,7 +569,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #string version -ARTY.version="0.9.95" +ARTY.version="0.9.96" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -596,9 +602,9 @@ ARTY.version="0.9.95" --- Creates a new ARTY object. -- @param #ARTY self -- @param Wrapper.Group#GROUP group The GROUP object for which artillery tasks should be assigned. --- @return #ARTY ARTY object. --- @return nil If group does not exist or is not a ground or naval group. -function ARTY:New(group) +-- @param alias (Optional) Alias name the group will be calling itself when sending messages. +-- @return #ARTY ARTY object or nil if group does not exist or is not a ground or naval group. +function ARTY:New(group, alias) BASE:F2(group) -- Inherits from FSM_CONTROLLABLE @@ -616,11 +622,17 @@ function ARTY:New(group) if group:IsGround()==false and group:IsShip()==false then self:E(ARTY.id..string.format("ERROR: ARTY group %s has to be a GROUND or SHIP group!", group:GetName())) return nil - end + end -- Set the controllable for the FSM. self:SetControllable(group) + if alias~=nil then + self.alias=tostring(alias) + else + self.alias=group:GetName() + end + -- Set the initial coordinates of the ARTY group. self.InitialCoord=group:GetCoordinate() @@ -628,9 +640,7 @@ function ARTY:New(group) local DCSgroup=Group.getByName(group:GetName()) local DCSunit=DCSgroup:getUnit(1) self.DCSdesc=DCSunit:getDesc() - - --self.DCSdesc=group:GetDesc() - + -- DCS descriptors. self:T3(ARTY.id.."DCS descriptors for group "..group:GetName()) for id,desc in pairs(self.DCSdesc) do @@ -640,6 +650,13 @@ function ARTY:New(group) -- Maximum speed in km/h. self.SpeedMax=group:GetSpeedMax() + -- Group is mobile or not (e.g. mortars). + if self.SpeedMax>1 then + self.ismobile=true + else + self.ismobile=false + end + -- Set speed to 0.7 of maximum. self.Speed=self.SpeedMax * 0.7 @@ -790,6 +807,12 @@ end -- @return #string Name of the move. Can be used for further reference, e.g. deleting the move from the list. function ARTY:AssignMoveCoord(coord, time, speed, onroad, cancel, name, unique) self:F({coord=coord, time=time, speed=speed, onroad=onroad, cancel=cancel, name=name, unique=unique}) + + -- Reject move if the group is immobile. + if not self.ismobile then + self:T(ARTY.id..string.format("%s: group is immobile. Rejecting move request!", self.Controllable:GetName())) + return nil + end -- Default if unique==nil then @@ -1165,6 +1188,13 @@ function ARTY:onafterStart(Controllable, From, Event, To) end end + -- Some mobility consitency checks if group cannot move. + if not self.ismobile then + self.RearmingPlaceCoord=nil + self.relocateafterfire=false + self.autorelocate=false + end + local text=string.format("\n******************************************************\n") text=text..string.format("Arty group = %s\n", Controllable:GetName()) text=text..string.format("Artillery attribute = %s\n", tostring(self.IsArtillery)) @@ -1173,6 +1203,7 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("Number of units = %d\n", self.IniGroupStrength) text=text..string.format("Speed max = %d km/h\n", self.SpeedMax) text=text..string.format("Speed default = %d km/h\n", self.Speed) + text=text..string.format("Is mobile = %s\n", tostring(self.ismobile)) text=text..string.format("Min range = %.1f km\n", self.minrange/1000) text=text..string.format("Max range = %.1f km\n", self.maxrange/1000) text=text..string.format("Total ammo count = %d\n", self.Nammo0) @@ -1358,7 +1389,7 @@ function ARTY:_OnEventShot(EventData) self.Nshots=self.Nshots+1 -- Debug output. - local text=string.format("%s, fired shot %d of %d with weapon %s on target %s.", self.Controllable:GetName(), self.Nshots, self.currentTarget.nshells, _weaponName, self.currentTarget.name) + local text=string.format("%s, fired shot %d of %d with weapon %s on target %s.", self.alias, self.Nshots, self.currentTarget.nshells, _weaponName, self.currentTarget.name) self:T(ARTY.id..text) MESSAGE:New(text, 5):Clear():ToAllIf(self.report or self.Debug) @@ -1427,7 +1458,7 @@ function ARTY:_OnEventShot(EventData) self:T(ARTY.id..string.format("Group %s ammo: total=%d, shells=%d, rockets=%d, missiles=%d", self.Controllable:GetName(), _nammo, _nshells, _nrockets, _nmissiles)) self:T(ARTY.id..string.format("Group %s uses weapontype %s for current target.", self.Controllable:GetName(), _weapontype)) - + -- Default switches for cease fire and relocation. local _ceasefire=false local _relocate=false @@ -1474,66 +1505,6 @@ function ARTY:_OnEventShot(EventData) end end ---- Check if group is (partly) out of ammo of a special weapon type. --- @param #ARTY self --- @param #table targets Table of targets. --- @return @boolean True if any target requests a weapon type that is empty. -function ARTY:_CheckOutOfAmmo(targets) - - -- Get current ammo. - local _nammo,_nshells,_nrockets,_nmissiles=self:GetAmmo() - - -- Special weapon type requested ==> Check if corresponding ammo is empty. - local _partlyoutofammo=false - - for _,Target in pairs(targets) do - - if Target.weapontype==ARTY.WeaponType.Auto and _nammo==0 then - - self:T(ARTY.id..string.format("Group %s, auto weapon requested for target %s but all ammo is empty.", self.Controllable:GetName(), Target.name)) - _partlyoutofammo=true - - elseif Target.weapontype==ARTY.WeaponType.Cannon and _nshells==0 then - - self:T(ARTY.id..string.format("Group %s, cannons requested for target %s but shells empty.", self.Controllable:GetName(), Target.name)) - _partlyoutofammo=true - - elseif Target.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes<=0 then - - self:T(ARTY.id..string.format("Group %s, tactical nukes requested for target %s but nukes empty.", self.Controllable:GetName(), Target.name)) - _partlyoutofammo=true - - elseif Target.weapontype==ARTY.WeaponType.Rockets and _nrockets==0 then - - self:T(ARTY.id..string.format("Group %s, rockets requested for target %s but rockets empty.", self.Controllable:GetName(), Target.name)) - _partlyoutofammo=true - - elseif Target.weapontype==ARTY.WeaponType.UnguidedAny and _nshells+_nrockets==0 then - - self:T(ARTY.id..string.format("Group %s, unguided weapon requested for target %s but shells AND rockets empty.", self.Controllable:GetName(), Target.name)) - _partlyoutofammo=true - - elseif Target.weapontype==ARTY.WeaponType.GuidedMissile and _nmissiles==0 then - - self:T(ARTY.id..string.format("Group %s, guided missiles requested for target %s but all missiles empty.", self.Controllable:GetName(), Target.name)) - _partlyoutofammo=true - - elseif Target.weapontype==ARTY.WeaponType.CruiseMissile and _nmissiles==0 then - - self:T(ARTY.id..string.format("Group %s, cruise missiles requested for target %s but all missiles empty.", self.Controllable:GetName(), Target.name)) - _partlyoutofammo=true - - elseif Target.weapontype==ARTY.WeaponType.AntiShipMissile and _nmissiles==0 then - - self:T(ARTY.id..string.format("Group %s, anti-ship missiles requested for target %s but all missiles empty.", self.Controllable:GetName(), Target.name)) - _partlyoutofammo=true - - end - - end - - return _partlyoutofammo -end --- After "Start" event. Initialized ROE and alarm state. Starts the event handler. -- @param #ARTY self @@ -1670,7 +1641,7 @@ function ARTY:_OnEventMarkChange(Event) local _assign=self:_Markertext(Event.text) -- Check if ENGAGE or MOVE or REQUEST keywords were found. - if _assign==nil or not (_assign.engage or _assign.move or _assign.request) then + if _assign==nil or not (_assign.engage or _assign.move or _assign.request or _assign.cancel) then return end @@ -1716,13 +1687,20 @@ function ARTY:_OnEventMarkChange(Event) end -- Cancel current target and return. - if _assign.cancelcurrent and _validkey then - if _assign.move and self.currentMove then + if _assign.cancel and _validkey then + if _assign.cancelmove and self.currentMove then self.Controllable:ClearTasks() self:Arrived() - end - if _assign.engage and self.currentTarget then + elseif _assign.canceltarget and self.currentTarget then + self.currentTarget.engaged=self.currentTarget.engaged+1 self:CeaseFire(self.currentTarget) + elseif _assign.cancelrearm and self.is("Rearming") then + local nammo=self:GetAmmo() + if nammo>0 then + self:Rearmed() + else + self:Winchester() + end end return end @@ -1761,7 +1739,7 @@ function ARTY:_OnEventMarkChange(Event) MESSAGE:New(text, 10):ToCoalitionIf(batterycoalition, self.report or self.Debug) -- Assign a relocation of the arty group. - local _movename=self:AssignMoveCoord(_coord, _assign.time, _assign.speed, _assign.onroad, _assign.canceltarget,_name, true) + local _movename=self:AssignMoveCoord(_coord, _assign.time, _assign.speed, _assign.onroad, _assign.movecanceltarget,_name, true) if _movename~=nil then local _mid=self:_GetMoveIndexByName(_movename) @@ -2087,18 +2065,9 @@ function ARTY:onafterOpenFire(Controllable, From, Event, To, target) elseif target.weapontype==ARTY.WeaponType.Rockets then nfire=Nrockets _type="rockets" - elseif target.weapontype==ARTY.WeaponType.UnguidedAny then - nfire=Nshells+Nrockets - _type="shells or rockets" - elseif target.weapontype==ARTY.WeaponType.GuidedMissile then - nfire=Nmissiles - _type="guided missiles" elseif target.weapontype==ARTY.WeaponType.CruiseMissile then nfire=Nmissiles _type="cruise missiles" - elseif target.weapontype==ARTY.WeaponType.AntiShipMissile then - nfire=Nmissiles - _type="anti-ship missiles" end -- Adjust if less than requested ammo is left. @@ -2165,6 +2134,8 @@ function ARTY:onafterCeaseFire(Controllable, From, Event, To, target) -- Clear tasks. self.Controllable:ClearTasks() + else + self:E(ARTY.id.."ERROR: No target in cease fire for group %s.", self.Controllable:GetName()) end -- Set number of shots to zero. @@ -2365,20 +2336,21 @@ function ARTY:_CheckRearmed() end -- Full Ammo count. - self.FullAmmo=self.Nammo0 * nunits / self.IniGroupStrength + local FullAmmo=self.Nammo0 * nunits / self.IniGroupStrength -- Rearming status in per cent. - local _rearmpc=nammo/self.FullAmmo*100 + local _rearmpc=nammo/FullAmmo*100 -- Send message if rearming > 1% complete if _rearmpc>1 then - local text=string.format("%s, rearming %d %% complete. nammo=%d , fullammo=%d", self.Controllable:GetName(), _rearmpc, nammo, self.FullAmmo) + local text=string.format("%s, rearming %d %% complete.", self.alias, _rearmpc) self:T(ARTY.id..text) MESSAGE:New(text, 10):ToCoalitionIf(self.Controllable:GetCoalition(), self.report or self.Debug) end - -- Return if ammo is full. Strangely, I got the case that a Paladin got one more shell than it can max carry, i.e. 40 not 39 when rearming when it still had some ammo left. - if nammo>=self.FullAmmo then + -- Return if ammo is full. + -- TODO: Strangely, I got the case that a Paladin got one more shell than it can max carry, i.e. 40 not 39 when rearming when it still had some ammo left. Need to report. + if nammo>=FullAmmo then return true else return false @@ -2402,7 +2374,7 @@ function ARTY:onbeforeMove(Controllable, From, Event, To, move) self:_EventFromTo("onbeforeMove", Event, From, To) -- Check if group can actually move... - if self.SpeedMax<1 then + if not self.ismobile then return false end @@ -2528,10 +2500,7 @@ function ARTY:onafterDead(Controllable, From, Event, To) if units~=nil then nunits=#units end - - -- Adjust full ammo count - self.FullAmmo=self.Nammo0*nunits/self.IniGroupStrength - + -- Message. local text=string.format("%s, one of our units just died! %d units left.", self.Controllable:GetName(), nunits) MESSAGE:New(text, 5):ToAllIf(self.Debug) @@ -2785,10 +2754,10 @@ function ARTY:_Move(group, ToCoord, Speed, OnRoad) path[#path+1]=ToCoord:WaypointGround(Speed, formation) task[#task+1]=group:TaskFunction("ARTY._PassingWaypoint", self, #path-1, true) - if self.Debug then - cpini:SmokeBlue() - ToCoord:SmokeBlue() - end + --if self.Debug then + -- cpini:SmokeBlue() + -- ToCoord:SmokeBlue() + --end -- Init waypoints of the group. local Waypoints={} @@ -2927,6 +2896,9 @@ function ARTY:GetAmmo(display) -- Typename of current weapon local Tammo=ammotable[w]["desc"]["typeName"] + local _weaponString = self:_split(Tammo,"%.") + local _weaponName = _weaponString[#_weaponString] + -- Get the weapon category: shell=0, missile=1, rocket=2, bomb=3 local Category=ammotable[w].desc.category @@ -2987,7 +2959,7 @@ function ARTY:GetAmmo(display) nshells=nshells+Nammo -- Debug info. - text=text..string.format("- %d shells of type %s\n", Nammo, Tammo) + text=text..string.format("- %d shells of type %s\n", Nammo, _weaponName) elseif _gotrocket then @@ -2995,7 +2967,7 @@ function ARTY:GetAmmo(display) nrockets=nrockets+Nammo -- Debug info. - text=text..string.format("- %d rockets of type %s\n", Nammo, Tammo) + text=text..string.format("- %d rockets of type %s\n", Nammo, _weaponName) elseif _gotmissile then @@ -3005,7 +2977,7 @@ function ARTY:GetAmmo(display) end -- Debug info. - text=text..string.format("- %d %s missiles of type %s\n", Nammo, self:_MissileCategoryName(MissileCategory), Tammo) + text=text..string.format("- %d %s missiles of type %s\n", Nammo, self:_MissileCategoryName(MissileCategory), _weaponName) else @@ -3130,9 +3102,12 @@ function ARTY:_Markertext(text) assignment.move=false assignment.engage=false assignment.request=false + assignment.cancel=false assignment.readonly=false + assignment.movecanceltarget=false + assignment.cancelmove=false assignment.canceltarget=false - assignment.cancelcurrent=false + assignment.cancelrearm=false -- Check for correct keywords. if text:lower():find("arty engage") or text:lower():find("arty attack") then @@ -3140,9 +3115,11 @@ function ARTY:_Markertext(text) elseif text:lower():find("arty move") or text:lower():find("arty relocate") then assignment.move=true elseif text:lower():find("arty request") then - assignment.request=true + assignment.request=true + elseif text:lower():find("arty cancel") then + assignment.cancel=true else - self:E(ARTY.id..'ERROR: Neither "ARTY ENGAGE" nor "ARTY MOVE" nor "ARTY RELOCATE" nor "ARTY REQUEST" keyword specified!') + self:E(ARTY.id..'ERROR: Neither "ARTY ENGAGE" nor "ARTY MOVE" nor "ARTY RELOCATE" nor "ARTY REQUEST" nor "ARTY CANCEL" keyword specified!') return nil end @@ -3150,11 +3127,12 @@ function ARTY:_Markertext(text) local keywords=self:_split(text, ",") self:T({keywords=keywords}) - for _,key in pairs(keywords) do + for _,keyphrase in pairs(keywords) do -- Split keyphrase by space. First one is the key and second, ... the parameter(s) until the next comma. - local s=self:_split(key, " ") - local val=s[2] + local str=self:_split(keyphrase, " ") + local key=str[1] + local val=str[2] -- Battery name, i.e. which ARTY group should fire. if key:lower():find("battery") then @@ -3177,7 +3155,7 @@ function ARTY:_Markertext(text) elseif assignment.engage and key:lower():find("shot") then - assignment.nshells=tonumber(s[2]) + assignment.nshells=tonumber(val) self:T(ARTY.id..string.format("Key Shot=%s.", val)) elseif assignment.engage and key:lower():find("prio") then @@ -3202,7 +3180,7 @@ function ARTY:_Markertext(text) elseif val:lower():find("rocket") then assignment.weapontype=ARTY.WeaponType.Rockets elseif val:lower():find("missile") then - assignment.weapontype=ARTY.WeaponType.GuidedMissile + assignment.weapontype=ARTY.WeaponType.CruiseMissile elseif val:lower():find("nuke") then assignment.weapontype=ARTY.WeaponType.TacticalNukes else @@ -3225,16 +3203,11 @@ function ARTY:_Markertext(text) assignment.readonly=true self:T2(ARTY.id..string.format("Key Readonly=true.")) - elseif assignment.move and key:lower():find("canceltarget") then + elseif assignment.move and (key:lower():find("cancel target") or key:lower():find("cancel target")) then - assignment.canceltarget=true + assignment.movecanceltarget=true self:T2(ARTY.id..string.format("Key Cancel Target (before move)=true.")) - elseif (assignment.engage or assignment.move) and key:lower():find("cancelcurrent") then - - assignment.cancelcurrent=true - self:T2(ARTY.id..string.format("Key Cancel Current=true.")) - elseif assignment.request and key:lower():find("rearm") then assignment.requestrearming=true @@ -3259,20 +3232,35 @@ function ARTY:_Markertext(text) assignment.requestmoves=true self:T2(ARTY.id..string.format("Key Request Moves=true.")) - + + elseif assignment.cancel and (key:lower():find("engagement") or key:lower():find("attack") or key:lower():find("target")) then + + assignment.canceltarget=true + self:T2(ARTY.id..string.format("Key Cancel Target=true.")) + + elseif assignment.cancel and (key:lower():find("move") or key:lower():find("relocation")) then + + assignment.cancelmove=true + self:T2(ARTY.id..string.format("Key Cancel Move=true.")) + + elseif assignment.cancel and key:lower():find("rearm") then + + assignment.cancelrearm=true + self:T2(ARTY.id..string.format("Key Cancel Rearm=true.")) + elseif key:lower():find("lldms") then local _flat = "%d+:%d+:%d+%s*[N,S]" local _flon = "%d+:%d+:%d+%s*[W,E]" - local _lat=key:match(_flat) - local _lon=key:match(_flon) - self:T2(ARTY.id..string.format("Key LLDMS: lat=%s, long=%s", _lat,_lon)) + local _lat=keyphrase:match(_flat) + local _lon=keyphrase:match(_flon) + self:T2(ARTY.id..string.format("Key LLDMS: lat=%s, long=%s format=DMS", _lat,_lon)) if _lat and _lon then -- Convert DMS string to DD numbers format. local _latitude, _longitude=self:_LLDMS2DD(_lat, _lon) - self:T2(ARTY.id..string.format("Key LLDMS: lat=%.3f, long=%.3f", _latitude,_longitude)) + self:T2(ARTY.id..string.format("Key LLDMS: lat=%.3f, long=%.3f format=DD", _latitude,_longitude)) -- Convert LL to coordinate object. if _latitude and _longitude then @@ -3464,11 +3452,11 @@ function ARTY:_CheckTargetsInRange() for i=1,#self.targets do local _target=self.targets[i] - self:T(ARTY.id..string.format("Before: Target %s - in range = %s", _target.name, tostring(_target.inrange))) + self:T3(ARTY.id..string.format("Before: Target %s - in range = %s", _target.name, tostring(_target.inrange))) -- Check if target is in range. local _inrange,_toofar,_tooclose=self:_TargetInRange(_target) - self:T(ARTY.id..string.format("Inbetw: Target %s - in range = %s, toofar = %s, tooclose = %s", _target.name, tostring(_target.inrange), tostring(_toofar), tostring(_tooclose))) + self:T3(ARTY.id..string.format("Inbetw: Target %s - in range = %s, toofar = %s, tooclose = %s", _target.name, tostring(_target.inrange), tostring(_toofar), tostring(_tooclose))) -- Init default for assigning moves into range. local _movetowards=false @@ -3502,7 +3490,7 @@ function ARTY:_CheckTargetsInRange() if _inrange then -- Inform coalition that target is now in range. - local text=string.format("%s, target %s is now in range.", self.Controllable:GetName(), _target.name) + local text=string.format("%s, target %s is now in range.", self.alias, _target.name) self:T(ARTY.id..text) MESSAGE:New(text,10):ToCoalitionIf(self.Controllable:GetCoalition(), self.report or self.Debug) end @@ -3528,15 +3516,15 @@ function ARTY:_CheckTargetsInRange() local _waytogo=_dist-self.maxrange+_safetymargin local _heading=self:_GetHeading(_from,_target.coord) _tocoord=_from:Translate(_waytogo, _heading) - _name=string.format("Relocation to within max firing range of target %s", _target.name) + _name=string.format("%s, relocation to within max firing range of target %s", self.alias, _target.name) elseif _moveaway then - -- Target was in range on previous check but now we are too far away. - local _waytogo=_dist-self.minrange+_safetymargin - local _heading=self:_GetHeading(_target.coord,_from) - _tocoord=_from:Translate(_waytogo, _heading) - _name=string.format("Relocation to within min firing range of target %s", _target.name) + -- Target was in range on previous check but now we are too far away. + local _waytogo=_dist-self.minrange+_safetymargin + local _heading=self:_GetHeading(_target.coord,_from) + _tocoord=_from:Translate(_waytogo, _heading) + _name=string.format("%s, relocation to within min firing range of target %s", self.alias, _target.name) end @@ -3553,7 +3541,7 @@ function ARTY:_CheckTargetsInRange() -- Update value. _target.inrange=_inrange - self:T(ARTY.id..string.format("After: Target %s - in range = %s", _target.name, tostring(_target.inrange))) + self:T3(ARTY.id..string.format("After: Target %s - in range = %s", _target.name, tostring(_target.inrange))) end end @@ -3750,6 +3738,52 @@ function ARTY:_GetMoveIndexByName(name) return nil end +--- Check if group is (partly) out of ammo of a special weapon type. +-- @param #ARTY self +-- @param #table targets Table of targets. +-- @return @boolean True if any target requests a weapon type that is empty. +function ARTY:_CheckOutOfAmmo(targets) + + -- Get current ammo. + local _nammo,_nshells,_nrockets,_nmissiles=self:GetAmmo() + + -- Special weapon type requested ==> Check if corresponding ammo is empty. + local _partlyoutofammo=false + + for _,Target in pairs(targets) do + + if Target.weapontype==ARTY.WeaponType.Auto and _nammo==0 then + + self:T(ARTY.id..string.format("Group %s, auto weapon requested for target %s but all ammo is empty.", self.Controllable:GetName(), Target.name)) + _partlyoutofammo=true + + elseif Target.weapontype==ARTY.WeaponType.Cannon and _nshells==0 then + + self:T(ARTY.id..string.format("Group %s, cannons requested for target %s but shells empty.", self.Controllable:GetName(), Target.name)) + _partlyoutofammo=true + + elseif Target.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes<=0 then + + self:T(ARTY.id..string.format("Group %s, tactical nukes requested for target %s but nukes empty.", self.Controllable:GetName(), Target.name)) + _partlyoutofammo=true + + elseif Target.weapontype==ARTY.WeaponType.Rockets and _nrockets==0 then + + self:T(ARTY.id..string.format("Group %s, rockets requested for target %s but rockets empty.", self.Controllable:GetName(), Target.name)) + _partlyoutofammo=true + + elseif Target.weapontype==ARTY.WeaponType.CruiseMissile and _nmissiles==0 then + + self:T(ARTY.id..string.format("Group %s, cruise missiles requested for target %s but all missiles empty.", self.Controllable:GetName(), Target.name)) + _partlyoutofammo=true + + end + + end + + return _partlyoutofammo +end + --- Check if a selected weapon type is available for this target, i.e. if the current amount of ammo of this weapon type is currently available. -- @param #ARTY self -- @param #boolean target Target array data structure. @@ -3769,14 +3803,8 @@ function ARTY:_CheckWeaponTypeAvailable(target) nfire=self.Nukes elseif target.weapontype==ARTY.WeaponType.Rockets then nfire=Nrockets - elseif target.weapontype==ARTY.WeaponType.UnguidedAny then - nfire=Nshells+Nrockets - elseif target.weapontype==ARTY.WeaponType.GuidedMissile then - nfire=Nmissiles elseif target.weapontype==ARTY.WeaponType.CruiseMissile then nfire=Nmissiles - elseif target.weapontype==ARTY.WeaponType.AntiShipMissile then - nfire=Nmissiles end return nfire @@ -3797,14 +3825,8 @@ function ARTY:_CheckWeaponTypePossible(target) possible=self.Nukes0>0 elseif target.weapontype==ARTY.WeaponType.Rockets then possible=self.Nrockets0>0 - elseif target.weapontype==ARTY.WeaponType.UnguidedAny then - possible=self.Nshells0+self.Nrockets0>0 - elseif target.weapontype==ARTY.WeaponType.GuidedMissile then - possible=self.Nmissiles0>0 elseif target.weapontype==ARTY.WeaponType.CruiseMissile then possible=self.Nmissiles0>0 - elseif target.weapontype==ARTY.WeaponType.AntiShipMissile then - possible=self.Nmissiles0>0 end return possible @@ -3904,11 +3926,11 @@ function ARTY:_TargetInRange(target, message) if _dist < self.minrange then _inrange=false _tooclose=true - text=string.format("%s, target is out of range. Distance of %.1f km is below min range of %.1f km.", self.Controllable:GetName(), _dist/1000, self.minrange/1000) + text=string.format("%s, target is out of range. Distance of %.1f km is below min range of %.1f km.", self.alias, _dist/1000, self.minrange/1000) elseif _dist > self.maxrange then _inrange=false _toofar=true - text=string.format("%s, target is out of range. Distance of %.1f km is greater than max range of %.1f km.", self.Controllable:GetName(), _dist/1000, self.maxrange/1000) + text=string.format("%s, target is out of range. Distance of %.1f km is greater than max range of %.1f km.", self.alias, _dist/1000, self.maxrange/1000) end -- Debug output. @@ -3918,7 +3940,7 @@ function ARTY:_TargetInRange(target, message) end -- Remove target if ARTY group cannot move, e.g. Mortas. No chance to be ever in range. - if self.SpeedMax<1 and _inrange==false then + if not self.ismobile and _inrange==false then self:RemoveTarget(target.name) end @@ -3938,14 +3960,8 @@ function ARTY:_WeaponTypeName(tnumber) name="Cannons" elseif tnumber==ARTY.WeaponType.Rockets then name="Rockets" - elseif tnumber==ARTY.WeaponType.UnguidedAny then - name="Unguided Weapons" -- (Cannon or Rockets) elseif tnumber==ARTY.WeaponType.CruiseMissile then name="Cruise Missiles" - elseif tnumber==ARTY.WeaponType.GuidedMissile then - name="Guided Missiles" - elseif tnumber==ARTY.WeaponType.AntiShipMissile then - name="Anti-Ship Missiles" elseif tnumber==ARTY.WeaponType.TacticalNukes then name="Tactical Nukes" end @@ -4039,7 +4055,7 @@ function ARTY:_LLDMS2DD(l1,l2) local _format = "%d+:%d+:%d+" local _ldms=ll:match(_format) - if ldms then + if _ldms then -- Split DMS to degrees, minutes and seconds. local _dms=self:_split(_ldms, ":") @@ -4071,8 +4087,8 @@ function ARTY:_LLDMS2DD(l1,l2) end -- Debug text. - local text=string.format("\nLatitude %.3f", _latitude) - text=text..string.format("\nLongitude %.3f", _longitude) + local text=string.format("\nLatitude %s", tostring(_latitude)) + text=text..string.format("\nLongitude %s", tostring(_longitude)) self:T2(ARTY.id..text) return _latitude,_longitude From 804b8a800ef4cfe664e20f3342fb4a8d4374ea1e Mon Sep 17 00:00:00 2001 From: Ewald Zietsman Date: Sun, 10 Jun 2018 01:46:22 +0200 Subject: [PATCH 167/420] Added basic offset support to ZoneUnit --- Moose Development/Moose/Core/Zone.lua | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 72317d2ba..fd65dcf2d 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -961,7 +961,7 @@ end --- # ZONE_UNIT class, extends @{Zone#ZONE_RADIUS} -- --- The ZONE_UNIT class defined by a zone around a @{Unit#UNIT} with a radius. +-- The ZONE_UNIT class defined by a zone attached to a @{Unit#UNIT} with a radius and optional x and y offsets. -- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. -- -- @field #ZONE_UNIT @@ -969,14 +969,21 @@ ZONE_UNIT = { ClassName="ZONE_UNIT", } ---- Constructor to create a ZONE_UNIT instance, taking the zone name, a zone unit and a radius. +--- Constructor to create a ZONE_UNIT instance, taking the zone name, a zone unit and a radius and optional offsets in X and Y directions. -- @param #ZONE_UNIT self -- @param #string ZoneName Name of the zone. -- @param Wrapper.Unit#UNIT ZoneUNIT The unit as the center of the zone. -- @param Dcs.DCSTypes#Distance Radius The radius of the zone. +-- @param Dcs.DCSTypes#Distance dx The offset in X direction, +x is north. +-- @param Dcs.DCSTypes#Distance dy The offset in Y direction, +y is east. -- @return #ZONE_UNIT self -function ZONE_UNIT:New( ZoneName, ZoneUNIT, Radius ) +function ZONE_UNIT:New( ZoneName, ZoneUNIT, Radius, dx, dy ) + + self.dy = dy or 0.0 + self.dx = dx or 0.0 + local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, ZoneUNIT:GetVec2(), Radius ) ) + self:F( { ZoneName, ZoneUNIT:GetVec2(), Radius } ) self.ZoneUNIT = ZoneUNIT @@ -988,12 +995,18 @@ end --- Returns the current location of the @{Unit#UNIT}. -- @param #ZONE_UNIT self --- @return Dcs.DCSTypes#Vec2 The location of the zone based on the @{Unit#UNIT}location. +-- @return Dcs.DCSTypes#Vec2 The location of the zone based on the @{Unit#UNIT}location and the offset, if any. function ZONE_UNIT:GetVec2() self:F2( self.ZoneName ) local ZoneVec2 = self.ZoneUNIT:GetVec2() if ZoneVec2 then + env.info(self.dx .. " " .. self.dy) + + -- update the zone position with the offsets. + ZoneVec2.x = ZoneVec2.x + self.dx + ZoneVec2.y = ZoneVec2.y + self.dy + self.LastVec2 = ZoneVec2 return ZoneVec2 else From ba2d359af2cc39d454611747f7b4d5a96ce98eed Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sun, 10 Jun 2018 23:35:22 +0200 Subject: [PATCH 168/420] ARTY 1.0.0 - Added pseudo functions for FMS states. - Fixed a few bugs. --- Moose Development/Moose/Core/Event.lua | 5 +- .../Moose/Functional/Artillery.lua | 478 ++++++++++++++---- 2 files changed, 393 insertions(+), 90 deletions(-) diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 4592d634e..a1be00587 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -933,7 +933,9 @@ function EVENT:onEvent( Event ) Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName() --Event.WeaponTgtDCSUnit = Event.Weapon:getTarget() end - + +-- @FC: something like this should be added. +--[[ if Event.idx then Event.MarkID=Event.idx Event.MarkVec3=Event.pos @@ -942,6 +944,7 @@ function EVENT:onEvent( Event ) Event.MarkCoalition=Event.coalition Event.MarkGroupID = Event.groupID end +]] if Event.cargo then Event.Cargo = Event.cargo diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 27e1a2d6a..61201a6bf 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -63,12 +63,13 @@ -- @field #number IniGroupStrength Inital number of units in the ARTY group. -- @field #boolean IsArtillery If true, ARTY group has attribute "Artillery". This is automatically derived from the DCS descriptor table. -- @field #boolean ismobile If true, ARTY group can move. +-- @field #string groupname Name of the ARTY group as defined in the mission editor. -- @field #string alias Name of the ARTY group. -- @field #number SpeedMax Maximum speed of ARTY group in km/h. This is determined from the DCS descriptor table. -- @field #number Speed Default speed in km/h the ARTY group moves at. Maximum speed possible is 80% of maximum speed the group can do. -- @field #number RearmingDistance Safe distance in meters between ARTY group and rearming group or place at which rearming is possible. Default 100 m. -- @field Wrapper.Group#GROUP RearmingGroup Unit designated to rearm the ARTY group. --- @field #number RearmingGroupSpeed Speed in km/h the rearming unit moves at. Default 50 km/h. +-- @field #number RearmingGroupSpeed Speed in km/h the rearming unit moves at. Default is 50% of the max speed possible of the group. -- @field #boolean RearmingGroupOnRoad If true, rearming group will move to ARTY group or rearming place using mainly roads. Default false. -- @field Core.Point#COORDINATE RearmingGroupCoord Initial coordinates of the rearming unit. After rearming complete, the unit will return to this position. -- @field Core.Point#COORDINATE RearmingPlaceCoord Coordinates of the rearming place. If the place is more than 100 m away from the ARTY group, the group will go there. @@ -358,15 +359,15 @@ -- -- The mission designer has a few options to tailor the ARTY object according to his needs. -- --- * @{#ARTY.SetAutomaticRelocate}(*maxdist*, *onroad*) lets the ARTY group automatically move to within firing range if a current target is outside the min/max firing range. The +-- * @{#ARTY.SetAutoRelocateToFiringRange}(*maxdist*, *onroad*) lets the ARTY group automatically move to within firing range if a current target is outside the min/max firing range. The -- optional parameter *maxdist* is the maximum distance im km the group will move. If the distance is greater no relocation is performed. Default is 50 km. --- * @{#ARTY.SetRelocateAfterEngagement}(*rmax*, *rmin*) will cause the ARTY group to change its position after each firing assignment. +-- * @{#ARTY.SetAutoRelocateAfterEngagement}(*rmax*, *rmin*) will cause the ARTY group to change its position after each firing assignment. -- Optional parameters *rmax*, *rmin* define the max/min distance for relocation of the group. Default distance is randomly between 300 and 800 m. -- * @{#ARTY.RemoveAllTargets}() removes all targets from the target queue. -- * @{#ARTY.RemoveTarget}(*name*) deletes the target with *name* from the target queue. -- * @{#ARTY.SetMaxFiringRange}(*range*) defines the maximum firing range. Targets further away than this distance are not engaged. -- * @{#ARTY.SetMinFiringRange}(*range*) defines the minimum firing range. Targets closer than this distance are not engaged. --- * @{#ARTY.SetRearmingGroup}(*group*) sets the group resposible for rearming of the ARTY group once it is out of ammo. +-- * @{#ARTY.SetRearmingGroup}(*group*) sets the group responsible for rearming of the ARTY group once it is out of ammo. -- * @{#ARTY.SetReportON}() and @{#ARTY.SetReportOFF}() can be used to enable/disable status reports of the ARTY group send to all coalition members. -- * @{#ARTY.SetWaitForShotTime}(*waittime*) sets the time after which a target is deleted from the queue if no shooting event occured after the target engagement started. -- Default is 300 seconds. Note that this can for example happen, when the assigned target is out of range. @@ -455,13 +456,14 @@ ARTY={ DCSdesc=nil, Type=nil, DisplayName=nil, + groupname=nil, alias=nil, ismobile=true, IniGroupStrength=0, IsArtillery=nil, RearmingDistance=100, RearmingGroup=nil, - RearmingGroupSpeed=50, + RearmingGroupSpeed=nil, RearmingGroupOnRoad=false, RearmingGroupCoord=nil, RearmingPlaceCoord=nil, @@ -569,7 +571,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #string version -ARTY.version="0.9.96" +ARTY.version="1.0.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -586,18 +588,19 @@ ARTY.version="0.9.96" -- DONE: Abort firing task if no shooting event occured with 5(?) minutes. Something went wrong then. Min/max range for example. -- DONE: Improve assigned time for engagement. Next day? -- DONE: Improve documentation. --- TODO: Add pseudo user transitions. OnAfter... +-- DONE: Add pseudo user transitions. OnAfter... -- DONE: Make reaming unit a group. -- DONE: Write documenation. -- DONE: Add command move to make arty group move. -- DONE: remove schedulers for status event. --- TODO: Improve handling of special weapons. When winchester if using selected weapons? --- TODO: Handle rearming for ships. --- TODO: Make coordinate after rearming general, i.e. also work after the group has moved to anonther location. +-- DONE: Improve handling of special weapons. When winchester if using selected weapons? +-- TODO: Handle rearming for ships. How? +-- DONE: Make coordinate after rearming general, i.e. also work after the group has moved to anonther location. -- TODO: Add set commands via markers. E.g. set rearming place. --- TODO: Test stationary types like mortas ==> rearming etc. +-- DONE: Test stationary types like mortas ==> rearming etc. +-- TODO: Add hit event and make the arty group relocate. -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Creates a new ARTY object. -- @param #ARTY self @@ -627,10 +630,14 @@ function ARTY:New(group, alias) -- Set the controllable for the FSM. self:SetControllable(group) + -- Set the group name + self.groupname=group:GetName() + + -- Set an alias name. if alias~=nil then self.alias=tostring(alias) else - self.alias=group:GetName() + self.alias=self.groupname end -- Set the initial coordinates of the ARTY group. @@ -706,6 +713,268 @@ function ARTY:New(group, alias) -- Unknown transitons. To be checked if adding these causes problems. self:AddTransition("Rearming", "Arrived", "Rearming") self:AddTransition("Rearming", "Move", "Rearming") + + + --- User function for OnAfter "NewTarget" event. + -- @function [parent=#ARTY] OnAfterNewTarget + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #table target Array holding the target info. + + --- User function for OnAfter "OpenFire" event. + -- @function [parent=#ARTY] OnAfterOpenFire + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #table target Array holding the target info. + + --- User function for OnAfter "CeaseFire" event. + -- @function [parent=#ARTY] OnAfterCeaseFire + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #table target Array holding the target info. + + --- User function for OnAfer "NewMove" event. + -- @function [parent=#ARTY] OnAfterNewMove + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #table move Array holding the move info. + + --- User function for OnAfer "Move" event. + -- @function [parent=#ARTY] OnAfterMove + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #table move Array holding the move info. + + --- User function for OnAfer "Arrived" event. + -- @function [parent=#ARTY] OnAfterArrvied + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- User function for OnAfter "Winchester" event. + -- @function [parent=#ARTY] OnAfterWinchester + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- User function for OnAfter "Rearm" event. + -- @function [parent=#ARTY] OnAfterRearm + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- User function for OnAfter "Rearmed" event. + -- @function [parent=#ARTY] OnAfterRearmed + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- User function for OnAfter "Start" event. + -- @function [parent=#ARTY] OnAfterStart + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- User function for OnAfter "Status" event. + -- @function [parent=#ARTY] OnAfterStatus + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- User function for OnAfter "Dead" event. + -- @function [parent=#ARTY] OnAfterDead + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + + --- User function for OnEnter "CombatReady" state. + -- @function [parent=#ARTY] OnEnterCombatReady + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- User function for OnEnter "Firing" state. + -- @function [parent=#ARTY] OnEnterFiring + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- User function for OnEnter "OutOfAmmo" state. + -- @function [parent=#ARTY] OnEnterOutOfAmmo + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- User function for OnEnter "Rearming" state. + -- @function [parent=#ARTY] OnEnterRearming + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- User function for OnEnter "Rearmed" state. + -- @function [parent=#ARTY] OnEnterRearmed + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + --- User function for OnEnter "Moving" state. + -- @function [parent=#ARTY] OnEnterMoving + -- @param #ARTY self + -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + + + --- Function to start the ARTY FSM process. + -- @function [parent=#ARTY] Start + -- @param #ARTY self + + --- Function to start the ARTY FSM process after a delay. + -- @function [parent=#ARTY] __Start + -- @param #ARTY self + -- @param #number Delay before start in seconds. + + --- Function to update the status of the ARTY group and tigger FSM events. Triggers the FSM event "Status". + -- @function [parent=#ARTY] Status + -- @param #ARTY self + + --- Function to update the status of the ARTY group and tigger FSM events after a delay. Triggers the FSM event "Status". + -- @function [parent=#ARTY] __Status + -- @param #ARTY self + -- @param #number Delay in seconds. + + --- Function called when a unit of the ARTY group died. Triggers the FSM event "Dead". + -- @function [parent=#ARTY] Dead + -- @param #ARTY self + + --- Function called when a unit of the ARTY group died after a delay. Triggers the FSM event "Dead". + -- @function [parent=#ARTY] __Dead + -- @param #ARTY self + -- @param #number Delay in seconds. + + --- Add a new target for the ARTY group. Triggers the FSM event "NewTarget". + -- @function [parent=#ARTY] NewTarget + -- @param #ARTY self + -- @param #table target Array holding the target data. + + --- Add a new target for the ARTY group with a delay. Triggers the FSM event "NewTarget". + -- @function [parent=#ARTY] __NewTarget + -- @param #ARTY self + -- @param #number delay Delay in seconds. + -- @param #table target Array holding the target data. + + --- Add a new relocation move for the ARTY group. Triggers the FSM event "NewMove". + -- @function [parent=#ARTY] NewMove + -- @param #ARTY self + -- @param #table move Array holding the relocation move data. + + --- Add a new relocation for the ARTY group after a delay. Triggers the FSM event "NewMove". + -- @function [parent=#ARTY] __NewMove + -- @param #ARTY self + -- @param #number delay Delay in seconds. + -- @param #table move Array holding the relocation move data. + + --- Order ARTY group to open fire on a target. Triggers the FSM event "OpenFire". + -- @function [parent=#ARTY] OpenFire + -- @param #ARTY self + -- @param #table target Array holding the target data. + + --- Order ARTY group to open fire on a target with a delay. Triggers the FSM event "Move". + -- @function [parent=#ARTY] __OpenFire + -- @param #ARTY self + -- @param #number delay Delay in seconds. + -- @param #table target Array holding the target data. + + --- Order ARTY group to cease firing on a target. Triggers the FSM event "CeaseFire". + -- @function [parent=#ARTY] CeaseFire + -- @param #ARTY self + -- @param #table target Array holding the target data. + + --- Order ARTY group to cease firing on a target after a delay. Triggers the FSM event "CeaseFire". + -- @function [parent=#ARTY] __CeaseFire + -- @param #ARTY self + -- @param #number delay Delay in seconds. + -- @param #table target Array holding the target data. + + --- Order ARTY group to move to another location. Triggers the FSM event "Move". + -- @function [parent=#ARTY] Move + -- @param #ARTY self + -- @param #table move Array holding the relocation move data. + + --- Order ARTY group to move to another location after a delay. Triggers the FSM event "Move". + -- @function [parent=#ARTY] __Move + -- @param #ARTY self + -- @param #number delay Delay in seconds. + -- @param #table move Array holding the relocation move data. + + --- Tell ARTY group it has arrived at its destination. Triggers the FSM event "Arrived". + -- @function [parent=#ARTY] Arrived + -- @param #ARTY self + + --- Tell ARTY group it has arrived at its destination after a delay. Triggers the FSM event "Arrived". + -- @function [parent=#ARTY] __Arrived + -- @param #ARTY self + -- @param #number delay Delay in seconds. + + --- Tell ARTY group it is combat ready. Triggers the FSM event "CombatReady". + -- @function [parent=#ARTY] CombatReady + -- @param #ARTY self + + --- Tell ARTY group it is combat ready after a delay. Triggers the FSM event "CombatReady". + -- @function [parent=#ARTY] __CombatReady + -- @param #ARTY self + -- @param #number delay Delay in seconds. + + --- Tell ARTY group it is out of ammo. Triggers the FSM event "Winchester". + -- @function [parent=#ARTY] Winchester + -- @param #ARTY self + + --- Tell ARTY group it is out of ammo after a delay. Triggers the FSM event "Winchester". + -- @function [parent=#ARTY] __Winchester + -- @param #ARTY self + -- @param #number delay Delay in seconds. + return self end @@ -776,7 +1045,7 @@ function ARTY:AssignTargetCoord(coord, prio, radius, nshells, maxengage, time, w -- Target name should be unique and is not. if unique==true and _unique==false then - self:T(ARTY.id..string.format("%s: target %s should have a unique name but name was already given. Rejecting target!", self.Controllable:GetName(), _name)) + self:T(ARTY.id..string.format("%s: target %s should have a unique name but name was already given. Rejecting target!", self.groupname, _name)) return nil end @@ -810,7 +1079,7 @@ function ARTY:AssignMoveCoord(coord, time, speed, onroad, cancel, name, unique) -- Reject move if the group is immobile. if not self.ismobile then - self:T(ARTY.id..string.format("%s: group is immobile. Rejecting move request!", self.Controllable:GetName())) + self:T(ARTY.id..string.format("%s: group is immobile. Rejecting move request!", self.groupname)) return nil end @@ -828,7 +1097,7 @@ function ARTY:AssignMoveCoord(coord, time, speed, onroad, cancel, name, unique) -- Move name should be unique and is not. if unique==true and _unique==false then - self:T(ARTY.id..string.format("%s: move %s should have a unique name but name was already given. Rejecting move!", self.Controllable:GetName(), _name)) + self:T(ARTY.id..string.format("%s: move %s should have a unique name but name was already given. Rejecting move!", self.groupname, _name)) return nil end @@ -909,7 +1178,7 @@ end --- Assign a group, which is responsible for rearming the ARTY group. If the group is too far away from the ARTY group it will be guided towards the ARTY group. -- @param #ARTY self --- @param Wrapper.Group#GROUP group Group that is supposed to rearm the ARTY group. +-- @param Wrapper.Group#GROUP group Group that is supposed to rearm the ARTY group. For the blue coalition, this is often a unarmed M818 transport whilst for red an unarmed Ural-375 transport can be used. function ARTY:SetRearmingGroup(group) self:F({group=group}) self.RearmingGroup=group @@ -917,10 +1186,10 @@ end --- Set the speed the rearming group moves at towards the ARTY group or the rearming place. -- @param #ARTY self --- @param #number speed Speed in km/h. Default 50 km/h. +-- @param #number speed Speed in km/h. function ARTY:SetRearmingGroupSpeed(speed) self:F({speed=speed}) - self.RearmingGroupSpeed=speed or 50 + self.RearmingGroupSpeed=speed end --- Define if rearming group uses mainly roads to drive to the ARTY group or rearming place. @@ -957,7 +1226,7 @@ end -- @param #ARTY self -- @param #number maxdistance (Optional) The maximum distance in km the group will travel to get within firing range. Default is 50 km. No automatic relocation is performed if targets are assigned which are further away. -- @param #boolean onroad (Optional) If true, ARTY group uses roads whenever possible. Default false, i.e. group will move in a straight line to the assigned coordinate. -function ARTY:SetAutomaticRelocate(maxdistance, onroad) +function ARTY:SetAutoRelocateToFiringRange(maxdistance, onroad) self:F({distance=maxdistance, onroad=onroad}) self.autorelocate=true self.autorelocatemaxdist=maxdistance or 50 @@ -968,6 +1237,19 @@ function ARTY:SetAutomaticRelocate(maxdistance, onroad) self.autorelocateonroad=onroad end +--- Set relocate after firing. Group will find a new location after each engagement. Default is off +-- @param #ARTY self +-- @param #number rmax (Optional) Max distance in meters, the group will move to relocate. Default is 800 m. +-- @param #number rmin (Optional) Min distance in meters, the group will move to relocate. Default is 300 m. +function ARTY:SetAutoRelocateAfterEngagement(rmax, rmin) + self.relocateafterfire=true + self.relocateRmax=rmax or 800 + self.relocateRmin=rmin or 300 + + -- Ensure that Rmin<=Rmax + self.relocateRmin=math.min(self.relocateRmin, self.relocateRmax) +end + --- Report messages of ARTY group turned on. This is the default. -- @param #ARTY self function ARTY:SetReportON() @@ -1004,19 +1286,19 @@ function ARTY:RemoveTarget(name) if id then -- Remove target from table. - self:T(ARTY.id..string.format("Group %s: Removing target %s (id=%d).", self.Controllable:GetName(), name, id)) + self:T(ARTY.id..string.format("Group %s: Removing target %s (id=%d).", self.groupname, name, id)) table.remove(self.targets, id) -- Delete marker belonging to this engagement. if self.markallow then local batteryname,markTargetID, markMoveID=self:_GetMarkIDfromName(name) - if batteryname==self.Controllable:GetName() and markTargetID~=nil then + if batteryname==self.groupname and markTargetID~=nil then COORDINATE:RemoveMark(markTargetID) end end end - self:T(ARTY.id..string.format("Group %s: Number of targets = %d.", self.Controllable:GetName(), #self.targets)) + self:T(ARTY.id..string.format("Group %s: Number of targets = %d.", self.groupname, #self.targets)) end --- Delete a move from move list. @@ -1031,19 +1313,19 @@ function ARTY:RemoveMove(name) if id then -- Remove move from table. - self:T(ARTY.id..string.format("Group %s: Removing move %s (id=%d).", self.Controllable:GetName(), name, id)) + self:T(ARTY.id..string.format("Group %s: Removing move %s (id=%d).", self.groupname, name, id)) table.remove(self.moves, id) -- Delete marker belonging to this relocation move. if self.markallow then local batteryname,markTargetID,markMoveID=self:_GetMarkIDfromName(name) - if batteryname==self.Controllable:GetName() and markMoveID~=nil then + if batteryname==self.groupname and markMoveID~=nil then COORDINATE:RemoveMark(markMoveID) end end end - self:T(ARTY.id..string.format("Group %s: Number of moves = %d.", self.Controllable:GetName(), #self.moves)) + self:T(ARTY.id..string.format("Group %s: Number of moves = %d.", self.groupname, #self.moves)) end --- Delete ALL targets from current target list. @@ -1114,16 +1396,6 @@ function ARTY:SetTacNukeFires(nfires, range) self.nukerange=range end ---- Set relocate after firing. Group will find a new location after each engagement. Default is off --- @param #ARTY self --- @param #number rmax (Optional) Max distance in meters, the group will move to relocate. Default is 800 m. --- @param #number rmin (Optional) Min distance in meters, the group will move to relocate. Default is 300 m. -function ARTY:SetRelocateAfterEngagement(rmax, rmin) - self.relocateafterfire=true - self.relocateRmax=rmax or 800 - self.relocateRmin=rmin or 300 -end - --- Enable assigning targets and moves by placing markers on the F10 map. -- @param #ARTY self -- @param #number key (Optional) Authorization key. Only players knowing this key can assign targets. Default is no authorization required. @@ -1193,10 +1465,31 @@ function ARTY:onafterStart(Controllable, From, Event, To) self.RearmingPlaceCoord=nil self.relocateafterfire=false self.autorelocate=false + self.RearmingGroupSpeed=20 + end + + -- Set Rearming group speed if not specified by user + if self.RearmingGroup then + + -- Get max speed of rearming group. + local speedmax=self.RearmingGroup:GetSpeedMax() + self:T(ARTY.id..string.format("%s, rearming group %s max speed = %.1f km/h.", self.groupname, self.RearmingGroup:GetName(), speedmax)) + + if self.RearmingGroupSpeed==nil then + -- Set rearming group speed to 50% of max possible speed. + self.RearmingGroupSpeed=speedmax*0.5 + else + -- Ensure that speed is <= max speed. + self.RearmingGroupSpeed=math.min(self.RearmingGroupSpeed, self.RearmingGroup:GetSpeedMax()) + end + else + -- Just to have a reasonable number for output format below. + self.RearmingGroupSpeed=23 end local text=string.format("\n******************************************************\n") - text=text..string.format("Arty group = %s\n", Controllable:GetName()) + text=text..string.format("Arty group = %s\n", self.groupname) + text=text..string.format("Arty alias = %s\n", self.alias) text=text..string.format("Artillery attribute = %s\n", tostring(self.IsArtillery)) text=text..string.format("Type = %s\n", self.Type) text=text..string.format("Display Name = %s\n", self.DisplayName) @@ -1230,7 +1523,7 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("Relocate after fire = %s\n", tostring(self.relocateafterfire)) text=text..string.format("Relocate min dist. = %d m\n", self.relocateRmin) text=text..string.format("Relocate max dist. = %d m\n", self.relocateRmax) - text=text..string.format("Auto move in range = %s\n", tostring(self.autorelocate)) + text=text..string.format("Auto move in range = %s\n", tostring(self.autorelocate)) text=text..string.format("Auto move dist. max = %.1f km\n", self.autorelocatemaxdist/1000) text=text..string.format("Auto move on road = %s\n", tostring(self.autorelocateonroad)) text=text..string.format("Marker assignments = %s\n", tostring(self.markallow)) @@ -1322,7 +1615,7 @@ function ARTY:_StatusReport(display) local Clock=self:_SecondsToClock(timer.getAbsTime()) local text=string.format("\n******************* STATUS ***************************\n") - text=text..string.format("ARTY group = %s\n", self.Controllable:GetName()) + text=text..string.format("ARTY group = %s\n", self.groupname) text=text..string.format("Clock = %s\n", Clock) text=text..string.format("FSM state = %s\n", self:GetState()) text=text..string.format("Total ammo count = %d\n", Nammo) @@ -1381,7 +1674,7 @@ function ARTY:_OnEventShot(EventData) if group and group:IsAlive() then - if EventData.IniGroupName == self.Controllable:GetName() then + if EventData.IniGroupName == self.groupname then if self.currentTarget then @@ -1406,7 +1699,7 @@ function ARTY:_OnEventShot(EventData) return _weapon:getPoint() end) - self:T(ARTY.id..string.format("ARTY %s: Weapon still in air: %s", self.Controllable:GetName(), tostring(_status))) + self:T(ARTY.id..string.format("ARTY %s: Weapon still in air: %s", self.groupname, tostring(_status))) if _status then @@ -1430,7 +1723,7 @@ function ARTY:_OnEventShot(EventData) -- Start track the shell if we want to model a tactical nuke. if self.currentTarget.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes>0 then - self:T(ARTY.id..string.format("ARTY %s: Tracking of weapon starts in two seconds.", self.Controllable:GetName())) + self:T(ARTY.id..string.format("ARTY %s: Tracking of weapon starts in two seconds.", self.groupname)) timer.scheduleFunction(_TrackWeapon, EventData.weapon, timer.getTime() + 2.0) end @@ -1445,7 +1738,7 @@ function ARTY:_OnEventShot(EventData) -- Check if we are completely out of ammo. local _outofammo=false if _nammo==0 then - self:T(ARTY.id..string.format("Group %s completely out of ammo.", self.Controllable:GetName())) + self:T(ARTY.id..string.format("Group %s completely out of ammo.", self.groupname)) _outofammo=true end @@ -1455,8 +1748,8 @@ function ARTY:_OnEventShot(EventData) -- Weapon type name for current target. local _weapontype=self:_WeaponTypeName(self.currentTarget.weapontype) - self:T(ARTY.id..string.format("Group %s ammo: total=%d, shells=%d, rockets=%d, missiles=%d", self.Controllable:GetName(), _nammo, _nshells, _nrockets, _nmissiles)) - self:T(ARTY.id..string.format("Group %s uses weapontype %s for current target.", self.Controllable:GetName(), _weapontype)) + self:T(ARTY.id..string.format("Group %s ammo: total=%d, shells=%d, rockets=%d, missiles=%d", self.groupname, _nammo, _nshells, _nrockets, _nmissiles)) + self:T(ARTY.id..string.format("Group %s uses weapontype %s for current target.", self.groupname, _weapontype)) -- Default switches for cease fire and relocation. local _ceasefire=false @@ -1466,7 +1759,7 @@ function ARTY:_OnEventShot(EventData) if self.Nshots >= self.currentTarget.nshells then -- Debug message - local text=string.format("Group %s stop firing on target %s.", self.Controllable:GetName(), self.currentTarget.name) + local text=string.format("Group %s stop firing on target %s.", self.groupname, self.currentTarget.name) self:T(ARTY.id..text) MESSAGE:New(text, 5):ToAllIf(self.Debug) @@ -1499,7 +1792,7 @@ function ARTY:_OnEventShot(EventData) end else - self:E(ARTY.id..string.format("WARNING: No current target for group %s?!", self.Controllable:GetName())) + self:E(ARTY.id..string.format("WARNING: No current target for group %s?!", self.groupname)) end end end @@ -1517,7 +1810,7 @@ function ARTY:onEvent(Event) end -- Set battery and coalition. - local batteryname=self.Controllable:GetName() + local batteryname=self.groupname local batterycoalition=self.Controllable:GetCoalition() self:T2(string.format("Event captured = %s", tostring(batteryname))) @@ -1557,7 +1850,7 @@ function ARTY:_OnEventMarkRemove(Event) -- Get battery coalition and name. local batterycoalition=self.Controllable:GetCoalition() - local batteryname=self.Controllable:GetName() + local batteryname=self.groupname if Event.text~=nil and Event.text:find("BATTERY") then @@ -1632,7 +1925,7 @@ function ARTY:_OnEventMarkChange(Event) -- Get battery coalition and name. local batterycoalition=self.Controllable:GetCoalition() - local batteryname=self.Controllable:GetName() + local batteryname=self.groupname -- Check if the coalition is the same or an authorization key has been defined. if (batterycoalition==Event.coalition and self.markkey==nil) or self.markkey~=nil then @@ -1650,7 +1943,7 @@ function ARTY:_OnEventMarkChange(Event) if #_assign.battery>0 then _assigned=false for _,bat in pairs(_assign.battery) do - self:T2(ARTY.id..string.format("Compare battery names %s=%s ==> %s",batteryname, bat, tostring(batteryname==bat))) + self:T3(ARTY.id..string.format("Compare battery names %s=%s ==> %s",batteryname, bat, tostring(batteryname==bat))) if batteryname==bat then _assigned=true end @@ -1694,7 +1987,7 @@ function ARTY:_OnEventMarkChange(Event) elseif _assign.canceltarget and self.currentTarget then self.currentTarget.engaged=self.currentTarget.engaged+1 self:CeaseFire(self.currentTarget) - elseif _assign.cancelrearm and self.is("Rearming") then + elseif _assign.cancelrearm and self:is("Rearming") then local nammo=self:GetAmmo() if nammo>0 then self:Rearmed() @@ -1770,7 +2063,7 @@ function ARTY:_OnEventMarkChange(Event) if _assign.prio then text=text..string.format("\nPrio %d",_assign.prio) end - if _assign.prio then + if _assign.radius then text=text..string.format("\nRadius %d m",_assign.radius) end if _assign.nshells then @@ -1816,7 +2109,7 @@ function ARTY:_OnEventDead(EventData) self:F(EventData) -- Name of controllable. - local _name=self.Controllable:GetName() + local _name=self.groupname -- Check for correct group. if EventData.IniGroupName==_name then @@ -1898,7 +2191,7 @@ function ARTY:onafterStatus(Controllable, From, Event, To) end end for _,targetname in pairs(notpossible) do - self:E(ARTY.id..string.format("%s: Removing target %s because requested weapon is not possible with this type of unit.", self.Controllable:GetName(), targetname)) + self:E(ARTY.id..string.format("%s: Removing target %s because requested weapon is not possible with this type of unit.", self.groupname, targetname)) self:RemoveTarget(targetname) end @@ -1995,7 +2288,7 @@ function ARTY:onbeforeOpenFire(Controllable, From, Event, To, target) -- Check that group has no current target already. if self.currentTarget then -- This should not happen. Some earlier check failed. - self:E(ARTY.id..string.format("ERROR: Group %s already has a target %s!", self.Controllable:GetName(), self.currentTarget.name)) + self:E(ARTY.id..string.format("ERROR: Group %s already has a target %s!", self.groupname, self.currentTarget.name)) -- Deny transition. return false end @@ -2003,7 +2296,7 @@ function ARTY:onbeforeOpenFire(Controllable, From, Event, To, target) -- Check if target is in range. if not self:_TargetInRange(target) then -- This should not happen. Some earlier check failed. - self:E(ARTY.id..string.format("ERROR: Group %s, target %s is out of range!", self.Controllable:GetName(), self.currentTarget.name)) + self:E(ARTY.id..string.format("ERROR: Group %s, target %s is out of range!", self.groupname, self.currentTarget.name)) -- Deny transition. return false end @@ -2135,7 +2428,7 @@ function ARTY:onafterCeaseFire(Controllable, From, Event, To, target) self.Controllable:ClearTasks() else - self:E(ARTY.id.."ERROR: No target in cease fire for group %s.", self.Controllable:GetName()) + self:E(ARTY.id.."ERROR: No target in cease fire for group %s.", self.groupname) end -- Set number of shots to zero. @@ -2178,10 +2471,10 @@ function ARTY:onbeforeRearm(Controllable, From, Event, To) local _rearmed=self:_CheckRearmed() if _rearmed then - self:T(ARTY.id..string.format("%s, group is already armed to the teeth. Rearming request denied!", self.Controllable:GetName())) + self:T(ARTY.id..string.format("%s, group is already armed to the teeth. Rearming request denied!", self.groupname)) return false else - self:T(ARTY.id..string.format("%s, group might be rearmed.", self.Controllable:GetName())) + self:T(ARTY.id..string.format("%s, group might be rearmed.", self.groupname)) end -- Check if a reaming unit or rearming place was specified. @@ -2240,7 +2533,8 @@ function ARTY:onafterRearm(Controllable, From, Event, To) -- Route Rearming group to rearming place. if dR > self.RearmingDistance then - self:_Move(self.RearmingGroup, self:_VicinityCoord(self.RearmingPlaceCoord, self.RearmingDistance/4, self.RearmingDistance/2), self.RearmingGroupSpeed, self.RearmingGroupOnRoad) + local ToCoord=self:_VicinityCoord(self.RearmingPlaceCoord, self.RearmingDistance/4, self.RearmingDistance/2) + self:_Move(self.RearmingGroup, ToCoord, self.RearmingGroupSpeed, self.RearmingGroupOnRoad) end elseif self.RearmingGroup then @@ -2314,6 +2608,9 @@ function ARTY:onafterRearmed(Controllable, From, Event, To) local d=self.RearmingGroup:GetCoordinate():Get2DDistance(self.RearmingGroupCoord) if d > self.RearmingDistance then self:_Move(self.RearmingGroup, self.RearmingGroupCoord, self.RearmingGroupSpeed, self.RearmingGroupOnRoad) + else + -- Clear tasks. + self.RearmingGroup:ClearTasks() end end @@ -2502,7 +2799,7 @@ function ARTY:onafterDead(Controllable, From, Event, To) end -- Message. - local text=string.format("%s, one of our units just died! %d units left.", self.Controllable:GetName(), nunits) + local text=string.format("%s, one of our units just died! %d units left.", self.groupname, nunits) MESSAGE:New(text, 5):ToAllIf(self.Debug) self:T(ARTY.id..text) @@ -2800,7 +3097,7 @@ function ARTY._PassingWaypoint(group, arty, i, final) ]] -- Arrived event. - if final and arty.Controllable:GetName()==group:GetName() then + if final and arty.groupname==group:GetName() then arty:Arrived() end @@ -2868,7 +3165,7 @@ function ARTY:GetAmmo(display) if unit and unit:IsAlive() then -- Output. - local text=string.format("ARTY group %s - unit %s:\n", self.Controllable:GetName(), unit:GetName()) + local text=string.format("ARTY group %s - unit %s:\n", self.groupname, unit:GetName()) -- Get ammo table. local ammotable=unit:GetAmmo() @@ -3040,7 +3337,7 @@ end function ARTY:_MarkerKeyAuthentification(text) -- Set battery and coalition. - local batteryname=self.Controllable:GetName() + local batteryname=self.groupname local batterycoalition=self.Controllable:GetCoalition() -- Get assignment. @@ -3133,11 +3430,14 @@ function ARTY:_Markertext(text) local str=self:_split(keyphrase, " ") local key=str[1] local val=str[2] + + -- Debug output. + self:T3(ARTY.id..string.format("%s, keyphrase = %s, key = %s, val = %s", self.groupname, tostring(keyphrase), tostring(key), tostring(val))) -- Battery name, i.e. which ARTY group should fire. if key:lower():find("battery") then - local v=self:_split(key, '"') + local v=self:_split(keyphrase, '"') for i=2,#v,2 do table.insert(assignment.battery, v[i]) @@ -3193,57 +3493,57 @@ function ARTY:_Markertext(text) assignment.speed=tonumber(val) self:T2(ARTY.id..string.format("Key Speed=%s.", val)) - elseif assignment.move and (key:lower():find("on road") or key:lower():find("onroad") or key:lower():find("use road")) then + elseif assignment.move and (keyphrase:lower():find("on road") or keyphrase:lower():find("onroad") or keyphrase:lower():find("use road")) then assignment.onroad=true self:T2(ARTY.id..string.format("Key Onroad=true.")) - elseif key:lower():find("irrevocable") or key:lower():find("readonly") then + elseif keyphrase:lower():find("irrevocable") or keyphrase:lower():find("readonly") then assignment.readonly=true self:T2(ARTY.id..string.format("Key Readonly=true.")) - elseif assignment.move and (key:lower():find("cancel target") or key:lower():find("cancel target")) then + elseif assignment.move and (keyphrase:lower():find("cancel target") or keyphrase:lower():find("canceltarget")) then assignment.movecanceltarget=true self:T2(ARTY.id..string.format("Key Cancel Target (before move)=true.")) - elseif assignment.request and key:lower():find("rearm") then + elseif assignment.request and keyphrase:lower():find("rearm") then assignment.requestrearming=true self:T2(ARTY.id..string.format("Key Request Rearming=true.")) - elseif assignment.request and key:lower():find("ammo") then + elseif assignment.request and keyphrase:lower():find("ammo") then assignment.requestammo=true self:T2(ARTY.id..string.format("Key Request Ammo=true.")) - elseif assignment.request and key:lower():find("target") then + elseif assignment.request and keyphrase:lower():find("target") then assignment.requesttargets=true self:T2(ARTY.id..string.format("Key Request Targets=true.")) - elseif assignment.request and key:lower():find("status") then + elseif assignment.request and keyphrase:lower():find("status") then assignment.requeststatus=true self:T2(ARTY.id..string.format("Key Request Status=true.")) - elseif assignment.request and (key:lower():find("move") or key:lower():find("relocation")) then + elseif assignment.request and (keyphrase:lower():find("move") or keyphrase:lower():find("relocation")) then assignment.requestmoves=true self:T2(ARTY.id..string.format("Key Request Moves=true.")) - elseif assignment.cancel and (key:lower():find("engagement") or key:lower():find("attack") or key:lower():find("target")) then + elseif assignment.cancel and (keyphrase:lower():find("engagement") or keyphrase:lower():find("attack") or keyphrase:lower():find("target")) then assignment.canceltarget=true self:T2(ARTY.id..string.format("Key Cancel Target=true.")) - elseif assignment.cancel and (key:lower():find("move") or key:lower():find("relocation")) then + elseif assignment.cancel and (keyphrase:lower():find("move") or keyphrase:lower():find("relocation")) then assignment.cancelmove=true self:T2(ARTY.id..string.format("Key Cancel Move=true.")) - elseif assignment.cancel and key:lower():find("rearm") then + elseif assignment.cancel and keyphrase:lower():find("rearm") then assignment.cancelrearm=true self:T2(ARTY.id..string.format("Key Cancel Rearm=true.")) @@ -3289,7 +3589,7 @@ end --- Request Moves. -- @param #ARTY self function ARTY:_MarkRequestMoves() - local text=string.format("%s, relocations:", self.Controllable:GetName()) + local text=string.format("%s, relocations:", self.groupname) if #self.moves>0 then for _,move in pairs(self.moves) do if self.currentMove and move.name == self.currentMove.name then @@ -3307,7 +3607,7 @@ end --- Request Targets. -- @param #ARTY self function ARTY:_MarkRequestTargets() - local text=string.format("%s, targets:", self.Controllable:GetName()) + local text=string.format("%s, targets:", self.groupname) if #self.targets>0 then for _,target in pairs(self.targets) do if self.currentTarget and target.name == self.currentTarget.name then @@ -3327,7 +3627,7 @@ end -- @param #number markerid ID of the placed marker. -- @return #string Name of target engagement. function ARTY:_MarkTargetName(markerid) - return string.format("BATTERY=%s, Marked Target ID=%d", self.Controllable:GetName(), markerid) + return string.format("BATTERY=%s, Marked Target ID=%d", self.groupname, markerid) end --- Create a name for a relocation move initiated by placing a marker. @@ -3335,7 +3635,7 @@ end -- @param #number markerid ID of the placed marker. -- @return #string Name of relocation move. function ARTY:_MarkMoveName(markerid) - return string.format("BATTERY=%s, Marked Relocation ID=%d", self.Controllable:GetName(), markerid) + return string.format("BATTERY=%s, Marked Relocation ID=%d", self.groupname, markerid) end --- Get the marker ID from the assigned task name. @@ -3679,14 +3979,14 @@ function ARTY:_CheckShootingStarted() -- Debug info if self.Nshots==0 then - self:T(ARTY.id..string.format("%s, waiting for %d seconds for first shot on target %s.", self.Controllable:GetName(), dt, name)) + self:T(ARTY.id..string.format("%s, waiting for %d seconds for first shot on target %s.", self.groupname, dt, name)) end -- Check if we waited long enough and no shot was fired. if dt > self.WaitForShotTime and self.Nshots==0 then -- Debug info. - self:T(ARTY.id..string.format("%s, no shot event after %d seconds. Removing current target %s from list.", self.Controllable:GetName(), self.WaitForShotTime, name)) + self:T(ARTY.id..string.format("%s, no shot event after %d seconds. Removing current target %s from list.", self.groupname, self.WaitForShotTime, name)) -- CeaseFire. self:CeaseFire(self.currentTarget) @@ -3754,27 +4054,27 @@ function ARTY:_CheckOutOfAmmo(targets) if Target.weapontype==ARTY.WeaponType.Auto and _nammo==0 then - self:T(ARTY.id..string.format("Group %s, auto weapon requested for target %s but all ammo is empty.", self.Controllable:GetName(), Target.name)) + self:T(ARTY.id..string.format("Group %s, auto weapon requested for target %s but all ammo is empty.", self.groupname, Target.name)) _partlyoutofammo=true elseif Target.weapontype==ARTY.WeaponType.Cannon and _nshells==0 then - self:T(ARTY.id..string.format("Group %s, cannons requested for target %s but shells empty.", self.Controllable:GetName(), Target.name)) + self:T(ARTY.id..string.format("Group %s, cannons requested for target %s but shells empty.", self.groupname, Target.name)) _partlyoutofammo=true elseif Target.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes<=0 then - self:T(ARTY.id..string.format("Group %s, tactical nukes requested for target %s but nukes empty.", self.Controllable:GetName(), Target.name)) + self:T(ARTY.id..string.format("Group %s, tactical nukes requested for target %s but nukes empty.", self.groupname, Target.name)) _partlyoutofammo=true elseif Target.weapontype==ARTY.WeaponType.Rockets and _nrockets==0 then - self:T(ARTY.id..string.format("Group %s, rockets requested for target %s but rockets empty.", self.Controllable:GetName(), Target.name)) + self:T(ARTY.id..string.format("Group %s, rockets requested for target %s but rockets empty.", self.groupname, Target.name)) _partlyoutofammo=true elseif Target.weapontype==ARTY.WeaponType.CruiseMissile and _nmissiles==0 then - self:T(ARTY.id..string.format("Group %s, cruise missiles requested for target %s but all missiles empty.", self.Controllable:GetName(), Target.name)) + self:T(ARTY.id..string.format("Group %s, cruise missiles requested for target %s but all missiles empty.", self.groupname, Target.name)) _partlyoutofammo=true end @@ -3994,7 +4294,7 @@ end -- @param #string From From state. -- @param #string To To state. function ARTY:_EventFromTo(BA, Event, From, To) - local text=string.format("%s: %s EVENT %s: %s --> %s", BA, self.Controllable:GetName(), Event, From, To) + local text=string.format("%s: %s EVENT %s: %s --> %s", BA, self.groupname, Event, From, To) self:T3(ARTY.id..text) end From d5d2de75776863ea5495196f7047912e36125773 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sun, 10 Jun 2018 23:41:07 +0200 Subject: [PATCH 169/420] Controllable alarm state ships don't have option to change alarm state to other than green --- Moose Development/Moose/Wrapper/Controllable.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index d539dd098..0b56cc0d7 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -2654,7 +2654,6 @@ function CONTROLLABLE:OptionAlarmStateGreen() elseif self:IsShip() then -- AI.Option.Naval.id.ALARM_STATE does not seem to exist! --Controller:setOption( AI.Option.Naval.id.ALARM_STATE, AI.Option.Naval.val.ALARM_STATE.GREEN ) - Controller:setOption( AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.GREEN ) end return self From 3fee4a87daaa151020fbb562c009e798f60c6167 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 11 Jun 2018 20:29:58 +0200 Subject: [PATCH 170/420] ARTY v1.0.1 Added cluster and alias assignment via marks. --- .../Moose/Functional/Artillery.lua | 142 +++++++++++++++--- 1 file changed, 118 insertions(+), 24 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 61201a6bf..d2c1ad784 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -65,6 +65,7 @@ -- @field #boolean ismobile If true, ARTY group can move. -- @field #string groupname Name of the ARTY group as defined in the mission editor. -- @field #string alias Name of the ARTY group. +-- @field #table clusters Table of names of clusters the group belongs to. Can be used to address all groups within the cluster simultaniously. -- @field #number SpeedMax Maximum speed of ARTY group in km/h. This is determined from the DCS descriptor table. -- @field #number Speed Default speed in km/h the ARTY group moves at. Maximum speed possible is 80% of maximum speed the group can do. -- @field #number RearmingDistance Safe distance in meters between ARTY group and rearming group or place at which rearming is possible. Default 100 m. @@ -293,6 +294,8 @@ -- * *radius* Scattering radius of the fired shots in meters. Default is 100 m. -- * *weapon* Type of weapon to be used. Valid parameters are *cannon*, *rocket*, *missile*, *nuke*. Default is automatic selection. -- * *battery* Name of the ARTY group that the target is assigned to. Note that **the name is case sensitive** and has to be given in quotation marks. Default is all ARTY groups of the right coalition. +-- * *alias* Alias of the ARTY group that the target is assigned to. The alias is **case sensitive** and needs to be in quotation marks. +-- * *cluster* The cluster of ARTY groups that is addessed. Clusters can be defined by the function @{#ARTY.AddToCluster}(*clusters*). Names are **case sensitive** and need to be in quotation marks. -- * *key* A number to authorize the target assignment. Only specifing the correct number will trigger an engagement. -- * *lldms* Specify the coordinates in Lat/Long degrees, minutes and seconds format. The actual location of the marker is unimportant here. The group will engage the coordinates given in the lldms keyword. -- Format is DD:MM:SS[N,S] DD:MM:SS[W,E]. See example below. This can be useful when coordinates in this format are obtained from elsewhere. @@ -305,9 +308,13 @@ -- arty engage, battery "Blue MRLS 1", key 666 -- arty engage, battery "Paladin Alpha", weapon nukes, shots 1, time 20:15 -- arty engage, lldms 41:51:00N 41:47:58E +-- arty engage, alias "Bob", weapon missiles +-- arty engage, cluster "All Mortas" +-- arty engage, cluster "Northern Batteries" "Southern Batteries" +-- arty engage, cluster "Northern Batteries", cluster "Southern Batteries" -- --- Note that the keywords and parameters are *case insensitve*. Only exception are the battery group names. These must be exactly the same as the names of the goups defined --- in the mission editor. +-- Note that the keywords and parameters are *case insensitve*. Only exception are the battery, alias and cluster names. +-- These must be exactly the same as the names of the goups defined in the mission editor or the aliases and cluster names defined in the script. -- -- ### Relocation Assignments -- @@ -318,6 +325,8 @@ -- * *on road* Group will use mainly roads. Default is off, i.e. it will go in a straight line from its current position to the assigned coordinate. -- * *canceltarget* Group will cancel all running firing engagements and immidiately start to move. Default is that group will wait until is current assignment is over. -- * *battery* Name of the ARTY group that the relocation is assigned to. +-- * *alias* Alias of the ARTY group that the target is assigned to. The alias is **case sensitive** and needs to be in quotation marks. +-- * *cluster* The cluster of ARTY groups that is addessed. Clusters can be defined by the function @{#ARTY.AddToCluster}(*clusters*). Names are **case sensitive** and need to be in quotation marks. -- * *key* A number to authorize the target assignment. Only specifing the correct number will trigger an engagement. -- * *lldms* Specify the coordinates in Lat/Long degrees, minutes and seconds format. The actual location of the marker is unimportant. The group will move to the coordinates given in the lldms keyword. -- Format is DD:MM:SS[N,S] DD:MM:SS[W,E]. See example below. @@ -329,6 +338,10 @@ -- arty move, battery "Blue Paladin" -- arty move, battery "Blue MRLS", canceltarget, speed 10, on road -- arty move, lldms 41:51:00N 41:47:58E +-- arty move, alias "Bob", weapon missiles +-- arty move, cluster "All Howitzer" +-- arty move, cluster "Northern Batteries" "Southern Batteries" +-- arty move, cluster "Northern Batteries", cluster "Southern Batteries" -- -- ### Requests -- @@ -341,7 +354,7 @@ -- For example -- arty request, ammo -- arty request, battery "Paladin Bravo", targets --- arty request, battery "MRLS Charly", move +-- arty request, cluster "All Mortars", move -- -- The actual location of the marker is irrelevant for these requests. -- @@ -363,6 +376,7 @@ -- optional parameter *maxdist* is the maximum distance im km the group will move. If the distance is greater no relocation is performed. Default is 50 km. -- * @{#ARTY.SetAutoRelocateAfterEngagement}(*rmax*, *rmin*) will cause the ARTY group to change its position after each firing assignment. -- Optional parameters *rmax*, *rmin* define the max/min distance for relocation of the group. Default distance is randomly between 300 and 800 m. +-- * @{#ARTY.AddToCluster}(*clusters*) Can be used to add the ARTY group to one or more clusters. All groups in a cluster can be addressed simultaniously with one marker command. -- * @{#ARTY.RemoveAllTargets}() removes all targets from the target queue. -- * @{#ARTY.RemoveTarget}(*name*) deletes the target with *name* from the target queue. -- * @{#ARTY.SetMaxFiringRange}(*range*) defines the maximum firing range. Targets further away than this distance are not engaged. @@ -458,6 +472,7 @@ ARTY={ DisplayName=nil, groupname=nil, alias=nil, + clusters={}, ismobile=true, IniGroupStrength=0, IsArtillery=nil, @@ -571,7 +586,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #string version -ARTY.version="1.0.0" +ARTY.version="1.0.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -605,7 +620,7 @@ ARTY.version="1.0.0" --- Creates a new ARTY object. -- @param #ARTY self -- @param Wrapper.Group#GROUP group The GROUP object for which artillery tasks should be assigned. --- @param alias (Optional) Alias name the group will be calling itself when sending messages. +-- @param alias (Optional) Alias name the group will be calling itself when sending messages. Default is the group name. -- @return #ARTY ARTY object or nil if group does not exist or is not a ground or naval group. function ARTY:New(group, alias) BASE:F2(group) @@ -1136,6 +1151,38 @@ function ARTY:AssignMoveCoord(coord, time, speed, onroad, cancel, name, unique) return _name end +--- Set alias, i.e. the name the group will use when sending messages. +-- @param #ARTY self +-- @param #string alias The alias for the group. +function ARTY:SetAlias(alias) + self:F({alias=alias}) + self.alias=tostring(alias) +end + +--- Add ARTY group to one or more clusters. Enables addressing all ARTY groups within a cluster simultaniously via marker assignments. +-- @param #ARTY self +-- @param #table clusters Table of cluster names the group should belong to. +function ARTY:AddToCluster(clusters) + self:F({clusters=clusters}) + + -- Convert input to table. + local names + if type(clusters)=="table" then + names=clusters + elseif type(clusters)=="string" then + names={clusters} + else + -- error message + self:E(ARTY.id.."ERROR: Input parameter must be a string or a table in ARTY:AddToCluster()!") + return + end + + -- Add names to cluster array. + for _,cluster in pairs(names) do + table.insert(self.clusters, cluster) + end +end + --- Set minimum firing range. Targets closer than this distance are not engaged. -- @param #ARTY self -- @param #number range Min range in kilometers. Default is 0.1 km. @@ -1529,6 +1576,10 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("Marker assignments = %s\n", tostring(self.markallow)) text=text..string.format("Marker auth. key = %s\n", tostring(self.markkey)) text=text..string.format("Marker readonly = %s\n", tostring(self.markreadonly)) + text=text..string.format("Clusters:\n") + for _,cluster in pairs(self.clusters) do + text=text..string.format("- %s\n", tostring(cluster)) + end text=text..string.format("******************************************************\n") text=text..string.format("Targets:\n") for _, target in pairs(self.targets) do @@ -1810,10 +1861,10 @@ function ARTY:onEvent(Event) end -- Set battery and coalition. - local batteryname=self.groupname - local batterycoalition=self.Controllable:GetCoalition() + --local batteryname=self.groupname + --local batterycoalition=self.Controllable:GetCoalition() - self:T2(string.format("Event captured = %s", tostring(batteryname))) + self:T2(string.format("Event captured = %s", tostring(self.groupname))) self:T2(string.format("Event id = %s", tostring(Event.id))) self:T2(string.format("Event time = %s", tostring(Event.time))) self:T2(string.format("Event idx = %s", tostring(Event.idx))) @@ -1822,20 +1873,20 @@ function ARTY:onEvent(Event) self:T2(string.format("Event text = %s", tostring(Event.text))) if Event.initiator~=nil then local _unitname=Event.initiator:getName() - self:T(string.format("Event ini unit name = %s", tostring(_unitname))) + self:T2(string.format("Event ini unit name = %s", tostring(_unitname))) end if Event.id==world.event.S_EVENT_MARK_ADDED then - self:E({event="S_EVENT_MARK_ADDED", battery=batteryname, vec3=Event.pos}) + self:T2({event="S_EVENT_MARK_ADDED", battery=self.groupname, vec3=Event.pos}) elseif Event.id==world.event.S_EVENT_MARK_CHANGE then - self:E({event="S_EVENT_MARK_CHANGE", battery=batteryname, vec3=Event.pos}) + self:T({event="S_EVENT_MARK_CHANGE", battery=self.groupname, vec3=Event.pos}) -- Handle event. self:_OnEventMarkChange(Event) elseif Event.id==world.event.S_EVENT_MARK_REMOVED then - self:E({event="S_EVENT_MARK_REMOVED", battery=batteryname, vec3=Event.pos}) + self:T2({event="S_EVENT_MARK_REMOVED", battery=self.groupname, vec3=Event.pos}) -- Hande event. self:_OnEventMarkRemove(Event) @@ -1938,16 +1989,38 @@ function ARTY:_OnEventMarkChange(Event) return end - -- Check if job is assigned to this ARTY group. Default is for all ARTY groups. - local _assigned=true - if #_assign.battery>0 then - _assigned=false + -- Check if job is assigned to this ARTY group. Default is for all ARTY groups. + local _assigned=false + + -- If any array is filled something has been assigned. + if #_assign.battery>0 or #_assign.aliases>0 or #_assign.cluster>0 then + + -- Loop over batteries. for _,bat in pairs(_assign.battery) do - self:T3(ARTY.id..string.format("Compare battery names %s=%s ==> %s",batteryname, bat, tostring(batteryname==bat))) - if batteryname==bat then + if self.groupname==bat then _assigned=true end end + + -- Loop over aliases. + for _,alias in pairs(_assign.aliases) do + if self.alias==alias then + _assigned=true + end + end + + -- Loop over clusters. + for _,bat in pairs(_assign.cluster) do + for _,cluster in pairs(self.clusters) do + if cluster==bat then + _assigned=true + end + end + end + + else + -- No one explicitly assigned ==> we assume to be assigned. + _assigned=true end -- We were not addressed. @@ -1995,6 +2068,7 @@ function ARTY:_OnEventMarkChange(Event) self:Winchester() end end + -- Cancels Done ==> End of story! return end @@ -2027,7 +2101,7 @@ function ARTY:_OnEventMarkChange(Event) -- Create a new name. This determins the string we search when deleting a move! local _name=self:_MarkMoveName(_id) - local text=string.format("%s, received new relocation assignment.", batteryname) + local text=string.format("%s, received new relocation assignment.", self.alias) text=text..string.format("\nCoordinates %s",_coord:ToStringLLDMS()) MESSAGE:New(text, 10):ToCoalitionIf(batterycoalition, self.report or self.Debug) @@ -2046,7 +2120,7 @@ function ARTY:_OnEventMarkChange(Event) local _randomcoord=_coord:GetRandomCoordinateInRadius(100) _randomcoord:MarkToCoalition(_markertext, batterycoalition, self.markreadonly or _assign.readonly) else - local text=string.format("%s, relocation not possible.", batteryname) + local text=string.format("%s, relocation not possible.", self.alias) MESSAGE:New(text, 10):ToCoalitionIf(batterycoalition, self.report or self.Debug) end @@ -2055,7 +2129,7 @@ function ARTY:_OnEventMarkChange(Event) -- Create a new name. local _name=self:_MarkTargetName(_id) - local text=string.format("%s, received new target assignment.", batteryname) + local text=string.format("%s, received new target assignment.", self.alias) text=text..string.format("\nCoordinates %s",_coord:ToStringLLDMS()) if _assign.time then text=text..string.format("\nTime %s",_assign.time) @@ -3374,11 +3448,11 @@ function ARTY:_MarkerKeyAuthentification(text) -- Send message local text="" if mykey==nil then - text=string.format("%s, authorization required but did not receive a key!", batteryname) + text=string.format("%s, authorization required but did not receive a key!", self.alias) elseif _validkey==false then - text=string.format("%s, authorization required but did receive an incorrect key (key=%s)!", batteryname, tostring(mykey)) + text=string.format("%s, authorization required but did receive an incorrect key (key=%s)!", self.alias, tostring(mykey)) elseif _validkey==true then - text=string.format("%s, authentification successful!", batteryname) + text=string.format("%s, authentification successful!", self.alias) end MESSAGE:New(text, 10):ToCoalitionIf(batterycoalition, self.report or self.Debug) end @@ -3396,6 +3470,8 @@ function ARTY:_Markertext(text) -- Assignment parameters. local assignment={} assignment.battery={} + assignment.aliases={} + assignment.cluster={} assignment.move=false assignment.engage=false assignment.request=false @@ -3443,6 +3519,24 @@ function ARTY:_Markertext(text) table.insert(assignment.battery, v[i]) self:T2(ARTY.id..string.format("Key Battery=%s.", v[i])) end + + elseif key:lower():find("alias") then + + local v=self:_split(keyphrase, '"') + + for i=2,#v,2 do + table.insert(assignment.aliases, v[i]) + self:T2(ARTY.id..string.format("Key Aliases=%s.", v[i])) + end + + elseif key:lower():find("cluster") then + + local v=self:_split(keyphrase, '"') + + for i=2,#v,2 do + table.insert(assignment.cluster, v[i]) + self:T2(ARTY.id..string.format("Key Cluster=%s.", v[i])) + end elseif (assignment.engage or assignment.move) and key:lower():find("time") then From 89e67ba54d6b16bbb2be7b5e5fc36dc1ac04d29c Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Tue, 12 Jun 2018 19:50:21 +0200 Subject: [PATCH 171/420] ARTY v1.0.2 Changed behavior that if no arty group is addressed explicitly, nothing is happening. Before all groups were addressed. --- .../Moose/Functional/Artillery.lua | 61 ++++++++++++------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index d2c1ad784..fa5d346d9 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -285,33 +285,40 @@ -- ARTY group are able to assign targets and moves without a key. -- -- ### Target Assignments --- A new target can be assigned by writing **arty engage** in the marker text. This can be followed by a **comma separated list** of optional keywords and parameters: +-- A new target can be assigned by writing **arty engage** in the marker text. +-- This is followed by a **comma separated list** of (optional) keywords and parameters. +-- First, it is important to address the ARTY group or groups that should engage. This can be done in numrous ways. The keywords are *battery*, *alias*, *cluster*. +-- It is also possible to address all ARTY groups by the keyword *everyone* or *allbatteries*. These two can be used synonymously. +-- **Note that**, if no battery is assigned nothing will happen. -- +-- * *everyone* or *allbatteries* The target is assigned to all batteries. +-- * *battery* Name of the ARTY group that the target is assigned to. Note that **the name is case sensitive** and has to be given in quotation marks. Default is all ARTY groups of the right coalition. +-- * *alias* Alias of the ARTY group that the target is assigned to. The alias is **case sensitive** and needs to be in quotation marks. +-- * *cluster* The cluster of ARTY groups that is addessed. Clusters can be defined by the function @{#ARTY.AddToCluster}(*clusters*). Names are **case sensitive** and need to be in quotation marks. +-- * *key* A number to authorize the target assignment. Only specifing the correct number will trigger an engagement. -- * *time* Time for which which the engagement is schedules, e.g. 08:42. Default is as soon as possible. -- * *prio* Priority of the engagement as number between 1 (high prio) and 100 (low prio). Default is 50, i.e. medium priority. -- * *shots* Number of shots (shells, rockets or missiles) fired at each engagement. Default is 5. -- * *maxengage* Number of times the target is engaged. Default is 1. -- * *radius* Scattering radius of the fired shots in meters. Default is 100 m. -- * *weapon* Type of weapon to be used. Valid parameters are *cannon*, *rocket*, *missile*, *nuke*. Default is automatic selection. --- * *battery* Name of the ARTY group that the target is assigned to. Note that **the name is case sensitive** and has to be given in quotation marks. Default is all ARTY groups of the right coalition. --- * *alias* Alias of the ARTY group that the target is assigned to. The alias is **case sensitive** and needs to be in quotation marks. --- * *cluster* The cluster of ARTY groups that is addessed. Clusters can be defined by the function @{#ARTY.AddToCluster}(*clusters*). Names are **case sensitive** and need to be in quotation marks. --- * *key* A number to authorize the target assignment. Only specifing the correct number will trigger an engagement. -- * *lldms* Specify the coordinates in Lat/Long degrees, minutes and seconds format. The actual location of the marker is unimportant here. The group will engage the coordinates given in the lldms keyword. -- Format is DD:MM:SS[N,S] DD:MM:SS[W,E]. See example below. This can be useful when coordinates in this format are obtained from elsewhere. -- * *readonly* The marker is readonly and cannot be deleted by users. Hence, assignment cannot be cancelled by removing the marker. -- -- Here are examples of valid marker texts: --- arty engage +-- arty engage, battery "Blue Paladin Alpha" +-- arty engage, everyone +-- arty engage, allbatteries +-- arty engage, alias "Bob", weapon missiles +-- arty engage, cluster "All Mortas" +-- arty engage, cluster "Northern Batteries" "Southern Batteries" +-- arty engage, cluster "Northern Batteries", cluster "Southern Batteries" -- arty engage, shots 20, prio 10, time 08:15, weapon cannons -- arty engage, battery "Blue Paladin 1" "Blue MRLS 1", shots 10, time 10:15 -- arty engage, battery "Blue MRLS 1", key 666 -- arty engage, battery "Paladin Alpha", weapon nukes, shots 1, time 20:15 -- arty engage, lldms 41:51:00N 41:47:58E --- arty engage, alias "Bob", weapon missiles --- arty engage, cluster "All Mortas" --- arty engage, cluster "Northern Batteries" "Southern Batteries" --- arty engage, cluster "Northern Batteries", cluster "Southern Batteries" -- -- Note that the keywords and parameters are *case insensitve*. Only exception are the battery, alias and cluster names. -- These must be exactly the same as the names of the goups defined in the mission editor or the aliases and cluster names defined in the script. @@ -333,15 +340,14 @@ -- * *readonly* Marker cannot be deleted by users any more. Hence, assignment cannot be cancelled by removing the marker. -- -- Here are some examples: --- arty move --- arty move, time 23:45, speed 50, on road -- arty move, battery "Blue Paladin" -- arty move, battery "Blue MRLS", canceltarget, speed 10, on road --- arty move, lldms 41:51:00N 41:47:58E +-- arty move, cluster "mobile", lldms 41:51:00N 41:47:58E -- arty move, alias "Bob", weapon missiles -- arty move, cluster "All Howitzer" -- arty move, cluster "Northern Batteries" "Southern Batteries" -- arty move, cluster "Northern Batteries", cluster "Southern Batteries" +-- arty move, everyone -- -- ### Requests -- @@ -352,7 +358,7 @@ -- * *ammo* Current ammunition status is reported. -- -- For example --- arty request, ammo +-- arty request, everyone, ammo -- arty request, battery "Paladin Bravo", targets -- arty request, cluster "All Mortars", move -- @@ -586,7 +592,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #string version -ARTY.version="1.0.1" +ARTY.version="1.0.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -614,6 +620,7 @@ ARTY.version="1.0.1" -- TODO: Add set commands via markers. E.g. set rearming place. -- DONE: Test stationary types like mortas ==> rearming etc. -- TODO: Add hit event and make the arty group relocate. +-- TODO: Add illumination and smoke. --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1901,7 +1908,7 @@ function ARTY:_OnEventMarkRemove(Event) -- Get battery coalition and name. local batterycoalition=self.Controllable:GetCoalition() - local batteryname=self.groupname + --local batteryname=self.groupname if Event.text~=nil and Event.text:find("BATTERY") then @@ -1986,6 +1993,7 @@ function ARTY:_OnEventMarkChange(Event) -- Check if ENGAGE or MOVE or REQUEST keywords were found. if _assign==nil or not (_assign.engage or _assign.move or _assign.request or _assign.cancel) then + self:T(ARTY.id..string.format("WARNING: %s, no keyword ENGAGE, MOVE, REQUEST or CANCEL in mark text! Command will not be executed. Text:\n%s", self.groupname, Event.text)) return end @@ -1993,7 +2001,10 @@ function ARTY:_OnEventMarkChange(Event) local _assigned=false -- If any array is filled something has been assigned. - if #_assign.battery>0 or #_assign.aliases>0 or #_assign.cluster>0 then + if _assign.everyone then + -- Everyone was addressed. + _assigned=true + else --#_assign.battery>0 or #_assign.aliases>0 or #_assign.cluster>0 then -- Loop over batteries. for _,bat in pairs(_assign.battery) do @@ -2017,14 +2028,12 @@ function ARTY:_OnEventMarkChange(Event) end end end - - else - -- No one explicitly assigned ==> we assume to be assigned. - _assigned=true + end -- We were not addressed. if not _assigned then + self:T3(ARTY.id..string.format("INFO: ARTY group %s was not addressed! Mark text:\n%s", self.groupname, Event.text)) return end @@ -3411,7 +3420,7 @@ end function ARTY:_MarkerKeyAuthentification(text) -- Set battery and coalition. - local batteryname=self.groupname + --local batteryname=self.groupname local batterycoalition=self.Controllable:GetCoalition() -- Get assignment. @@ -3443,7 +3452,7 @@ function ARTY:_MarkerKeyAuthentification(text) if mykey~=nil then _validkey=self.markkey==mykey end - self:T2(ARTY.id..string.format("%s, authkey=%s == %s=playerkey ==> valid=%s", batteryname, tostring(self.markkey), tostring(mykey), tostring(_validkey))) + self:T2(ARTY.id..string.format("%s, authkey=%s == %s=playerkey ==> valid=%s", self.groupname, tostring(self.markkey), tostring(mykey), tostring(_validkey))) -- Send message local text="" @@ -3472,6 +3481,7 @@ function ARTY:_Markertext(text) assignment.battery={} assignment.aliases={} assignment.cluster={} + assignment.everyone=false assignment.move=false assignment.engage=false assignment.request=false @@ -3537,6 +3547,11 @@ function ARTY:_Markertext(text) table.insert(assignment.cluster, v[i]) self:T2(ARTY.id..string.format("Key Cluster=%s.", v[i])) end + + elseif keyphrase:lower():find("everyone") or keyphrase:lower():find("all batteries") or keyphrase:lower():find("allbatteries") then + + assignment.everyone=true + self:T(ARTY.id..string.format("Key Everyone=true.")) elseif (assignment.engage or assignment.move) and key:lower():find("time") then From 6c9c4432cc09c37b2c0087bb9027b97c01c191e8 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 13 Jun 2018 00:12:26 +0200 Subject: [PATCH 172/420] ARTY v1.0.3 Added "set" mark commands for rearming place and rearming group. --- .../Moose/Functional/Artillery.lua | 106 +++++++++++++----- 1 file changed, 77 insertions(+), 29 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index fa5d346d9..79fbbdabd 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -1980,6 +1980,14 @@ function ARTY:_OnEventMarkChange(Event) -- Check if marker has a text and the "arty" keyword. if Event.text~=nil and Event.text:lower():find("arty") then + + -- Convert (wrong x-->z, z-->x) vec3 + -- TODO: This needs to be "fixed", once DCS gives the correct numbers for x and z. + -- local vec3={y=Event.pos.y, x=Event.pos.x, z=Event.pos.z} + local vec3={y=Event.pos.y, x=Event.pos.z, z=Event.pos.x} + + -- Get coordinate from vec3. + local _coord=COORDINATE:NewFromVec3(vec3) -- Get battery coalition and name. local batterycoalition=self.Controllable:GetCoalition() @@ -1992,8 +2000,8 @@ function ARTY:_OnEventMarkChange(Event) local _assign=self:_Markertext(Event.text) -- Check if ENGAGE or MOVE or REQUEST keywords were found. - if _assign==nil or not (_assign.engage or _assign.move or _assign.request or _assign.cancel) then - self:T(ARTY.id..string.format("WARNING: %s, no keyword ENGAGE, MOVE, REQUEST or CANCEL in mark text! Command will not be executed. Text:\n%s", self.groupname, Event.text)) + if _assign==nil or not (_assign.engage or _assign.move or _assign.request or _assign.cancel or _assign.set) then + self:T(ARTY.id..string.format("WARNING: %s, no keyword ENGAGE, MOVE, REQUEST, CANCEL or SET in mark text! Command will not be executed. Text:\n%s", self.groupname, Event.text)) return end @@ -2001,9 +2009,11 @@ function ARTY:_OnEventMarkChange(Event) local _assigned=false -- If any array is filled something has been assigned. - if _assign.everyone then + if _assign.everyone then + -- Everyone was addressed. _assigned=true + else --#_assign.battery>0 or #_assign.aliases>0 or #_assign.cluster>0 then -- Loop over batteries. @@ -2036,7 +2046,12 @@ function ARTY:_OnEventMarkChange(Event) self:T3(ARTY.id..string.format("INFO: ARTY group %s was not addressed! Mark text:\n%s", self.groupname, Event.text)) return end - + + -- Coordinate was given in text, e.g. as lat, long. + if _assign.coord then + _coord=_assign.coord + end + -- Check if the authorization key is required and if it is valid. local _validkey=self:_MarkerKeyAuthentification(Event.text) @@ -2061,7 +2076,7 @@ function ARTY:_OnEventMarkChange(Event) return end - -- Cancel current target and return. + -- Cancel stuff and return. if _assign.cancel and _validkey then if _assign.cancelmove and self.currentMove then self.Controllable:ClearTasks() @@ -2080,27 +2095,37 @@ function ARTY:_OnEventMarkChange(Event) -- Cancels Done ==> End of story! return end + + -- Set stuff and return. + if _assign.set and _validkey then + if _assign.setrearmingplace then + self:SetRearmingPlace(_coord) + _coord:RemoveMark(Event.idx) + _coord:MarkToCoalition(string.format("Rearming place for battery %s", self.groupname), self.Controllable:GetCoalition(), false, string.format("New rearming place for battery %s defined.", self.groupname)) + if self.Debug then + _coord:SmokeOrange() + end + end + if _assign.setrearminggroup then + _coord:RemoveMark(Event.idx) + local rearminggroupcoord=_assign.setrearminggroup:GetCoordinate() + rearminggroupcoord:MarkToCoalition(string.format("Rearming group for battery %s", self.groupname), self.Controllable:GetCoalition(), false, string.format("New rearming group for battery %s defined.", self.groupname)) + self:SetRearmingGroup(_assign.setrearminggroup) + if self.Debug then + rearminggroupcoord:SmokeOrange() + end + end + -- Set stuff Done ==> End of story! + return + end -- Handle engagements and relocations. if _validkey then - - -- Convert (wrong x-->z, z-->x) vec3 - -- TODO: This needs to be "fixed", once DCS gives the correct numbers for x and z. - -- local vec3={y=Event.pos.y, x=Event.pos.x, z=Event.pos.z} - local vec3={y=Event.pos.y, x=Event.pos.z, z=Event.pos.x} - - -- Get coordinate from vec3. - local _coord=COORDINATE:NewFromVec3(vec3) -- Remove old mark because it might contain confidential data such as the key. -- Also I don't know who can see the mark which was created. _coord:RemoveMark(Event.idx) - -- Coordinate was given in text, e.g. as lat, long. - if _assign.coord then - _coord=_assign.coord - end - -- Anticipate marker ID. -- WARNING: Make sure, no marks are set until the COORDINATE:MarkToCoalition() is called or the target/move name will be wrong and target cannot be removed by deleting its marker. local _id=UTILS._MarkID+1 @@ -3486,11 +3511,14 @@ function ARTY:_Markertext(text) assignment.engage=false assignment.request=false assignment.cancel=false + assignment.set=false assignment.readonly=false assignment.movecanceltarget=false assignment.cancelmove=false assignment.canceltarget=false assignment.cancelrearm=false + assignment.setrearmingplace=false + assignment.setrearminggroup=false -- Check for correct keywords. if text:lower():find("arty engage") or text:lower():find("arty attack") then @@ -3500,9 +3528,11 @@ function ARTY:_Markertext(text) elseif text:lower():find("arty request") then assignment.request=true elseif text:lower():find("arty cancel") then - assignment.cancel=true + assignment.cancel=true + elseif text:lower():find("arty set") then + assignment.set=true else - self:E(ARTY.id..'ERROR: Neither "ARTY ENGAGE" nor "ARTY MOVE" nor "ARTY RELOCATE" nor "ARTY REQUEST" nor "ARTY CANCEL" keyword specified!') + self:E(ARTY.id..'ERROR: Neither "ARTY ENGAGE" nor "ARTY MOVE" nor "ARTY RELOCATE" nor "ARTY REQUEST" nor "ARTY CANCEL" nor "ARTY SET" keyword specified!') return nil end @@ -3551,8 +3581,13 @@ function ARTY:_Markertext(text) elseif keyphrase:lower():find("everyone") or keyphrase:lower():find("all batteries") or keyphrase:lower():find("allbatteries") then assignment.everyone=true - self:T(ARTY.id..string.format("Key Everyone=true.")) - + self:T(ARTY.id..string.format("Key Everyone=true.")) + + elseif keyphrase:lower():find("irrevocable") or keyphrase:lower():find("readonly") then + + assignment.readonly=true + self:T2(ARTY.id..string.format("Key Readonly=true.")) + elseif (assignment.engage or assignment.move) and key:lower():find("time") then if val:lower():find("now") then @@ -3597,21 +3632,16 @@ function ARTY:_Markertext(text) end self:T2(ARTY.id..string.format("Key Weapon=%s.", val)) - elseif assignment.move and key:lower():find("speed") then + elseif (assignment.move or assignment.set) and key:lower():find("speed") then assignment.speed=tonumber(val) self:T2(ARTY.id..string.format("Key Speed=%s.", val)) - elseif assignment.move and (keyphrase:lower():find("on road") or keyphrase:lower():find("onroad") or keyphrase:lower():find("use road")) then + elseif (assignment.move or assignment.set) and (keyphrase:lower():find("on road") or keyphrase:lower():find("onroad") or keyphrase:lower():find("use road")) then assignment.onroad=true self:T2(ARTY.id..string.format("Key Onroad=true.")) - elseif keyphrase:lower():find("irrevocable") or keyphrase:lower():find("readonly") then - - assignment.readonly=true - self:T2(ARTY.id..string.format("Key Readonly=true.")) - elseif assignment.move and (keyphrase:lower():find("cancel target") or keyphrase:lower():find("canceltarget")) then assignment.movecanceltarget=true @@ -3656,6 +3686,24 @@ function ARTY:_Markertext(text) assignment.cancelrearm=true self:T2(ARTY.id..string.format("Key Cancel Rearm=true.")) + + elseif assignment.set and keyphrase:lower():find("rearming place") then + + assignment.setrearmingplace=true + self:T(ARTY.id..string.format("Key Set Rearming Place=true.")) + + elseif assignment.set and keyphrase:lower():find("rearming group") then + + local v=self:_split(keyphrase, '"') + local groupname=v[2] + env.info("FF v2 groupname = "..tostring(v[2])) + + local group=GROUP:FindByName(groupname) + if group and group:IsAlive() then + assignment.setrearminggroup=group + end + + self:T2(ARTY.id..string.format("Key Set Rearming Group = %s.", tostring(groupname))) elseif key:lower():find("lldms") then From 851c55e9854af7f09680392724bd723b8351fe1c Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 13 Jun 2018 23:00:05 +0200 Subject: [PATCH 173/420] ARTY v1.0.3 buggy something wrong with nuke, illu smoke --- Moose Development/Moose/Core/Point.lua | 5 +- .../Moose/Functional/Artillery.lua | 192 ++++++++++++++++-- 2 files changed, 180 insertions(+), 17 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index b7688ad82..35eb6566d 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -998,9 +998,10 @@ do -- COORDINATE --- Creates an illumination bomb at the point. -- @param #COORDINATE self - function COORDINATE:IlluminationBomb() + -- @param #number power + function COORDINATE:IlluminationBomb(power) self:F2() - trigger.action.illuminationBomb( self:GetVec3() ) + trigger.action.illuminationBomb( self:GetVec3(), power ) end diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 79fbbdabd..a8f8758a0 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -55,6 +55,8 @@ -- @field #number Nrockets0 Initial amount of rockets of the whole group. -- @field #number Nmissiles0 Initial amount of missiles of the whole group. -- @field #number Nukes0 Initial amount of tactical nukes of the whole group. Default is 0. +-- @field #number Nillu0 Initial amount of illumination shells of the whole group. Default is 0. +-- @field #number Nsmoke0 Initial amount of smoke shells of the whole group. Default is 0. -- @field #number StatusInterval Update interval in seconds between status updates. Default 10 seconds. -- @field #number WaitForShotTime Max time in seconds to wait until fist shot event occurs after target is assigned. If time is passed without shot, the target is deleted. Default is 300 seconds. -- @field #table DCSdesc DCS descriptors of the ARTY group. @@ -84,7 +86,9 @@ -- @field #number minrange Minimum firing range in kilometers. Targets closer than this distance are not engaged. Default 0.1 km. -- @field #number maxrange Maximum firing range in kilometers. Targets further away than this distance are not engaged. Default 10000 km. -- @field #number nukewarhead Explosion strength of tactical nuclear warhead in kg TNT. Default 75000. --- @field #number Nukes Number of nuclear shells, the group has available. Default is same number as normal shells. Note that if normal shells are empty, firing nukes is also not possible any more. +-- @field #number Nukes Number of nuclear shells, the group has available. Note that if normal shells are empty, firing nukes is also not possible any more. +-- @field #number Nillu Number of illumination shells the group has available. Note that if normal shells are empty, firing illumination shells is also not possible any more. +-- @filed #number Nsmoke Number of smoke shells the group has available. Note that if normal shells are empty, firing smoke shells is also not possible any more. -- @field #number nukerange Demolition range of tactical nuclear explostions. -- @field #boolean nukefire Ignite additional fires and smoke for nuclear explosions Default true. -- @field #number nukefires Number of nuclear fires and subexplosions. @@ -226,6 +230,8 @@ -- * @{#ARTY.WeaponType}.Rockets: Only unguided are used during the attack. Corresponding ammo type are rockets/nurs and can be defined by @{#ARTY.SetRocketTypes}. -- * @{#ARTY.WeaponType}.CruiseMissile: Only cruise missiles are used during the attack. Corresponding ammo type are missiles and can be defined by @{#ARTY.SetMissileTypes}. -- * @{#ARTY.WeaponType}.TacticalNukes: Use tactical nuclear shells. This works only with units that have shells and is described below. +-- * @{#ARTY.WeaponType}.IlluminationShells: Use illumination shells. This works only with units that have shells and is described below. +-- * @{#ARTY.WeaponType}.SmokeShells: Use smoke shells. This works only with units that have shells and is described below. -- -- ## Assigning Moves -- The ARTY group can be commanded to move. This is done by the @{#ARTY.AssignMoveCoord}(*coord*,*time*,*speed*,*onroad*,*cancel*,*name*) function. @@ -471,6 +477,8 @@ ARTY={ Nrockets0=0, Nmissiles0=0, Nukes0=0, + Nillu0=0, + Nsmoke0=0, StatusInterval=10, WaitForShotTime=300, DCSdesc=nil, @@ -499,6 +507,8 @@ ARTY={ maxrange=1000000, nukewarhead=75000, Nukes=nil, + Nillu=nil, + Nsmoke=nil, nukefire=false, nukefires=nil, nukerange=nil, @@ -519,11 +529,10 @@ ARTY.WeaponType={ Auto=1073741822, Cannon=805306368, Rockets=30720, - --UnguidedAny=805339120, - --GuidedMissile=268402688, CruiseMissile=2097152, - --AntiShipMissile=65536, TacticalNukes=666, + IlluminationShells=667, + SmokeShells=668, } --- Database of common artillery unit properties. @@ -1440,6 +1449,22 @@ function ARTY:SetTacNukeWarhead(strength) self.nukewarhead=self.nukewarhead*1000*1000 -- convert to kg TNT. end +--- Set number of illumination shells available to the group. +-- Note that it can be max the number of normal shells. Also if all normal shells are empty, firing illumination shells is also not possible any more until group gets rearmed. +-- @param #ARTY self +-- @param #number n Number of warheads for the whole group. +function ARTY:SetIlluminationShells(n) + self.Nillu=n +end + +--- Set number of smoke shells available to the group. +-- Note that it can be max the number of normal shells. Also if all normal shells are empty, firing smoke shells is also not possible any more until group gets rearmed. +-- @param #ARTY self +-- @param #number n Number of warheads for the whole group. +function ARTY:SetSmokeShells(n) + self.Nsmoke=n +end + --- Set nuclear fires and extra demolition explosions. -- @param #ARTY self -- @param #number nfires (Optional) Number of big smoke and fire objects created in the demolition zone. @@ -1497,6 +1522,8 @@ function ARTY:onafterStart(Controllable, From, Event, To) if self.nukefires==nil then self.nukefires=20/1000/1000*self.nukerange*self.nukerange end + + -- Init nuclear shells. if self.Nukes~=nil then self.Nukes0=math.min(self.Nukes, self.Nshells0) else @@ -1504,6 +1531,22 @@ function ARTY:onafterStart(Controllable, From, Event, To) self.Nukes0=0 end + -- Init illumination shells. + if self.Nillu~=nil then + self.Nillu0=math.min(self.Nillu, self.Nshells0) + else + self.Nillu=0 + self.Nillu0=0 + end + + -- Init smoke shells. + if self.Nsmoke~=nil then + self.Nsmoke0=math.min(self.Nsmoke, self.Nshells0) + else + self.Nsmoke=0 + self.Nsmoke0=0 + end + -- Check if we have and arty type that is in the DB. local _dbproperties=self:_CheckDB(self.DisplayName) self:T({dbproperties=_dbproperties}) @@ -1561,6 +1604,8 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("Nuclear warhead = %d tons TNT\n", self.nukewarhead/1000) text=text..string.format("Nuclear demolition = %d m\n", self.nukerange) text=text..string.format("Nuclear fires = %d (active=%s)\n", self.nukefires, tostring(self.nukefire)) + text=text..string.format("Number of illum. = %d\n", self.Nillu0) + text=text..string.format("Number of smoke = %d\n", self.Nsmoke0) if self.RearmingGroup or self.RearmingPlaceCoord then text=text..string.format("Rearming safe dist. = %d m\n", self.RearmingDistance) end @@ -1668,6 +1713,8 @@ function ARTY:_StatusReport(display) -- Get Ammo. local Nammo, Nshells, Nrockets, Nmissiles=self:GetAmmo() local Nnukes=self.Nukes + local Nillu=self.Nillu + local Nsmoke=self.Nsmoke local Tnow=timer.getTime() local Clock=self:_SecondsToClock(timer.getAbsTime()) @@ -1681,6 +1728,8 @@ function ARTY:_StatusReport(display) text=text..string.format("Number of rockets = %d\n", Nrockets) text=text..string.format("Number of missiles = %d\n", Nmissiles) text=text..string.format("Number of nukes = %d\n", Nnukes) + text=text..string.format("Number of illum. = %d\n", Nillu) + text=text..string.format("Number of smoke = %d\n", Nsmoke) if self.currentTarget then text=text..string.format("Current Target = %s\n", tostring(self.currentTarget.name)) text=text..string.format("Curr. Tgt assigned = %d\n", Tnow-self.currentTarget.Tassigned) @@ -1747,10 +1796,16 @@ function ARTY:_OnEventShot(EventData) -- Last known position of the weapon fired. local _lastpos={x=0, y=0, z=0} - --- Track the position of the weapon if it is supposed to model a tac nuke. + --- Track the position of the weapon if it is supposed to model a tac nuke, illumination or smoke shell. -- @param #table _weapon local function _TrackWeapon(_weapon) + -- Coordinate of the target + local _targetcoord=self.currentTarget.coord -- Core.Point#COORDINATE + self.illuMinalt=400 + self.illuMaxalt=1000 + self.illuPower=1000 + -- When the pcall status returns false the weapon has hit. local _status,_currpos = pcall( function() @@ -1758,31 +1813,94 @@ function ARTY:_OnEventShot(EventData) end) self:T(ARTY.id..string.format("ARTY %s: Weapon still in air: %s", self.groupname, tostring(_status))) + local _destroyweapon=false if _status then -- Update last position. _lastpos={x=_currpos.x, y=_currpos.y, z=_currpos.z} +--[[ + -- Coordinate and distance to target. + local _coord=COORDINATE:NewFromVec3(_lastpos) + local _dist=_coord:Get2DDistance(self.currentTarget.coord) - -- Check again in 0.05 seconds. - --return timer.getTime() + self.dtBombtrack + env.info("FF dist = ".._dist) + + if self.currentTarget.weapontype==ARTY.WeaponType.IlluminationShells then + + -- Check if within distace. + if _dist0 then - self:T(ARTY.id..string.format("ARTY %s: Tracking of weapon starts in two seconds.", self.groupname)) - timer.scheduleFunction(_TrackWeapon, EventData.weapon, timer.getTime() + 2.0) + local _tracknuke = self.currentTarget.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes>0 + local _trackillu = self.currentTarget.weapontype==ARTY.WeaponType.IlluminationShells and self.Nillu>0 + local _tracksmoke = self.currentTarget.weapontype==ARTY.WeaponType.SmokeShells and self.Nsmoke>0 + if _tracknuke or _trackillu or _tracksmoke then + self:T(ARTY.id..string.format("ARTY %s: Tracking of weapon starts in one second.", self.groupname)) + timer.scheduleFunction(_TrackWeapon, EventData.weapon, timer.getTime() + 1.0) end -- Get current ammo. @@ -1792,6 +1910,16 @@ function ARTY:_OnEventShot(EventData) if self.currentTarget.weapontype==ARTY.WeaponType.TacticalNukes then self.Nukes=self.Nukes-1 end + + -- Decrease available illuminatin shells because we just fired one. + if self.currentTarget.weapontype==ARTY.WeaponType.IlluminationShells then + self.Nillu=self.Nillu-1 + end + + -- Decrease available illuminatin shells because we just fired one. + if self.currentTarget.weapontype==ARTY.WeaponType.SmokeShells then + self.Nsmoke=self.Nsmoke-1 + end -- Check if we are completely out of ammo. local _outofammo=false @@ -2463,6 +2591,12 @@ function ARTY:onafterOpenFire(Controllable, From, Event, To, target) elseif target.weapontype==ARTY.WeaponType.TacticalNukes then nfire=self.Nukes _type="nuclear shells" + elseif target.weapontype==ARTY.WeaponType.IlluminationShells then + nfire=self.Nillu + _type="illumination shells" + elseif target.weapontype==ARTY.WeaponType.SmokeShells then + nfire=self.Nsmoke + _type="smoke shells" elseif target.weapontype==ARTY.WeaponType.Rockets then nfire=Nrockets _type="rockets" @@ -2704,6 +2838,8 @@ function ARTY:onafterRearmed(Controllable, From, Event, To) -- "Rearm" tactical nukes as well. self.Nukes=self.Nukes0 + self.Nillu=self.Nillu0 + self.Nsmoke=self.Nsmoke0 -- Route ARTY group back to where it came from (if distance is > 100 m). local dist=self.Controllable:GetCoordinate():Get2DDistance(self.InitialCoord) @@ -2960,7 +3096,7 @@ function ARTY:_FireAtCoord(coord, radius, nshells, weapontype) local group=self.Controllable --Wrapper.Group#GROUP -- Tactical nukes are actually cannon shells. - if weapontype==ARTY.WeaponType.TacticalNukes then + if weapontype==ARTY.WeaponType.TacticalNukes or weapontype==ARTY.WeaponType.IlluminationShells or weapontype==ARTY.WeaponType.SmokeShells then weapontype=ARTY.WeaponType.Cannon end @@ -3627,6 +3763,10 @@ function ARTY:_Markertext(text) assignment.weapontype=ARTY.WeaponType.CruiseMissile elseif val:lower():find("nuke") then assignment.weapontype=ARTY.WeaponType.TacticalNukes + elseif val:lower():find("illu") then + assignment.weapontype=ARTY.WeaponType.IlluminationShells + elseif val:lower():find("smoke") then + assignment.weapontype=ARTY.WeaponType.SmokeShells else assignment.weapontype=ARTY.WeaponType.Auto end @@ -4223,6 +4363,16 @@ function ARTY:_CheckOutOfAmmo(targets) self:T(ARTY.id..string.format("Group %s, tactical nukes requested for target %s but nukes empty.", self.groupname, Target.name)) _partlyoutofammo=true + + elseif Target.weapontype==ARTY.WeaponType.IlluminationShells and self.Nillu<=0 then + + self:T(ARTY.id..string.format("Group %s, illumination shells requested for target %s but illumination shells empty.", self.groupname, Target.name)) + _partlyoutofammo=true + + elseif Target.weapontype==ARTY.WeaponType.SmokeShells and self.Nsmoke<=0 then + + self:T(ARTY.id..string.format("Group %s, smoke shells requested for target %s but smoke shells empty.", self.groupname, Target.name)) + _partlyoutofammo=true elseif Target.weapontype==ARTY.WeaponType.Rockets and _nrockets==0 then @@ -4258,6 +4408,10 @@ function ARTY:_CheckWeaponTypeAvailable(target) nfire=Nshells elseif target.weapontype==ARTY.WeaponType.TacticalNukes then nfire=self.Nukes + elseif target.weapontype==ARTY.WeaponType.IlluminationShells then + nfire=self.Nillu + elseif target.weapontype==ARTY.WeaponType.SmokeShells then + nfire=self.Nsmoke elseif target.weapontype==ARTY.WeaponType.Rockets then nfire=Nrockets elseif target.weapontype==ARTY.WeaponType.CruiseMissile then @@ -4280,6 +4434,10 @@ function ARTY:_CheckWeaponTypePossible(target) possible=self.Nshells0>0 elseif target.weapontype==ARTY.WeaponType.TacticalNukes then possible=self.Nukes0>0 + elseif target.weapontype==ARTY.WeaponType.IlluminationShells then + possible=self.Nillus0>0 + elseif target.weapontype==ARTY.WeaponType.SmokeShells then + possible=self.Nsmoke0>0 elseif target.weapontype==ARTY.WeaponType.Rockets then possible=self.Nrockets0>0 elseif target.weapontype==ARTY.WeaponType.CruiseMissile then @@ -4420,7 +4578,11 @@ function ARTY:_WeaponTypeName(tnumber) elseif tnumber==ARTY.WeaponType.CruiseMissile then name="Cruise Missiles" elseif tnumber==ARTY.WeaponType.TacticalNukes then - name="Tactical Nukes" + name="Tactical Nukes" + elseif tnumber==ARTY.WeaponType.IlluminationShells then + name="Illumination Shells" + elseif tnumber==ARTY.WeaponType.SmokeShells then + name="Smoke Shells" end return name end From ca2eec88780a9da7cb538f25fb426efaf52a13f4 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Fri, 15 Jun 2018 00:07:35 +0200 Subject: [PATCH 174/420] ARTY v1.0.3 Fixed bug in illu and smoke shell impementation. --- .../Moose/Functional/Artillery.lua | 147 +++++++++++------- 1 file changed, 92 insertions(+), 55 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index a8f8758a0..c3e03b6f7 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -88,7 +88,11 @@ -- @field #number nukewarhead Explosion strength of tactical nuclear warhead in kg TNT. Default 75000. -- @field #number Nukes Number of nuclear shells, the group has available. Note that if normal shells are empty, firing nukes is also not possible any more. -- @field #number Nillu Number of illumination shells the group has available. Note that if normal shells are empty, firing illumination shells is also not possible any more. --- @filed #number Nsmoke Number of smoke shells the group has available. Note that if normal shells are empty, firing smoke shells is also not possible any more. +-- @field #number illuPower Power of illumination warhead in mega candela. Default 1 mcd. +-- @field #number illuMinalt Minimum altitude in meters the illumination warhead will detonate. +-- @field #number illuMaxalt Maximum altitude in meters the illumination warhead will detonate. +-- @field #number Nsmoke Number of smoke shells the group has available. Note that if normal shells are empty, firing smoke shells is also not possible any more. +-- @field Utilities.Utils#SMOKECOLOR Smoke color of smoke shells. Default SMOKECOLOR.red. -- @field #number nukerange Demolition range of tactical nuclear explostions. -- @field #boolean nukefire Ignite additional fires and smoke for nuclear explosions Default true. -- @field #number nukefires Number of nuclear fires and subexplosions. @@ -320,11 +324,11 @@ -- arty engage, cluster "All Mortas" -- arty engage, cluster "Northern Batteries" "Southern Batteries" -- arty engage, cluster "Northern Batteries", cluster "Southern Batteries" --- arty engage, shots 20, prio 10, time 08:15, weapon cannons +-- arty engage, cluster "Horwitzers", shots 20, prio 10, time 08:15, weapon cannons -- arty engage, battery "Blue Paladin 1" "Blue MRLS 1", shots 10, time 10:15 -- arty engage, battery "Blue MRLS 1", key 666 -- arty engage, battery "Paladin Alpha", weapon nukes, shots 1, time 20:15 --- arty engage, lldms 41:51:00N 41:47:58E +-- arty engage, battery "Horwitzer 1", lldms 41:51:00N 41:47:58E -- -- Note that the keywords and parameters are *case insensitve*. Only exception are the battery, alias and cluster names. -- These must be exactly the same as the names of the goups defined in the mission editor or the aliases and cluster names defined in the script. @@ -376,7 +380,7 @@ -- -- For example -- arty cancel, target, battery "Paladin Bravo" --- arty cancel, move +-- arty cancel, everyone, move -- arty cancel, rearming, battery "MRLS Charly" -- -- @@ -507,11 +511,15 @@ ARTY={ maxrange=1000000, nukewarhead=75000, Nukes=nil, - Nillu=nil, - Nsmoke=nil, nukefire=false, nukefires=nil, nukerange=nil, + Nillu=nil, + illuPower=1000000, + illuMinalt=500, + illuMaxalt=1500, + Nsmoke=nil, + smokeColor=SMOKECOLOR.Red, relocateafterfire=false, relocateRmin=300, relocateRmax=800, @@ -626,10 +634,10 @@ ARTY.version="1.0.2" -- DONE: Improve handling of special weapons. When winchester if using selected weapons? -- TODO: Handle rearming for ships. How? -- DONE: Make coordinate after rearming general, i.e. also work after the group has moved to anonther location. --- TODO: Add set commands via markers. E.g. set rearming place. +-- DONE: Add set commands via markers. E.g. set rearming place. -- DONE: Test stationary types like mortas ==> rearming etc. -- TODO: Add hit event and make the arty group relocate. --- TODO: Add illumination and smoke. +-- DONE: Add illumination and smoke. --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1452,17 +1460,36 @@ end --- Set number of illumination shells available to the group. -- Note that it can be max the number of normal shells. Also if all normal shells are empty, firing illumination shells is also not possible any more until group gets rearmed. -- @param #ARTY self --- @param #number n Number of warheads for the whole group. -function ARTY:SetIlluminationShells(n) +-- @param #number n Number of illumination shells for the whole group. +-- @param #number power (Optional) Power of illumination warhead in mega candela. Default 1.0 mcd. +function ARTY:SetIlluminationShells(n, power) self.Nillu=n + self.illuPower=power or 1.0 + self.illuPower=self.illuPower * 1000000 +end + +--- Set minimum and maximum detotation altitude for illumination shells. A value between min/max is selected randomly. +-- The illumination bomb will burn for 300 seconds (5 minutes). Assuming a descent rate of 5 m/s the "optimal" altitude would be 1500 m. +-- @param #ARTY self +-- @param #number minalt (Optional) Minium altitude in meters. Default 500 m. +-- @param #number maxalt (Optional) Maximum altitude in meters. Default 1500 m. +function ARTY:SetIlluminationMinMaxAlt(minalt, maxalt) + self.illuMinalt=minalt or 500 + self.illuMaxalt=maxalt or 1500 + + if self.illuMinalt>self.illuMaxalt then + self.illuMinalt=self.illuMaxalt + end end --- Set number of smoke shells available to the group. -- Note that it can be max the number of normal shells. Also if all normal shells are empty, firing smoke shells is also not possible any more until group gets rearmed. -- @param #ARTY self --- @param #number n Number of warheads for the whole group. -function ARTY:SetSmokeShells(n) +-- @param #number n Number of smoke shells for the whole group. +-- @param Utilities.Utils#SMOKECOLOR color (Optional) Color of the smoke. Default SMOKECOLOR.Red. +function ARTY:SetSmokeShells(n, color) self.Nsmoke=n + self.smokeColor=color or SMOKECOLOR.Red end --- Set nuclear fires and extra demolition explosions. @@ -1605,7 +1632,11 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("Nuclear demolition = %d m\n", self.nukerange) text=text..string.format("Nuclear fires = %d (active=%s)\n", self.nukefires, tostring(self.nukefire)) text=text..string.format("Number of illum. = %d\n", self.Nillu0) + text=text..string.format("Illuminaton Power = %.3f mcd\n", self.illuPower/1000000) + text=text..string.format("Illuminaton Minalt = %d m\n", self.illuMinalt) + text=text..string.format("Illuminaton Maxalt = %d m\n", self.illuMaxalt) text=text..string.format("Number of smoke = %d\n", self.Nsmoke0) + text=text..string.format("Smoke color = %d\n", self.smokeColor) if self.RearmingGroup or self.RearmingPlaceCoord then text=text..string.format("Rearming safe dist. = %d m\n", self.RearmingDistance) end @@ -1798,45 +1829,42 @@ function ARTY:_OnEventShot(EventData) --- Track the position of the weapon if it is supposed to model a tac nuke, illumination or smoke shell. -- @param #table _weapon - local function _TrackWeapon(_weapon) - - -- Coordinate of the target - local _targetcoord=self.currentTarget.coord -- Core.Point#COORDINATE - self.illuMinalt=400 - self.illuMaxalt=1000 - self.illuPower=1000 + local function _TrackWeapon(_data) -- When the pcall status returns false the weapon has hit. - local _status,_currpos = pcall( + local _weaponalive,_currpos = pcall( function() - return _weapon:getPoint() + return _data.weapon:getPoint() end) - self:T(ARTY.id..string.format("ARTY %s: Weapon still in air: %s", self.groupname, tostring(_status))) + -- Debug + self:T3(ARTY.id..string.format("ARTY %s: Weapon still in air: %s", self.groupname, tostring(_weaponalive))) + + -- Destroy weapon before impact. local _destroyweapon=false - if _status then + if _weaponalive then -- Update last position. _lastpos={x=_currpos.x, y=_currpos.y, z=_currpos.z} ---[[ + -- Coordinate and distance to target. local _coord=COORDINATE:NewFromVec3(_lastpos) - local _dist=_coord:Get2DDistance(self.currentTarget.coord) + local _dist=_coord:Get2DDistance(_data.target.coord) - env.info("FF dist = ".._dist) + -- Debug + self:T3(ARTY.id..string.format("ARTY %s weapon to target dist = %d m", self.groupname,_dist)) - if self.currentTarget.weapontype==ARTY.WeaponType.IlluminationShells then + if _data.target.weapontype==ARTY.WeaponType.IlluminationShells then -- Check if within distace. - if _dist0 local _tracksmoke = self.currentTarget.weapontype==ARTY.WeaponType.SmokeShells and self.Nsmoke>0 if _tracknuke or _trackillu or _tracksmoke then - self:T(ARTY.id..string.format("ARTY %s: Tracking of weapon starts in one second.", self.groupname)) - timer.scheduleFunction(_TrackWeapon, EventData.weapon, timer.getTime() + 1.0) + + self:T(ARTY.id..string.format("ARTY %s: Tracking of weapon starts in two seconds.", self.groupname)) + + local _peter={} + _peter.weapon=EventData.weapon + _peter.target=UTILS.DeepCopy(self.currentTarget) + + timer.scheduleFunction(_TrackWeapon, _peter, timer.getTime() + 2.0) end -- Get current ammo. @@ -1970,12 +2013,6 @@ function ARTY:_OnEventShot(EventData) if _ceasefire then self:CeaseFire(self.currentTarget) end - - -- Group is out of ammo (or partly and can rearm) ==> Winchester (==> Rearm). - if _outofammo or (_partlyoutofammo and self.RearmingGroup ~=nil) then - --self:Winchester() - --return - end else self:E(ARTY.id..string.format("WARNING: No current target for group %s?!", self.groupname)) @@ -4435,7 +4472,7 @@ function ARTY:_CheckWeaponTypePossible(target) elseif target.weapontype==ARTY.WeaponType.TacticalNukes then possible=self.Nukes0>0 elseif target.weapontype==ARTY.WeaponType.IlluminationShells then - possible=self.Nillus0>0 + possible=self.Nillu0>0 elseif target.weapontype==ARTY.WeaponType.SmokeShells then possible=self.Nsmoke0>0 elseif target.weapontype==ARTY.WeaponType.Rockets then From ec0b32321a95c757900d76fe35b119b2d9c6ab2f Mon Sep 17 00:00:00 2001 From: Ewald Zietsman Date: Fri, 15 Jun 2018 19:58:26 +0200 Subject: [PATCH 175/420] Added Rho/Theta offset handling to ZoneUnit --- Moose Development/Moose/Core/Zone.lua | 34 +++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index fd65dcf2d..b1893213f 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -976,12 +976,24 @@ ZONE_UNIT = { -- @param Dcs.DCSTypes#Distance Radius The radius of the zone. -- @param Dcs.DCSTypes#Distance dx The offset in X direction, +x is north. -- @param Dcs.DCSTypes#Distance dy The offset in Y direction, +y is east. +-- @param Dcs.DCSTypes#Distance rho The distance of the zone from the unit +-- @param Dcs.DCSTypes#Angle theta The azimuth of the zone relative to unit +-- @param Dcs.DCSTypes#Boolean relative_to_unit if true, theta is measured clockwise from unit's direction else clockwise from north. -- @return #ZONE_UNIT self -function ZONE_UNIT:New( ZoneName, ZoneUNIT, Radius, dx, dy ) +function ZONE_UNIT:New( ZoneName, ZoneUNIT, Radius, dx, dy, rho, theta, relative_to_unit) self.dy = dy or 0.0 self.dx = dx or 0.0 + self.rho = rho or 0.0 + self.theta = theta * math.pi / 180.0 or 0.0 + self.relative_to_unit = relative_to_unit or false + -- check if the inputs was reasonable, either (dx, dy) or (rho, theta) can be given, else raise an exception. + + if (dx or dy) and (rho or theta) then + error("Cannot use arguments (dx, dy) with (rho, theta)") + end + local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, ZoneUNIT:GetVec2(), Radius ) ) self:F( { ZoneName, ZoneUNIT:GetVec2(), Radius } ) @@ -1001,11 +1013,23 @@ function ZONE_UNIT:GetVec2() local ZoneVec2 = self.ZoneUNIT:GetVec2() if ZoneVec2 then - env.info(self.dx .. " " .. self.dy) - -- update the zone position with the offsets. - ZoneVec2.x = ZoneVec2.x + self.dx - ZoneVec2.y = ZoneVec2.y + self.dy + if (self.dx or self.dy) then + ZoneVec2.x = ZoneVec2.x + self.dx + ZoneVec2.y = ZoneVec2.y + self.dy + end + + if (self.rho or self.theta) then + -- check if theta is relative to unit or relative to north + if self.relative_to_unit then + heading = self.ZoneUNIT:GetHeading() * math.pi / 180.0 or 0.0 + else + heading = 0.0 + end + + ZoneVec2.x = ZoneVec2.x + self.rho * math.cos( self.theta + heading ) + ZoneVec2.y = ZoneVec2.y + self.rho * math.sin( self.theta + heading ) + end self.LastVec2 = ZoneVec2 return ZoneVec2 From 58e5ffa2834e204db5c1442d4ac0e6b58b17000b Mon Sep 17 00:00:00 2001 From: Ewald Zietsman Date: Sat, 16 Jun 2018 15:33:59 +0200 Subject: [PATCH 176/420] Changed offsets argument to use a table for clarity. --- Moose Development/Moose/Core/Zone.lua | 63 +++++++++++++++------------ 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index b1893213f..27741c2a3 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -974,26 +974,27 @@ ZONE_UNIT = { -- @param #string ZoneName Name of the zone. -- @param Wrapper.Unit#UNIT ZoneUNIT The unit as the center of the zone. -- @param Dcs.DCSTypes#Distance Radius The radius of the zone. --- @param Dcs.DCSTypes#Distance dx The offset in X direction, +x is north. --- @param Dcs.DCSTypes#Distance dy The offset in Y direction, +y is east. --- @param Dcs.DCSTypes#Distance rho The distance of the zone from the unit --- @param Dcs.DCSTypes#Angle theta The azimuth of the zone relative to unit --- @param Dcs.DCSTypes#Boolean relative_to_unit if true, theta is measured clockwise from unit's direction else clockwise from north. +-- @param #table Offset A table specifying the offset. The offset table may have the following elements: +-- dx The offset in X direction, +x is north. +-- dy The offset in Y direction, +y is east. +-- rho The distance of the zone from the unit +-- theta The azimuth of the zone relative to unit +-- relative_to_unit If true, theta is measured clockwise from unit's direction else clockwise from north. If using dx, dy setting this to true makes +x parallel to unit heading. +-- dx, dy OR rho, theta may be used, not both. -- @return #ZONE_UNIT self -function ZONE_UNIT:New( ZoneName, ZoneUNIT, Radius, dx, dy, rho, theta, relative_to_unit) - - self.dy = dy or 0.0 - self.dx = dx or 0.0 - self.rho = rho or 0.0 - self.theta = theta * math.pi / 180.0 or 0.0 - self.relative_to_unit = relative_to_unit or false +function ZONE_UNIT:New( ZoneName, ZoneUNIT, Radius, Offset) - -- check if the inputs was reasonable, either (dx, dy) or (rho, theta) can be given, else raise an exception. - - if (dx or dy) and (rho or theta) then - error("Cannot use arguments (dx, dy) with (rho, theta)") + -- check if the inputs was reasonable, either (dx, dy) or (rho, theta) can be given, else raise an exception. + if (Offset.dx or Offset.dy) and (Offset.rho or Offset.theta) then + error("Cannot use (dx, dy) with (rho, theta)") end - + + self.dy = Offset.dy or 0.0 + self.dx = Offset.dx or 0.0 + self.rho = Offset.rho or 0.0 + self.theta = (Offset.theta or 0.0) * math.pi / 180.0 + self.relative_to_unit = Offset.relative_to_unit or false + local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, ZoneUNIT:GetVec2(), Radius ) ) self:F( { ZoneName, ZoneUNIT:GetVec2(), Radius } ) @@ -1013,20 +1014,24 @@ function ZONE_UNIT:GetVec2() local ZoneVec2 = self.ZoneUNIT:GetVec2() if ZoneVec2 then - -- update the zone position with the offsets. - if (self.dx or self.dy) then - ZoneVec2.x = ZoneVec2.x + self.dx - ZoneVec2.y = ZoneVec2.y + self.dy - end - - if (self.rho or self.theta) then - -- check if theta is relative to unit or relative to north - if self.relative_to_unit then - heading = self.ZoneUNIT:GetHeading() * math.pi / 180.0 or 0.0 + + if self.relative_to_unit then + heading = ( self.ZoneUNIT:GetHeading() or 0.0 ) * math.pi / 180.0 else heading = 0.0 - end - + end + + -- update the zone position with the offsets. + if (self.dx or self.dy) then + + -- use heading to rotate offset relative to unit using rotation matrix in 2D. + -- see: https://en.wikipedia.org/wiki/Rotation_matrix + ZoneVec2.x = ZoneVec2.x + self.dx * math.cos( -heading ) + self.dy * math.sin( -heading ) + ZoneVec2.y = ZoneVec2.y - self.dx * math.sin( -heading ) + self.dy * math.cos( -heading ) + end + + -- if using the polar coordinates + if (self.rho or self.theta) then ZoneVec2.x = ZoneVec2.x + self.rho * math.cos( self.theta + heading ) ZoneVec2.y = ZoneVec2.y + self.rho * math.sin( self.theta + heading ) end From 79d8113df33a84d614116da9c3795ac5d3ef2cca Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sat, 16 Jun 2018 19:03:44 +0200 Subject: [PATCH 177/420] ARTY v1.0.4 Minor adjustments. Fixed bug that coordinates from mark points are always at an altitude of 5 meters. --- Moose Development/Moose/Core/Point.lua | 2 ++ .../Moose/Functional/Artillery.lua | 28 +++++++++++++------ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 35eb6566d..b98159460 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -268,6 +268,8 @@ do -- COORDINATE -- Adjust height if altitude==nil then _coord.y=altitude + else + _coord.y=self:GetLandHeight() end return _coord diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index c3e03b6f7..c2ab7b275 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -14,7 +14,7 @@ -- * Automatic rearming once the artillery is out of ammo (optional). -- * Automatic relocation after each firing engagement to prevent counter strikes (optional). -- * Automatic relocation movements to get the battery within firing range (optional). --- * Simulation of tactical nuclear shells. +-- * Simulation of tactical nuclear shells as well as illumination and smoke shells. -- * New targets can be added during the mission, e.g. when they are detected by recon units. -- * Targets and relocations can be assigned by placing markers on the F10 map. -- * Finite state machine implementation. Mission designer can interact when certain events occur. @@ -383,6 +383,16 @@ -- arty cancel, everyone, move -- arty cancel, rearming, battery "MRLS Charly" -- +-- ### Settings +-- +-- A few options can be set by marks. The corresponding keyword is **arty set**. This can be used to define the rearming place and group for a battery. +-- +-- To set the reamring place of a group at the marker position type +-- arty set, battery "Paladin Alpha", rearming place +-- +-- Setting the rearming group is independent of the position of the mark. Just create one anywhere on the map and type +-- arty set, battery "Mortar Bravo", rearming group "Ammo Truck M818" +-- Note that the name of the rearming group has to be given in quotation marks and spellt exactly as the group name defined in the mission editor. -- -- ## Fine Tuning -- @@ -517,7 +527,7 @@ ARTY={ Nillu=nil, illuPower=1000000, illuMinalt=500, - illuMaxalt=1500, + illuMaxalt=1000, Nsmoke=nil, smokeColor=SMOKECOLOR.Red, relocateafterfire=false, @@ -609,7 +619,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #string version -ARTY.version="1.0.2" +ARTY.version="1.0.4" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1469,13 +1479,13 @@ function ARTY:SetIlluminationShells(n, power) end --- Set minimum and maximum detotation altitude for illumination shells. A value between min/max is selected randomly. --- The illumination bomb will burn for 300 seconds (5 minutes). Assuming a descent rate of 5 m/s the "optimal" altitude would be 1500 m. +-- The illumination bomb will burn for 300 seconds (5 minutes). Assuming a descent rate of ~3 m/s the "optimal" altitude would be 900 m. -- @param #ARTY self -- @param #number minalt (Optional) Minium altitude in meters. Default 500 m. --- @param #number maxalt (Optional) Maximum altitude in meters. Default 1500 m. +-- @param #number maxalt (Optional) Maximum altitude in meters. Default 1000 m. function ARTY:SetIlluminationMinMaxAlt(minalt, maxalt) self.illuMinalt=minalt or 500 - self.illuMaxalt=maxalt or 1500 + self.illuMaxalt=maxalt or 1000 if self.illuMinalt>self.illuMaxalt then self.illuMinalt=self.illuMaxalt @@ -2153,6 +2163,9 @@ function ARTY:_OnEventMarkChange(Event) -- Get coordinate from vec3. local _coord=COORDINATE:NewFromVec3(vec3) + + -- Adjust y component to actual land height. When a coordinate is create it uses y=5 m! + _coord.y=_coord:GetLandHeight() -- Get battery coalition and name. local batterycoalition=self.Controllable:GetCoalition() @@ -2263,7 +2276,7 @@ function ARTY:_OnEventMarkChange(Event) -- Set stuff and return. if _assign.set and _validkey then - if _assign.setrearmingplace then + if _assign.setrearmingplace and self.ismobile then self:SetRearmingPlace(_coord) _coord:RemoveMark(Event.idx) _coord:MarkToCoalition(string.format("Rearming place for battery %s", self.groupname), self.Controllable:GetCoalition(), false, string.format("New rearming place for battery %s defined.", self.groupname)) @@ -3873,7 +3886,6 @@ function ARTY:_Markertext(text) local v=self:_split(keyphrase, '"') local groupname=v[2] - env.info("FF v2 groupname = "..tostring(v[2])) local group=GROUP:FindByName(groupname) if group and group:IsAlive() then From 16b279c5db4e446467e5363bcc4044d6eceb5853 Mon Sep 17 00:00:00 2001 From: FlightControl_Master Date: Sun, 17 Jun 2018 07:37:34 +0200 Subject: [PATCH 178/420] Documentation first version --- .../Moose/Tasking/Task_CARGO.lua | 269 +++++++++++++++--- .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 60 ++-- 2 files changed, 267 insertions(+), 62 deletions(-) diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index cb6edeb49..cdcaca1f7 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -1,6 +1,4 @@ ---- **Tasking** -- The TASK_CARGO models tasks for players to transport @{Cargo}. --- --- ![Banner Image](..\Presentations\TASK_CARGO\Dia1.JPG) +--- **Tasking** -- Base class to model tasks for players to transport @{Cargo}. -- -- === -- @@ -31,10 +29,9 @@ do -- TASK_CARGO --- @type TASK_CARGO -- @extends Tasking.Task#TASK - --- - -- # TASK_CARGO class, extends @{Tasking.Task#TASK} + --- Base class to model tasks for players to transport @{Cargo}. -- - -- ## A flexible tasking system + -- ## 1. A flexible tasking system -- -- The TASK_CARGO classes provide you with a flexible tasking sytem, -- that allows you to transport cargo of various types between various locations @@ -44,7 +41,7 @@ do -- TASK_CARGO -- The SET_CARGO contains a collection of CARGO objects that must be handled by the players in the mission. -- -- - -- ## Task execution experience from the player perspective + -- ## 2. Task execution experience from the player perspective -- -- A human player can join the battle field in a client airborne slot or a ground vehicle within the CA module (ALT-J). -- The player needs to accept the task from the task overview list within the mission, using the radio menus. @@ -65,56 +62,221 @@ do -- TASK_CARGO -- In the menu, you'll find for each CARGO, that is part of the scope of the task, various actions that can be completed. -- Depending on the location of your Carrier unit, the menu options will vary. -- + -- ### 2.1. Joining a Cargo Transport Task -- - -- ## Cargo Pickup and Boarding + -- Select __Join Tasks__, and you'll see a **Transport** task category. Select __Transport__ and you'll see the different tasks + -- listed. -- - -- For cargo boarding, a cargo can only execute the boarding actions if it is within the foreseen **Reporting Range**. - -- Therefore, it is important that you steer your Carrier within the Reporting Range, - -- so that boarding actions can be executed on the cargo. - -- To Pickup and Board cargo, the following menu items will be shown in your carrier radio menu: + -- ![Task Types](../Tasking/###) -- - -- ### Board Cargo + -- Select one of the tasks ... -- - -- If your Carrier is within the Reporting Range of the cargo, it will allow to pickup the cargo by selecting this menu option. - -- Depending on the Cargo type, the cargo will either move to your Carrier or you will receive instructions how to handle the cargo - -- pickup. If the cargo moves to your carrier, it will indicate the boarding status. - -- Note that multiple units need to board your Carrier, so it is required to await the full boarding process. + -- ![Task_Types](../Tasking/###) + -- + -- Select Join Task ... + -- + -- After the menu "Join Task" selection, you are assigned to the Task. + -- + -- - ![Task_Types](../Tasking/Task_Briefing.JPG). + -- A briefing message is shown. + -- - The notification message is shown to all players, indicating that the cargo task is now assigned. + -- - When no task as part of the mission was assigned, the mission is set to **ONGOING**. + -- + -- From this moment on, you can Pickup cargo from a pickup location and Deploy cargo in deployment zones, using the **Task Action Menu**. + -- + -- ### 2.2. Task Action Menu. + -- + -- When a player has joined a task, for that player only, it's carrier radio menu will show an additional menu option. + -- It has the name of the task you currently joined and @ player name. + -- + -- ![Task_Types](../Tasking/Task_Briefing.JPG). + -- For example, this shows the task __Transport Liquids.002@ Transport#013__. + -- + -- We call this menu option the **Task Action Menu**. + -- Under this menu option, there will be other menu options available which are specific to the task you just selected. + -- Depending on the task type, these menu options will vary. + -- + -- ### 2.2. Cancel a joined Cargo Transport Task. + -- + -- One more thing, it is possible to cancel a task that you joined. + -- ![Task_Types](../Tasking/###) + -- + -- When this option is selected, the player is removed to be assigned as part of the task. + -- If the player was the last player that was assigned to the task, the task is set to "Hold". + -- + -- ### 2.3. Pickup cargo by Boarding, Loading and Sling Loading. + -- + -- There are three different ways how cargo can be picked up: + -- + -- - **Boarding**: Moveable cargo (like infantry or vehicles), can be boarded, that means, the cargo will move towards your carrier to board. + -- However, it can only execute the boarding actions if it is within the foreseen **Reporting Range**. + -- Therefore, it is important that you steer your Carrier within the Reporting Range around the cargo, + -- so that boarding actions can be executed on the cargo. The reporting range is set by the mission designer. + -- Fortunately, the cargo is reporting to you when it is within reporting range. + -- + -- - **Loading**: Stationary cargo (like crates), which are heavy, can only be loaded or sling loaded, meaning, + -- your carrier must be close enough to the cargo to be able to load the cargo within the carrier bays. + -- Moose provides you with an additional menu system to load stationary cargo into your carrier bays using the radio menu. + -- These menu options will become available, when the carrier is within loading range. + -- The Moose cargo will report to the carrier when the range is close enough. The load range is set by the mission designer. + -- + -- - **Sling Loading**: Stationary cargo (like crates), which are heavy, can only be loaded or sling loaded, meaning, + -- your carrier must be close enough to the cargo to be able to load the cargo within the carrier bays. + -- Sling loading cargo is done using the default DCS menu system. However, Moose cargo will report to the carrier that + -- it is within sling loading range. + -- + -- In order to be able to pickup cargo, you'll need to know where the cargo is located, right? + -- Fortunately, if your Carrier is not within the reporting range of the cargo, the HQ can help to route you to the locations of cargo. + -- Use the task action menu to receive HQ help for this. + -- + -- ![Task_Types](../Tasking/Task_Cargo_Actions.JPG) + -- + -- Depending on the location within the battlefield, the task action menu will contain **Route options** that can be selected + -- to start the HQ sending you routing messages. + -- + -- When selected, the HQ will send you routing messages. + -- + -- ![Task_Types](../Tasking/Task_Cargo_Routing_LL.JPG) + -- An example of routing in LL mode. + -- + -- ![Task_Types](../Tasking/Task_Cargo_Routing_BR.JPG) + -- An example of routing in BR mode. + -- + -- Possible coordinate formats are: Bearing Range (BR), Lattitude Longitude (LL) or Military Grid System (MGRS). + -- Note that for LL, there are two sub formats. + -- + -- The routing messages are formulated in the coordinate format that is currently active as configured in your settings profile. + -- ![Task_Types](../Tasking/Task_Cargo_Settings.JPG) + -- Use the Settings Menu to select the coordinate format that you would like to use for location determination. + -- + -- + -- #### 2.3.1. Pickup Cargo. + -- + -- In order to pickup cargo, use the **task action menu** to **route to a specific cargo**. + -- When a cargo route is selected, the HQ will send you routing messages indicating the location of the cargo. + -- + -- Upon arrival at the cargo, and when the cargo is within **reporting range**, the cargo will contact you and **further instructions will be given**. + -- + -- - When your Carrier is airborne, you will receive instructions to land your Carrier. + -- The action will not be completed until you've landed your Carrier. + -- + -- - For ground carriers, you can just drive to the optimal cargo board or load position. + -- + -- It takes a bit of skill to land a helicopter near a cargo to be loaded, but that is part of the game, isn't it? + -- Expecially when you are landing in a "hot" zone, so when cargo is under immediate threat of fire. + -- + -- #### 2.3.2. Board Cargo. + -- + -- If your Carrier is within the **Reporting Range of the cargo**, and the cargo is **moveable**, the **cargo can be boarded**! + -- + -- Select the task action menu and now a **Board or Load option** will be listed with the cargo name next to it! + -- Select the option from the action menu, and the cargo will start moving towards your carrier. + -- + -- The moveable cargo will run in formation to your carrier, and will board one by one, depending on the near range set by the mission designer. + -- The near range as added because carriers can be large or small, depending on the object size of the carrier. + -- Note that multiple units may need to board your Carrier, so it is required to await the full boarding process. -- Once the cargo is fully boarded within your Carrier, you will be notified of this. -- -- Note that for airborne Carriers, it is required to land first before the Boarding process can be initiated. -- If during boarding the Carrier gets airborne, the boarding process will be cancelled. -- - -- ## Pickup Cargo + -- #### 2.3.3. Load Cargo. -- - -- If your Carrier is not within the Reporting Range of the cargo, the HQ will guide you to its location. - -- Routing information is shown in flight that directs you to the cargo within Reporting Range. - -- Upon arrival, the Cargo will contact you and further instructions will be given. - -- When your Carrier is airborne, you will receive instructions to land your Carrier. - -- The action will not be completed until you've landed your Carrier. + -- If your Carrier is within the **Loading Range of the cargo**, and the cargo is **stationary**, the **cargo can be loaded**, but not boarded! + -- + -- Select the task action menu and now a **Load option** will be listed with the cargo name next to it! + -- Select the option from the action menu, and the cargo will loaded into your carrier. + -- Once the cargo is loaded within your Carrier, you will be notified of this. + -- + -- Note that for airborne Carriers, it is required to land first right near the cargo, before the loading process can be initiated. + -- As stated, this requires some pilot skills :-) + -- + -- #### 2.3.4. Sling Load Cargo (helicopters only). + -- + -- If your Carrier is within the **Loading Range of the cargo**, and the cargo is **stationary**, the **cargo can also be sling loaded**! + -- Note that this is only possible for helicopters. + -- + -- To sling load cargo, there is no task action menu required. Just follow the normal sling loading procedure and the cargo will report. + -- Use the normal DCS sling loading menu system to hook the cargo you the cable attached on your helicopter. + -- + -- Again note that you may land firstly right next to the cargo, before the loading process can be initiated. + -- As stated, this requires some pilot skills :-) -- -- - -- ## Cargo Deploy and UnBoarding + -- ### 2.4. Deploy cargo by Unboarding, Unloading and Sling Deploying. + -- + -- There are two different ways how cargo can be deployed: + -- + -- - **Unboarding**: Moveable cargo (like infantry or vehicles), can be unboarded, that means, + -- the cargo will step out of the carrier and will run to a group location. + -- Moose provides you with an additional menu system to unload stationary cargo from the carrier bays, + -- using the radio menu. These menu options will become available, when the carrier is within the deploy zone. + -- + -- - **Unloading**: Stationary cargo (like crates), which are heavy, can only be unloaded or sling loaded. + -- Moose provides you with an additional menu system to unload stationary cargo from the carrier bays, + -- using the radio menu. These menu options will become available, when the carrier is within the deploy zone. + -- + -- - **Sling Deploying**: Stationary cargo (like crates), which are heavy, can also be sling deployed. + -- Once the cargo is within the deploy zone, the cargo can be deployed from the sling onto the ground. + -- + -- In order to be able to deploy cargo, you'll need to know where the deploy zone is located, right? + -- Fortunately, the HQ can help to route you to the locations of deploy zone. + -- Use the task action menu to receive HQ help for this. + -- + -- ![Task_Types](../Tasking/Task_Cargo_Actions.JPG) + -- + -- Depending on the location within the battlefield, the task action menu will contain **Route options** that can be selected + -- to start the HQ sending you routing messages. Also, if the carrier cargo bays contain cargo, + -- then beside **Route options** there will also be **Deploy options** listed. + -- These **Deploy options** are meant to route you to the deploy zone locations. + -- + -- Possible routing coordinate formats are: Bearing Range (BR), Lattitude Longitude (LL) or Military Grid System (MGRS). + -- Note that for LL, there are two sub formats. + -- + -- The routing messages are formulated in the coordinate format that is currently active as configured in your settings profile. + -- ![Task_Types](../Tasking/Task_Cargo_Settings.JPG) + -- Use the Settings Menu to select the coordinate format that you would like to use for location determination. + -- + -- ### 2.4. Deploy Cargo. -- -- Various Deployment Zones can be foreseen in the scope of the Cargo transportation. Each deployment zone can be of varying @{Zone} type. -- The Cargo Handling Radio Menu provides with menu options to execute an action to steer your Carrier to a specific Zone. -- - -- ### UnBoard Cargo + -- In order to deploy cargo, use the task action menu to select a cargo to route to. + -- When selected, the HQ will send you routing messages indicating the location of the deploy zone. + -- + -- Upon arrival at the deploy zone, the HQ will contact you and further instructions will be given. -- - -- If your Carrier is already within a Deployment Zone, - -- then the Cargo Handling Radio Menu allows to **UnBoard** a specific cargo that is - -- loaded within your Carrier group into the Deployment Zone. - -- Note that the Unboarding process takes a while, as the cargo units (infantry or vehicles) must unload from your Carrier. - -- Ensure that you stay at the position or stay on the ground while Unboarding. - -- If any unforeseen manoeuvre is done by the Carrier, then the Unboarding will be cancelled. + -- #### 2.4.1. Unboard Cargo. -- - -- ### Deploy Cargo + -- If your Carrier is within the **deploy zone**, and the cargo is **moveable**, the **cargo can be unboarded**! -- - -- If your Carrier is not within a Deployment Zone, you'll need to fly towards one. - -- Fortunately, the Cargo Handling Radio Menu provides you with menu options to select a specific Deployment Zone to fly towards. - -- Once a Deployment Zone has been selected, your Carrier will receive routing information from HQ towards the Deployment Zone center. - -- Upon arrival, the HQ will provide you with further instructions. - -- When your Carrier is airborne, you will receive instructions to land your Carrier. - -- The action will not be completed until you've landed your Carrier! + -- Select the task action menu and now an **Unboard option** will be listed with the cargo name next to it! + -- Select the option from the action menu, and the cargo will step out of your carrier and will move towards a grouping point. + -- + -- The moveable cargo will unboard one by one, so note that multiple units may need to unboard your Carrier, + -- so it is required to await the full completion of the unboarding process. + -- Once the cargo is fully unboarded from your Carrier, you will be notified of this. + -- + -- Note that for airborne Carriers, it is required to land first before the unboarding process can be initiated. + -- If during unboarding the Carrier gets airborne, the unboarding process will be cancelled. + -- + -- #### 2.4.2. Unload Cargo. + -- + -- If your Carrier is within the **deploy zone**, and the cargo is **stationary**, the **cargo can be unloaded**, but not unboarded! + -- + -- Select the task action menu and now an **Unload option** will be listed with the cargo name next to it! + -- Select the option from the action menu, and the cargo will unloaded from your carrier. + -- Once the cargo is unloaded fom your Carrier, you will be notified of this. + -- + -- Note that for airborne Carriers, it is required to land first at the deploy zone, before the unloading process can be initiated. + -- + -- #### 2.4.3. Sling Deploy Cargo (helicopters only). + -- + -- If your Carrier is within the **deploy zone**, and the cargo is **stationary**, the **cargo can also be sling deploying**! + -- Note that this is only possible for helicopters. + -- + -- To sling deploy cargo, there is no task action menu required. Just follow the normal sling deploying procedure. -- -- ## Handle TASK_CARGO Events ... -- @@ -190,7 +352,36 @@ do -- TASK_CARGO -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. - + -- @usage + -- + -- -- Add a Transport task to transport cargo of different types to a Transport Deployment Zone. + -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, TransportGroups ) + -- + -- local CargoSetWorkmaterials = SET_CARGO:New():FilterTypes( "Workmaterials" ):FilterStart() + -- local EngineerCargoGroup = CARGO_GROUP:New( GROUP:FindByName( "Engineers" ), "Workmaterials", "Engineers", 250 ) + -- local ConcreteCargo = CARGO_SLINGLOAD:New( STATIC:FindByName( "Concrete" ), "Workmaterials", "Concrete", 150, 50 ) + -- local CrateCargo = CARGO_CRATE:New( STATIC:FindByName( "Crate" ), "Workmaterials", "Crate", 150, 50 ) + -- local EnginesCargo = CARGO_CRATE:New( STATIC:FindByName( "Engines" ), "Workmaterials", "Engines", 150, 50 ) + -- local MetalCargo = CARGO_CRATE:New( STATIC:FindByName( "Metal" ), "Workmaterials", "Metal", 150, 50 ) + -- + -- -- Here we add the task. We name the task "Build a Workplace". + -- -- We provide the CargoSetWorkmaterials, and a briefing as the 2nd and 3rd parameter. + -- -- The :AddTransportTask() returns a Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT object, which we keep as a reference for further actions. + -- -- The WorkplaceTask holds the created and returned Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT object. + -- local WorkplaceTask = TaskDispatcher:AddTransportTask( "Build a Workplace", CargoSetWorkmaterials, "Transport the workers, engineers and the equipment near the Workplace." ) + -- + -- -- Here we set a TransportDeployZone. We use the WorkplaceTask as the reference, and provide a ZONE object. + -- TaskDispatcher:SetTransportDeployZone( WorkplaceTask, ZONE:New( "Workplace" ) ) + -- + -- Helos = { SPAWN:New( "Helicopters 1" ), SPAWN:New( "Helicopters 2" ), SPAWN:New( "Helicopters 3" ), SPAWN:New( "Helicopters 4" ), SPAWN:New( "Helicopters 5" ) } + -- EnemyHelos = { SPAWN:New( "Enemy Helicopters 1" ), SPAWN:New( "Enemy Helicopters 2" ), SPAWN:New( "Enemy Helicopters 3" ) } + -- + -- -- This is our worker method! So when a cargo is deployed within a deployment zone, this method will be called. + -- -- By example we are spawning here a random friendly helicopter and a random enemy helicopter. + -- function WorkplaceTask:OnAfterCargoDeployed( From, Event, To, TaskUnit, Cargo, DeployZone ) + -- Helos[ math.random(1,#Helos) ]:Spawn() + -- EnemyHelos[ math.random(1,#EnemyHelos) ]:Spawn() + -- end self:AddTransition( "*", "CargoPickedUp", "*" ) diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index 7644674da..b232c7138 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -426,13 +426,33 @@ do -- TASK_CARGO_DISPATCHER --- Add a Transport task to transport cargo from fixed locations to a deployment zone. -- @param #TASK_CARGO_DISPATCHER self - -- @param #string TaskName (optional) The name of the transport task. + -- @param #string TaskPrefix (optional) The prefix of the transport task. + -- This prefix will be appended with a . + a number of 3 digits. + -- If no TaskPrefix is given, then "Transport" will be used as the prefix. -- @param Core.SetCargo#SET_CARGO SetCargo The SetCargo to be transported. -- @param #string Briefing The briefing of the task transport to be shown to the player. - -- @return #TASK_CARGO_DISPATCHER + -- @return Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT -- @usage -- -- -- Add a Transport task to transport cargo of different types to a Transport Deployment Zone. + -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, TransportGroups ) + -- + -- local CargoSetWorkmaterials = SET_CARGO:New():FilterTypes( "Workmaterials" ):FilterStart() + -- local EngineerCargoGroup = CARGO_GROUP:New( GROUP:FindByName( "Engineers" ), "Workmaterials", "Engineers", 250 ) + -- local ConcreteCargo = CARGO_SLINGLOAD:New( STATIC:FindByName( "Concrete" ), "Workmaterials", "Concrete", 150, 50 ) + -- local CrateCargo = CARGO_CRATE:New( STATIC:FindByName( "Crate" ), "Workmaterials", "Crate", 150, 50 ) + -- local EnginesCargo = CARGO_CRATE:New( STATIC:FindByName( "Engines" ), "Workmaterials", "Engines", 150, 50 ) + -- local MetalCargo = CARGO_CRATE:New( STATIC:FindByName( "Metal" ), "Workmaterials", "Metal", 150, 50 ) + -- + -- -- Here we add the task. We name the task "Build a Workplace". + -- -- We provide the CargoSetWorkmaterials, and a briefing as the 2nd and 3rd parameter. + -- -- The :AddTransportTask() returns a Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT object, which we keep as a reference for further actions. + -- -- The WorkplaceTask holds the created and returned Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT object. + -- local WorkplaceTask = TaskDispatcher:AddTransportTask( "Build a Workplace", CargoSetWorkmaterials, "Transport the workers, engineers and the equipment near the Workplace." ) + -- + -- -- Here we set a TransportDeployZone. We use the WorkplaceTask as the reference, and provide a ZONE object. + -- TaskDispatcher:SetTransportDeployZone( WorkplaceTask, ZONE:New( "Workplace" ) ) + -- function TASK_CARGO_DISPATCHER:AddTransportTask( TaskName, SetCargo, Briefing ) self.TransportCount = self.TransportCount + 1 @@ -444,32 +464,26 @@ do -- TASK_CARGO_DISPATCHER self.Transport[TaskName].Briefing = Briefing self.Transport[TaskName].Task = nil - return TaskName - end - - - --- Add a Transport task to transport cargo from fixed locations to a deployment zone. - -- @param #TASK_CARGO_DISPATCHER self - -- @param #string TaskName (optional) The name of the transport task. - -- @return Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT - function TASK_CARGO_DISPATCHER:GetTransportTask( TaskName ) - self:ManageTasks() + return self.Transport[TaskName] and self.Transport[TaskName].Task end - + --- Define one deploy zone for the Transport tasks. -- @param #TASK_CARGO_DISPATCHER self - -- @param #string TaskName (optional) The name of the Transport task. + -- @param Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT Task The name of the Transport task. -- @param TransportDeployZone A Transport deploy zone. -- @return #TASK_CARGO_DISPATCHER - function TASK_CARGO_DISPATCHER:SetTransportDeployZone( TaskName, TransportDeployZone ) + -- @usage + -- + -- + function TASK_CARGO_DISPATCHER:SetTransportDeployZone( Task, TransportDeployZone ) - if self.Transport[TaskName] then - self.Transport[TaskName].DeployZones = { TransportDeployZone } + if self.Transport[Task.TaskName] then + self.Transport[Task.TaskName].DeployZones = { TransportDeployZone } else - error( "TaskName does not exist" ) + error( "Task does not exist" ) end return self @@ -478,16 +492,16 @@ do -- TASK_CARGO_DISPATCHER --- Define the deploy zones for the Transport tasks. -- @param #TASK_CARGO_DISPATCHER self - -- @param #string TaskName (optional) The name of the Transport task. + -- @param Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT Task The name of the Transport task. -- @param TransportDeployZones A list of the Transport deploy zones. -- @return #TASK_CARGO_DISPATCHER -- - function TASK_CARGO_DISPATCHER:SetTransportDeployZones( TaskName, TransportDeployZones ) + function TASK_CARGO_DISPATCHER:SetTransportDeployZones( Task, TransportDeployZones ) - if self.Transport[TaskName] then - self.Transport[TaskName].DeployZones = TransportDeployZones + if self.Transport[Task.TaskName] then + self.Transport[Task.TaskName].DeployZones = TransportDeployZones else - error( "TaskName does not exist" ) + error( "Task does not exist" ) end return self From 6bdf0122e49cb6cc8b7d2fae063f0f789f92467a Mon Sep 17 00:00:00 2001 From: Dave Lugg Date: Fri, 15 Jun 2018 14:46:15 -0400 Subject: [PATCH 179/420] Error flag fixed. --- Moose Development/Moose/Wrapper/Client.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Client.lua b/Moose Development/Moose/Wrapper/Client.lua index 7df3c0586..4ca13c802 100644 --- a/Moose Development/Moose/Wrapper/Client.lua +++ b/Moose Development/Moose/Wrapper/Client.lua @@ -74,7 +74,7 @@ CLIENT = { --- Finds a CLIENT from the _DATABASE using the relevant DCS Unit. -- @param #CLIENT self -- @param #string ClientName Name of the DCS **Unit** as defined within the Mission Editor. --- @param #string ClientBriefing Text that describes the briefing of the mission when a Player logs into the Client. +-- @param #boolean Error Flag indicating whether or not to throw an error if the CLIENT cannot be found. True by default. Returns `nil` if `Error` is false and no Client was found. -- @return #CLIENT -- @usage -- -- Create new Clients. @@ -94,7 +94,7 @@ function CLIENT:Find( DCSUnit, Error ) return ClientFound end - if not Error then + if Error or Error == nil then error( "CLIENT not found for: " .. ClientName ) end end @@ -105,7 +105,7 @@ end -- @param #CLIENT self -- @param #string ClientName Name of the DCS **Unit** as defined within the Mission Editor. -- @param #string ClientBriefing Text that describes the briefing of the mission when a Player logs into the Client. --- @param #boolean Error A flag that indicates whether an error should be raised if the CLIENT cannot be found. By default an error will be raised. +-- @param #boolean Error Flag indicating whether or not to throw an error if the CLIENT cannot be found. True by default. Returns `nil` if `Error` is false and no Client was found. -- @return #CLIENT -- @usage -- -- Create new Clients. @@ -127,7 +127,7 @@ function CLIENT:FindByName( ClientName, ClientBriefing, Error ) return ClientFound end - if not Error then + if Error or Error == nil then error( "CLIENT not found for: " .. ClientName ) end end From 20c1f6bab4f8946a2a67f0449b5aeae1b6eed141 Mon Sep 17 00:00:00 2001 From: Jonathan Toppins Date: Mon, 18 Jun 2018 10:00:34 -0400 Subject: [PATCH 180/420] GROUP: provide a new GetFuelMin() method Taking the average fuel available across an entire flight is not really realistic and does not present an accurate representation of if a group needs to be sent to refuel or rtb. An example, a flight of 2 planes; plane 1 has 45% and plane 2 has 25% (45 + 25)/2 = 35, and 25% is the RTB threshold, plane 2 needs to go home now. However with the current averaging function plane 2 will have as little as 15% fuel (assume equal consumption) before the flight gets ordered home. Signed-off-by: Jonathan Toppins --- Moose Development/Moose/Wrapper/Group.lua | 44 +++++++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 776cf923c..0ea42e035 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -768,11 +768,41 @@ function GROUP:GetHeading() end ---- Returns relative amount of fuel (from 0.0 to 1.0) the group has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0. +--- Return the fuel state and unit reference for the unit with the least +-- amount of fuel in the group. +-- @param #GROUP self +-- @return #number The fuel state of the unit with the least amount of fuel +-- @return #Unit reference to #Unit object for further processing +function GROUP:GetFuelMin() + self:F(self.ControllableName) + + if not self:GetDCSObject() then + BASE:E( { "Cannot GetFuel", Group = self, Alive = self:IsAlive() } ) + return 0 + end + + local min = 65535 -- some sufficiently large number to init with + local unit = nil + local tmp = nil + + for UnitID, UnitData in pairs( self:GetUnits() ) do + tmp = UnitData:GetFuel() + if tmp < min then + min = tmp + unit = UnitData + end + end + + return min, unit +end + +--- Returns relative amount of fuel (from 0.0 to 1.0) the group has in its +-- internal tanks. If there are additional fuel tanks the value may be +-- greater than 1.0. -- @param #GROUP self -- @return #number The relative amount of fuel (from 0.0 to 1.0). --- @return #nil The GROUP is not existing or alive. -function GROUP:GetFuel() +-- @return #nil The GROUP is not existing or alive. +function GROUP:GetFuelAvg() self:F( self.ControllableName ) local DCSControllable = self:GetDCSObject() @@ -795,6 +825,14 @@ function GROUP:GetFuel() return 0 end +--- Returns relative amount of fuel (from 0.0 to 1.0) the group has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0. +-- @param #GROUP self +-- @return #number The relative amount of fuel (from 0.0 to 1.0). +-- @return #nil The GROUP is not existing or alive. +function GROUP:GetFuel() + return self:GetFuelAvg() +end + do -- Is Zone methods From 64c4d57a7bd3bf7f6f977da9abda7d6e007ea5d4 Mon Sep 17 00:00:00 2001 From: Jonathan Toppins Date: Mon, 18 Jun 2018 07:54:40 -0400 Subject: [PATCH 181/420] cleanup: remove unreferenced variables PatrolManageFuel seems to have been forgotten after a code refactor in commit ce0be4dcf75d ("Fixing TASK_DISPATCHER and optimizing reports") $ git rev-parse --show-toplevel projects/moose $ pwd projects/moose $ git grep "PatrolManageFuel" Moose Development/Moose/AI/AI_A2A.lua: self.PatrolManageFuel = true Moose Development/Moose/AI/AI_Patrol.lua: self.PatrolManageFuel = true Signed-off-by: Jonathan Toppins --- Moose Development/Moose/AI/AI_A2A.lua | 1 - Moose Development/Moose/AI/AI_Patrol.lua | 1 - 2 files changed, 2 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A.lua b/Moose Development/Moose/AI/AI_A2A.lua index 4fbe0f37d..76dfb573f 100644 --- a/Moose Development/Moose/AI/AI_A2A.lua +++ b/Moose Development/Moose/AI/AI_A2A.lua @@ -367,7 +367,6 @@ end -- @return #AI_A2A self function AI_A2A:SetFuelThreshold( PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime ) - self.PatrolManageFuel = true self.PatrolFuelThresholdPercentage = PatrolFuelThresholdPercentage self.PatrolOutOfFuelOrbitTime = PatrolOutOfFuelOrbitTime diff --git a/Moose Development/Moose/AI/AI_Patrol.lua b/Moose Development/Moose/AI/AI_Patrol.lua index 1f70d0015..48bbea449 100644 --- a/Moose Development/Moose/AI/AI_Patrol.lua +++ b/Moose Development/Moose/AI/AI_Patrol.lua @@ -590,7 +590,6 @@ end -- @return #AI_PATROL_ZONE self function AI_PATROL_ZONE:ManageFuel( PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime ) - self.PatrolManageFuel = true self.PatrolFuelThresholdPercentage = PatrolFuelThresholdPercentage self.PatrolOutOfFuelOrbitTime = PatrolOutOfFuelOrbitTime From 19197bf23463c61d8d149607b212c0d319569056 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 18 Jun 2018 23:13:21 +0200 Subject: [PATCH 182/420] Min Fuel Fixes AI_A2A and AI_Patrol: Changed average fuel for controllable to new min fuel function. Hopefully provides better RTB behavior. CONTROLLABLE: Added GetFuelMin and GetFuelAve functions to ensure polymorphic behavior. UNIT: Added GetFuelMin and GetFuelAve functions for completeness and potential polymorphism. --- Moose Development/Moose/AI/AI_A2A.lua | 2 +- Moose Development/Moose/AI/AI_Patrol.lua | 2 +- .../Moose/Wrapper/Controllable.lua | 22 +++++++++++++++++-- Moose Development/Moose/Wrapper/Unit.lua | 20 +++++++++++++++++ 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A.lua b/Moose Development/Moose/AI/AI_A2A.lua index 4fbe0f37d..154e99013 100644 --- a/Moose Development/Moose/AI/AI_A2A.lua +++ b/Moose Development/Moose/AI/AI_A2A.lua @@ -452,7 +452,7 @@ function AI_A2A:onafterStatus() if not self:Is( "Fuel" ) and not self:Is( "Home" ) then - local Fuel = self.Controllable:GetFuel() + local Fuel = self.Controllable:GetFuelMin() self:F({Fuel=Fuel}) if Fuel < self.PatrolFuelThresholdPercentage then if self.TankerName then diff --git a/Moose Development/Moose/AI/AI_Patrol.lua b/Moose Development/Moose/AI/AI_Patrol.lua index 1f70d0015..ecedcf697 100644 --- a/Moose Development/Moose/AI/AI_Patrol.lua +++ b/Moose Development/Moose/AI/AI_Patrol.lua @@ -824,7 +824,7 @@ function AI_PATROL_ZONE:onafterStatus() local RTB = false - local Fuel = self.Controllable:GetUnit(1):GetFuel() + local Fuel = self.Controllable:GetFuelMin() if Fuel < self.PatrolFuelThresholdPercentage then self:E( self.Controllable:GetName() .. " is out of fuel:" .. Fuel .. ", RTB!" ) local OldAIControllable = self.Controllable diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 0b56cc0d7..3199393b4 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -259,6 +259,26 @@ function CONTROLLABLE:GetLife0() return nil end +--- Returns relative minimum amount of fuel (from 0.0 to 1.0) a unit or group has in its internal tanks. +-- This method returns nil to ensure polymorphic behaviour! This method needs to be overridden by GROUP or UNIT. +-- @param #CONTROLLABLE self +-- @return #nil The CONTROLLABLE is not existing or alive. +function CONTROLLABLE:GetFuelMin() + self:F( self.ControllableName ) + + return nil +end + +--- Returns relative average amount of fuel (from 0.0 to 1.0) a unit or group has in its internal tanks. +-- This method returns nil to ensure polymorphic behaviour! This method needs to be overridden by GROUP or UNIT. +-- @param #CONTROLLABLE self +-- @return #nil The CONTROLLABLE is not existing or alive. +function CONTROLLABLE:GetFuelAve() + self:F( self.ControllableName ) + + return nil +end + --- Returns relative amount of fuel (from 0.0 to 1.0) the unit has in its internal tanks. -- This method returns nil to ensure polymorphic behaviour! This method needs to be overridden by GROUP or UNIT. -- @param #CONTROLLABLE self @@ -270,8 +290,6 @@ function CONTROLLABLE:GetFuel() end - - -- Tasks --- Clear all tasks from the controllable. diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 753b842f4..91d138ab8 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -571,6 +571,26 @@ function UNIT:GetFuel() return nil end +--- Returns relative amount of fuel (from 0.0 to 1.0) the UNIT has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0. +-- @param #UNIT self +-- @return #number The relative amount of fuel (from 0.0 to 1.0). +-- @return #nil The DCS Unit is not existing or alive. +function UNIT:GetFuelMin() + self:F( self.UnitName ) + + self:GetFuel() +end + +--- Returns relative amount of fuel (from 0.0 to 1.0) the UNIT has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0. +-- @param #UNIT self +-- @return #number The relative amount of fuel (from 0.0 to 1.0). +-- @return #nil The DCS Unit is not existing or alive. +function UNIT:GetFuelAve() + self:F( self.UnitName ) + + self:GetFuel() +end + --- Returns a list of one @{Wrapper.Unit}. -- @param #UNIT self -- @return #list A list of one @{Wrapper.Unit}. From 82d40759b5d0aaf72243d93641b87067a3d9d620 Mon Sep 17 00:00:00 2001 From: funkyfranky <> Date: Mon, 18 Jun 2018 23:55:31 +0200 Subject: [PATCH 183/420] Get Fuel Min AI_A2A_Dispatcher Changed tac display from ave fuel to min fuel --- .../Moose/AI/AI_A2A_Dispatcher.lua | 18 ++++++++--------- Moose Development/Moose/Wrapper/Unit.lua | 20 ------------------- 2 files changed, 9 insertions(+), 29 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index 7a017f854..d8018b2fe 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -356,7 +356,7 @@ do -- AI_A2A_DISPATCHER -- -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia9.JPG) -- - -- If it’s a cold war then the **borders of red and blue territory** need to be defined using a @{zone} object derived from @{Core.Zone#ZONE_BASE}. + -- If it's a cold war then the **borders of red and blue territory** need to be defined using a @{zone} object derived from @{Core.Zone#ZONE_BASE}. -- If a hot war is chosen then **no borders** actually need to be defined using the helicopter units other than -- it makes it easier sometimes for the mission maker to envisage where the red and blue territories roughly are. -- In a hot war the borders are effectively defined by the ground based radar coverage of a coalition. @@ -557,15 +557,15 @@ do -- AI_A2A_DISPATCHER -- * CAP zones can be of any type, and are derived from the @{Core.Zone#ZONE_BASE} class. Zones can be @{Core.Zone#ZONE}, @{Core.Zone#ZONE_POLYGON}, @{Core.Zone#ZONE_UNIT}, @{Core.Zone#ZONE_GROUP}, etc. -- This allows to setup **static, moving and/or complex zones** wherein aircraft will perform the CAP. -- - -- * Typically 20000-50000 metres width is used and they are spaced so that aircraft in the zone waiting for tasks don’t have to far to travel to protect their coalitions important targets. + -- * Typically 20000-50000 metres width is used and they are spaced so that aircraft in the zone waiting for tasks don't have to far to travel to protect their coalitions important targets. -- These targets are chosen as part of the mission design and might be an important airfield or town etc. -- Zone size is also determined somewhat by territory size, plane types -- (eg WW2 aircraft might mean smaller zones or more zones because they are slower and take longer to intercept enemy aircraft). -- - -- * In a **cold war** it is important to make sure a CAP zone doesn’t intrude into enemy territory as otherwise CAP flights will likely cross borders + -- * In a **cold war** it is important to make sure a CAP zone doesn't intrude into enemy territory as otherwise CAP flights will likely cross borders -- and spark a full scale conflict which will escalate rapidly. -- - -- * CAP flights do not need to be in the CAP zone before they are “on station†and ready for tasking. + -- * CAP flights do not need to be in the CAP zone before they are "on station" and ready for tasking. -- -- * Typically if a CAP flight is tasked and therefore leaves their zone empty while they go off and intercept their target another CAP flight will spawn to take their place. -- @@ -805,11 +805,11 @@ do -- AI_A2A_DISPATCHER -- For example because the mission calls for a EWR radar on the blue side the Ukraine might be chosen as a blue country -- so that the 55G6 EWR radar unit is available to blue. -- Some countries assign different tasking to aircraft, for example Germany assigns the CAP task to F-4E Phantoms but the USA does not. - -- Therefore if F4s are wanted as a coalition’s CAP or GCI aircraft Germany will need to be assigned to that coalition. + -- Therefore if F4s are wanted as a coalition's CAP or GCI aircraft Germany will need to be assigned to that coalition. -- -- ### 11.2. Country, type, load out, skill and skins for CAP and GCI aircraft? -- - -- * Note these can be from any countries within the coalition but must be an aircraft with one of the main tasks being “CAPâ€. + -- * Note these can be from any countries within the coalition but must be an aircraft with one of the main tasks being "CAP". -- * Obviously skins which are selected must be available to all players that join the mission otherwise they will see a default skin. -- * Load outs should be appropriate to a CAP mission eg perhaps drop tanks for CAP flights and extra missiles for GCI flights. -- * These decisions will eventually lead to template aircraft units being placed as late activation units that the script will use as templates for spawning CAP and GCI flights. Up to 4 different aircraft configurations can be chosen for each coalition. The spawned aircraft will inherit the characteristics of the template aircraft. @@ -1152,7 +1152,7 @@ do -- AI_A2A_DISPATCHER --- Define a border area to simulate a **cold war** scenario. -- A **cold war** is one where CAP aircraft patrol their territory but will not attack enemy aircraft or launch GCI aircraft unless enemy aircraft enter their territory. In other words the EWR may detect an enemy aircraft but will only send aircraft to attack it if it crosses the border. -- A **hot war** is one where CAP aircraft will intercept any detected enemy aircraft and GCI aircraft will launch against detected enemy aircraft without regard for territory. In other words if the ground radar can detect the enemy aircraft then it will send CAP and GCI aircraft to attack it. - -- If it’s a cold war then the **borders of red and blue territory** need to be defined using a @{zone} object derived from @{Core.Zone#ZONE_BASE}. This method needs to be used for this. + -- If it's a cold war then the **borders of red and blue territory** need to be defined using a @{zone} object derived from @{Core.Zone#ZONE_BASE}. This method needs to be used for this. -- If a hot war is chosen then **no borders** actually need to be defined using the helicopter units other than it makes it easier sometimes for the mission maker to envisage where the red and blue territories roughly are. In a hot war the borders are effectively defined by the ground based radar coverage of a coalition. Set the noborders parameter to 1 -- @param #AI_A2A_DISPATCHER self -- @param Core.Zone#ZONE_BASE BorderZone An object derived from ZONE_BASE, or a list of objects derived from ZONE_BASE. @@ -3031,7 +3031,7 @@ do -- AI_A2A_DISPATCHER for Defender, DefenderTask in pairs( self:GetDefenderTasks() ) do local Defender = Defender -- Wrapper.Group#GROUP if DefenderTask.Target and DefenderTask.Target.Index == DetectedItem.Index then - local Fuel = Defender:GetFuel() * 100 + local Fuel = Defender:GetFuelMin() * 100 local Damage = Defender:GetLife() / Defender:GetLife0() * 100 Report:Add( string.format( " - %s ( %s - %s ): ( #%d ) F: %3d, D:%3d - %s", Defender:GetName(), @@ -3054,7 +3054,7 @@ do -- AI_A2A_DISPATCHER local Defender = Defender -- Wrapper.Group#GROUP if not DefenderTask.Target then local DefenderHasTask = Defender:HasTask() - local Fuel = Defender:GetFuel() * 100 + local Fuel = Defender:GetFuelMin() * 100 local Damage = Defender:GetLife() / Defender:GetLife0() * 100 Report:Add( string.format( " - %s ( %s - %s ): ( #%d ) F: %3d, D:%3d - %s", Defender:GetName(), diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 91d138ab8..753b842f4 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -571,26 +571,6 @@ function UNIT:GetFuel() return nil end ---- Returns relative amount of fuel (from 0.0 to 1.0) the UNIT has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0. --- @param #UNIT self --- @return #number The relative amount of fuel (from 0.0 to 1.0). --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetFuelMin() - self:F( self.UnitName ) - - self:GetFuel() -end - ---- Returns relative amount of fuel (from 0.0 to 1.0) the UNIT has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0. --- @param #UNIT self --- @return #number The relative amount of fuel (from 0.0 to 1.0). --- @return #nil The DCS Unit is not existing or alive. -function UNIT:GetFuelAve() - self:F( self.UnitName ) - - self:GetFuel() -end - --- Returns a list of one @{Wrapper.Unit}. -- @param #UNIT self -- @return #list A list of one @{Wrapper.Unit}. From 7d90a94927d2003a6a3c60b342da3cdd8ad1eb66 Mon Sep 17 00:00:00 2001 From: funkyfranky <> Date: Tue, 19 Jun 2018 23:10:08 +0200 Subject: [PATCH 184/420] ARTY Updated docs. Added SetSpeed() function. --- .../Moose/Functional/Artillery.lua | 42 +++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index c2ab7b275..126ef78b9 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -237,8 +237,8 @@ -- * @{#ARTY.WeaponType}.IlluminationShells: Use illumination shells. This works only with units that have shells and is described below. -- * @{#ARTY.WeaponType}.SmokeShells: Use smoke shells. This works only with units that have shells and is described below. -- --- ## Assigning Moves --- The ARTY group can be commanded to move. This is done by the @{#ARTY.AssignMoveCoord}(*coord*,*time*,*speed*,*onroad*,*cancel*,*name*) function. +-- ## Assigning Relocation Movements +-- The ARTY group can be commanded to move. This is done by the @{#ARTY.AssignMoveCoord}(*coord*, *time*, *speed*, *onroad*, *cancel*, *name*) function. -- With this multiple timed moves of the group can be scheduled easily. By default, these moves will only be executed if the group is state **CombatReady**. -- -- ### Parameters @@ -274,7 +274,11 @@ -- -- After the rearming is complete, both groups will move back to their original positions. -- --- ## Tactical Nukes +-- ## Simulated Weapons +-- +-- In addtion to the standard weapons a group has available some special weapon types that are not possible to use in the native DCS environment are simulated. +-- +-- ### Tactical Nukes -- -- ARTY groups that can fire shells can also be used to fire tactical nukes. This is achieved by setting the weapon type to **ARTY.WeaponType.TacticalNukes** in the -- @{#ARTY.AssignTargetCoord}() function. @@ -284,6 +288,27 @@ -- Note that the group must always have convenctional shells left in order to fire a nuclear shell. -- -- The default explostion strength is 0.075 kilo tons TNT. The can be changed with the @{#ARTY.SetTacNukeWarhead}(*strength*), where *strength* is given in kilo tons TNT. +-- +-- ### Illumination Shells +-- +-- ARTY groups that possess shells can fire shells with illumination bombs. First, the group needs to be equipped with this weapon. This is done by the +-- function @{ARTY.SetIlluminationShells}(*n*, *power*), where *n* is the number of shells the group has available and *power* the illumination power in mega candela (mcd). +-- +-- In order to execute an engagement with illumination shells one has to use the weapon type *ARTY.WeaponType.IlluminationShells* in the +-- @{#ARTY.AssignTargetCoord}() function. +-- +-- In the simulation, the explosive shell that is fired is destroyed once it gets close to the target point but before it can actually impact. +-- At this position an illumination bomb is triggered at a random altitude between 500 and 1000 meters. This interval can be set by the function +-- @{ARTY.SetIlluminationMinMaxAlt}(*minalt*, *maxalt*). +-- +-- ### Smoke Shells +-- +-- In a similar way to illumination shells, ARTY groups can also employ smoke shells. The numer of smoke shells the group has available is set by the function +-- @{#ARTY.SetSmokeShells}(*n*, *color*), where *n* is the number of shells and *color* defines the smoke color. Default is SMOKECOLOR.Red. +-- +-- The weapon type to be used in the @{#ARTY.AssignTargetCoord}() function is *ARTY.WeaponType.SmokeShells*. +-- +-- The explosive shell the group fired is destroyed shortly before its impact on the ground and smoke of the speficied color is triggered at that position. -- -- -- ## Assignments via Markers on F10 Map @@ -403,6 +428,7 @@ -- * @{#ARTY.SetAutoRelocateAfterEngagement}(*rmax*, *rmin*) will cause the ARTY group to change its position after each firing assignment. -- Optional parameters *rmax*, *rmin* define the max/min distance for relocation of the group. Default distance is randomly between 300 and 800 m. -- * @{#ARTY.AddToCluster}(*clusters*) Can be used to add the ARTY group to one or more clusters. All groups in a cluster can be addressed simultaniously with one marker command. +-- * @{#ARTY.SetSpeed}(*speed*) sets the speed in km/h the group moves at if not explicitly stated otherwise. -- * @{#ARTY.RemoveAllTargets}() removes all targets from the target queue. -- * @{#ARTY.RemoveTarget}(*name*) deletes the target with *name* from the target queue. -- * @{#ARTY.SetMaxFiringRange}(*range*) defines the maximum firing range. Targets further away than this distance are not engaged. @@ -1355,6 +1381,13 @@ function ARTY:SetDebugOFF() self.Debug=false end +--- Set default speed the group is moving at if not specified otherwise. +-- @param #ARTY self +-- @param #number speed Speed in km/h. +function ARTY:SetSpeed(speed) + self.Speed=speed +end + --- Delete a target from target list. If the target is currently engaged, it is cancelled. -- @param #ARTY self -- @param #string name Name of the target. @@ -1601,6 +1634,9 @@ function ARTY:onafterStart(Controllable, From, Event, To) self.autorelocate=false self.RearmingGroupSpeed=20 end + + -- Check that default speed is below max speed. + self.Speed=math.min(self.Speed, self.SpeedMax) -- Set Rearming group speed if not specified by user if self.RearmingGroup then From ea89c6255b38fd15e40e0b5648c4e6808022c203 Mon Sep 17 00:00:00 2001 From: Van De Velde Date: Fri, 22 Jun 2018 05:59:28 +0200 Subject: [PATCH 185/420] Documentation improvements --- .../Moose/Tasking/CommandCenter.lua | 82 +++++++++- Moose Development/Moose/Tasking/Mission.lua | 98 ++++++++++- Moose Development/Moose/Tasking/Task.lua | 153 ++++++++++++++---- Moose Development/Moose/Tasking/Task_A2A.lua | 2 +- Moose Development/Moose/Tasking/Task_A2G.lua | 4 +- .../Moose/Tasking/Task_CARGO.lua | 24 +-- 6 files changed, 312 insertions(+), 51 deletions(-) diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index e8f9204e4..4b3e8a17c 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -30,19 +30,93 @@ --- Governs multiple missions, the tasking and the reporting. -- --- The commandcenter communicates important messages between the various groups of human players executing tasks in missions. +-- Command centers govern missions, communicates the task assignments between human players of the coalition, and manages the menu flow. +-- It can assign a random task to a player when requested. +-- The commandcenter provides the facilitites to communicate between human players online, executing a task. -- --- ## COMMANDCENTER constructor +-- ## 1. Create a command center object. -- -- * @{#COMMANDCENTER.New}(): Creates a new COMMANDCENTER object. -- --- ## Mission Management +-- ## 2. Command center mission management. +-- +-- Command centers manage missions. These can be added, removed and provides means to retrieve missions. +-- These methods are heavily used by the task dispatcher classes. -- -- * @{#COMMANDCENTER.AddMission}(): Adds a mission to the commandcenter control. -- * @{#COMMANDCENTER.RemoveMission}(): Removes a mission to the commandcenter control. -- * @{#COMMANDCENTER.GetMissions}(): Retrieves the missions table controlled by the commandcenter. -- --- ## Reference Zones +-- ## 3. Communication management between players. +-- +-- Command center provide means of communication between players. +-- Because a command center is a central object governing multiple missions, +-- there are several levels at which communication needs to be done. +-- Within MOOSE, communication is facilitated using the message system within the DCS simulator. +-- +-- Messages can be sent between players at various levels: +-- +-- - On a global level, to all players. +-- - On a coalition level, only to the players belonging to the same coalition. +-- - On a group level, to the players belonging to the same group. +-- +-- Messages can be sent to **all players** by the command center using the method @{Tasking.CommandCenter#COMMANDCENTER.MessageToAll}(). +-- +-- To send messages to **the coalition of the command center**, there are two methods available: +-- +-- - Use the method @{Tasking.CommandCenter#COMMANDCENTER.MessageToCoalition}() to send a specific message to the coalition, with a given message display duration. +-- - You can send a specific type of message using the method @{Tasking.CommandCenter#COMMANDCENTER.MessageTypeToCoalition}(). +-- This will send a message of a specific type to the coalition, and as a result its display duration will be flexible according the message display time selection by the human player. +-- +-- To send messages **to the group** of human players, there are also two methods available: +-- +-- - Use the method @{Tasking.CommandCenter#COMMANDCENTER.MessageToGroup}() to send a specific message to a group, with a given message display duration. +-- - You can send a specific type of message using the method @{Tasking.CommandCenter#COMMANDCENTER.MessageTypeToGroup}(). +-- This will send a message of a specific type to the group, and as a result its display duration will be flexible according the message display time selection by the human player . +-- +-- Messages are considered to be sometimes disturbing for human players, therefore, the settings menu provides the means to activate or deactivate messages. +-- For more information on the message types and display timings that can be selected and configured using the menu, refer to the @{Core.Settings} menu description. +-- +-- ## 4. Command center detailed methods. +-- +-- Various methods are added to manage command centers. +-- +-- ### 4.1. Naming and description. +-- +-- There are 3 methods that can be used to retrieve the description of a command center: +-- +-- - Use the method @{Tasking.CommandCenter#COMMANDCENTER.GetName}() to retrieve the name of the command center. +-- This is the name given as part of the @{Tasking.CommandCenter#COMMANDCENTER.New}() constructor. +-- The returned name using this method, is not to be used for message communication. +-- +-- A textual description can be retrieved that provides the command center name to be used within message communication: +-- +-- - @{Tasking.CommandCenter#COMMANDCENTER.GetShortText}() returns the command center name as `CC [CommandCenterName]`. +-- - @{Tasking.CommandCenter#COMMANDCENTER.GetText}() returns the command center name as `Command Center [CommandCenterName]`. +-- +-- ### 4.2. The coalition of the command center. +-- +-- The method @{Tasking.CommandCenter#COMMANDCENTER.GetCoalition}() returns the coalition of the command center. +-- The return value is an enumeration of the type @{DCS#coalition.side}, which contains the RED, BLUE and NEUTRAL coalition. +-- +-- ### 4.3. The command center is a real object. +-- +-- The command center must be represented by a live object within the DCS simulator. As a result, the command center +-- can be a @{Wrapper.Unit}, a @{Wrapper.Group}, an @{Wrapper.Airbase} or a @{Wrapper.Static} object. +-- +-- Using the method @{Tasking.CommandCenter#COMMANDCENTER.GetPositionable}() you retrieve the polymorphic positionable object representing +-- the command center, but just be aware that you should be able to use the representable object derivation methods. +-- +-- ### 5. Command center reports. +-- +-- Because a command center giverns multiple missions, there are several reports available that are generated by command centers. +-- These reports are generated using the following methods: +-- +-- - @{Tasking.CommandCenter#COMMANDCENTER.ReportSummary}(): Creates a summary report of all missions governed by the command center. +-- - @{Tasking.CommandCenter#COMMANDCENTER.ReportDetails}(): Creates a detailed report of all missions governed by the command center. +-- - @{Tasking.CommandCenter#COMMANDCENTER.ReportMissionPlayers}(): Creates a report listing the players active at the missions governed by the command center. +-- +-- ## 6. Reference Zones. -- -- Command Centers may be aware of certain Reference Zones within the battleground. These Reference Zones can refer to -- known areas, recognizable buildings or sites, or any other point of interest. diff --git a/Moose Development/Moose/Tasking/Mission.lua b/Moose Development/Moose/Tasking/Mission.lua index c2f9f2de1..6bbca053e 100644 --- a/Moose Development/Moose/Tasking/Mission.lua +++ b/Moose Development/Moose/Tasking/Mission.lua @@ -19,12 +19,106 @@ -- @module Tasking.Mission -- @image Task_Mission.JPG ---- The MISSION class --- @type MISSION +--- @type MISSION -- @field #MISSION.Clients _Clients -- @field Core.Menu#MENU_COALITION MissionMenu -- @field #string MissionBriefing -- @extends Core.Fsm#FSM + +--- Models goals to be achieved and can contain multiple tasks to be executed to achieve the goals. +-- +-- A mission contains multiple tasks and can be of different task types. +-- These tasks need to be assigned to human players to be executed. +-- +-- A mission can have multiple states, which will evolve as the mission progresses during the DCS simulation. +-- +-- - **IDLE**: The mission is defined, but not started yet. No task has yet been joined by a human player as part of the mission. +-- - **ENGAGED**: The mission is ongoing, players have joined tasks to be executed. +-- - **COMPLETED**: The goals of the mission has been successfully reached, and the mission is flagged as completed. +-- - **FAILED**: For a certain reason, the goals of the mission has not been reached, and the mission is flagged as failed. +-- - **HOLD**: The mission was enaged, but for some reason it has been put on hold. +-- +-- Note that a mission goals need to be checked by a goal check trigger: @{#MISSION.OnBeforeMissionGoals}(), which may return false if the goal has not been reached. +-- This goal is checked automatically by the mission object every x seconds. +-- +-- - @{#MISSION.Start}() or @{#MISSION.__Start}() will start the mission, and will bring it from **IDLE** state to **ENGAGED** state. +-- - @{#MISSION.Stop}() or @{#MISSION.__Stop}() will stop the mission, and will bring it from **ENGAGED** state to **IDLE** state. +-- - @{#MISSION.Complete}() or @{#MISSION.__Complete}() will complete the mission, and will bring the mission state to **COMPLETED**. +-- Note that the mission must be in state **ENGAGED** to be able to complete the mission. +-- - @{#MISSION.Fail}() or @{#MISSION.__Fail}() will fail the mission, and will bring the mission state to **FAILED**. +-- Note that the mission must be in state **ENGAGED** to be able to fail the mission. +-- - @{#MISSION.Hold}() or @{#MISSION.__Hold}() will hold the mission, and will bring the mission state to **HOLD**. +-- Note that the mission must be in state **ENGAGED** to be able to hold the mission. +-- Re-engage the mission using the engage trigger. +-- +-- The following sections provide an overview of the most important methods that can be used as part of a mission object. +-- Note that the @{Tasking.CommandCenter} system is using most of these methods to manage the missions in its system. +-- +-- ## 1. Create a mission object. +-- +-- - @{#MISSION.New}(): Creates a new MISSION object. +-- +-- ## 2. Mission task management. +-- +-- Missions maintain tasks, which can be added or removed, or enquired. +-- +-- - @{#MISSION.AddTask}(): Adds a task to the mission. +-- - @{#MISSION.RemoveTask}(): Removes a task from the mission. +-- +-- ## 3. Mission detailed methods. +-- +-- Various methods are added to manage missions. +-- +-- ### 3.1. Naming and description. +-- +-- There are several methods that can be used to retrieve the properties of a mission: +-- +-- - Use the method @{#MISSION.GetName}() to retrieve the name of the mission. +-- This is the name given as part of the @{#MISSION.New}() constructor. +-- +-- A textual description can be retrieved that provides the mission name to be used within message communication: +-- +-- - @{#MISSION.GetShortText}() returns the mission name as `Mission "MissionName"`. +-- - @{#MISSION.GetText}() returns the mission name as `Mission "MissionName (MissionPriority)"`. A longer version including the priority text of the mission. +-- +-- ### 3.2. Get task information. +-- +-- - @{#MISSION.GetTasks}(): Retrieves a list of the tasks controlled by the mission. +-- - @{#MISSION.GetTask}(): Retrieves a specific task controlled by the mission. +-- - @{#MISSION.GetTasksRemaining}(): Retrieve a list of the tasks that aren't finished or failed, and are governed by the mission. +-- - @{#MISSION.GetGroupTasks}(): Retrieve a list of the tasks that can be asigned to a @{Wrapper.Group}. +-- - @{#MISSION.GetTaskTypes}(): Retrieve a list of the different task types governed by the mission. +-- +-- ### 3.3. Get the command center. +-- +-- - @{#MISSION.GetCommandCenter}(): Retrieves the @{Tasking.CommandCenter} governing the mission. +-- +-- ### 3.4. Get the groups active in the mission as a @{Core.Set}. +-- +-- - @{#MISSION.GetGroups}(): Retrieves a @{Core.Set#SET_GROUP} of all the groups active in the mission (as part of the tasks). +-- +-- ### 3.5. Get the names of the players. +-- +-- - @{#MISSION.GetPlayerNames}(): Retrieves the list of the players that were active within th mission.. +-- +-- ## 4. Menu management. +-- +-- A mission object is able to manage its own menu structure. Use the @{#MISSION.GetMenu}() and @{#MISSION.SetMenu}() to manage the underlying submenu +-- structure managing the tasks of the mission. +-- +-- ## 5. Reporting management. +-- +-- Several reports can be generated for a mission, and will return a text string that can be used to display using the @{Core.Message} system. +-- +-- - @{#MISSION.ReportBriefing}(): Generates the briefing for the mission. +-- - @{#MISSION.ReportOverview}(): Generates an overview of the tasks and status of the mission. +-- - @{#MISSION.ReportDetails}(): Generates a detailed report of the tasks of the mission. +-- - @{#MISSION.ReportSummary}(): Generates a summary report of the tasks of the mission. +-- - @{#MISSION.ReportPlayersPerTask}(): Generates a report showing the active players per task. +-- - @{#MISSION.ReportPlayersProgress}(): Generates a report showing the task progress per player. +-- +-- +-- @field #MISSION MISSION = { ClassName = "MISSION", Name = "", diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index f76cbf7fe..3835621a3 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -1,4 +1,13 @@ ---- **Tasking** -- This module contains the TASK class, the main engine to run human taskings. +--- **Tasking** -- A task object governs the main engine to administer human taskings. +-- +-- **Features:** +-- +-- * Manage the overall task execution, following-up the progression made by the pilots and actors. +-- * Provide a mechanism to set a task status, depending on the progress made within the task. +-- * Manage a task briefing. +-- * Manage the players executing the task. +-- * Manage the task menu system. +-- * Manage the task goal and scoring. -- -- === -- @@ -21,43 +30,127 @@ -- @field Tasking.TaskInfo#TASKINFO TaskInfo -- @extends Core.Fsm#FSM_TASK ---- --- # TASK class, extends @{Core.Base#BASE} +--- Governs the main engine to administer human taskings. -- --- ## The TASK class implements the methods for task orchestration within MOOSE. +-- A task is governed by a @{Tasking.Mission} object. Tasks are of different types. +-- The @{#TASK} object is used or derived by more detailed tasking classes that will implement the task execution mechanisms +-- and goals. The following TASK_ classes are derived from @{#TASK}. -- --- The class provides a couple of methods to: -- --- * @{#TASK.AssignToGroup}():Assign a task to a group (of players). --- * @{#TASK.AddProcess}():Add a @{Process} to a task. --- * @{#TASK.RemoveProcesses}():Remove a running @{Process} from a running task. --- * @{#TASK.SetStateMachine}():Set a @{Core.Fsm} to a task. --- * @{#TASK.RemoveStateMachine}():Remove @{Core.Fsm} from a task. --- * @{#TASK.HasStateMachine}():Enquire if the task has a @{Core.Fsm} --- * @{#TASK.AssignToUnit}(): Assign a task to a unit. (Needs to be implemented in the derived classes from @{#TASK}. --- * @{#TASK.UnAssignFromUnit}(): Unassign the task from a unit. --- * @{#TASK.SetTimeOut}(): Set timer in seconds before task gets cancelled if not assigned. +-- TASK +-- TASK_A2A +-- TASK_A2A_ENGAGE +-- TASK_A2A_INTERCEPT +-- TASK_A2A_SWEEP +-- TASK_A2G +-- TASK_A2G_SEAD +-- TASK_A2G_CAS +-- TASK_A2G_BAI +-- TASK_CARGO +-- TASK_CARGO_TRANSPORT +-- TASK_CARGO_CSAR +-- +-- +-- +-- #### A2A Tasks +-- +-- - @{Tasking.Task_A2A#TASK_A2A_ENGAGE} - Models an A2A engage task of a target group of airborne intruders mid-air. +-- - @{Tasking.Task_A2A#TASK_A2A_INTERCEPT} - Models an A2A ground intercept task of a target group of airborne intruders mid-air. +-- - @{Tasking.Task_A2A#TASK_A2A_SWEEP} - Models an A2A sweep task to clean an area of previously detected intruders mid-air. +-- +-- #### A2G Tasks +-- +-- - @{Tasking.Task_A2G#TASK_A2G_SEAD} - Models an A2G Suppression or Extermination of Air Defenses task to clean an area of air to ground defense threats. +-- - @{Tasking.Task_A2G#TASK_A2G_CAS} - Models an A2G Close Air Support task to provide air support to nearby friendlies near the front-line. +-- - @{Tasking.Task_A2G#TASK_A2G_BAI} - Models an A2G Battlefield Air Interdiction task to provide air support to nearby friendlies near the front-line. +-- +-- #### Cargo Tasks +-- +-- - @{Tasking.Task_Cargo#TASK_CARGO_TRANSPORT} - Models the transportation of cargo to deployment zones. +-- - @{Tasking.Task_Cargo#TASK_CARGO_CSAR} - Models the rescue of downed friendly pilots from behind enemy lines. +-- +-- The above task objects take care of the **progress** and **completion** of the task **goal(s)**. +-- Tasks are executed by **human pilots** and actors within a DCS simulation. +-- Pilots can use a **menu system** to engage or abort a task, and provides means to +-- understand the **task briefing** and goals, and the relevant **task locations** on the map and +-- obtain **various reports** related to the task. +-- +-- As the task progresses, the **task status** will change over time, from Planned state to Completed state. +-- **Multiple pilots** can execute the same task, as such, the tasking system provides a **co-operative model** for joint task execution. +-- Depending on the task progress, a **scoring** can be allocated to award pilots of the achievements made. +-- The scoring is fully flexible, and different levels of awarding can be provided depending on the task type and complexity. +-- +-- ## 1. Task Statuses +-- +-- ### 1.1. Task status overview. +-- +-- A task has a state, reflecting the progress and completion of the task: +-- +-- - **Planned**: Expresses that the task is created, but not yet in execution and is not assigned yet to a pilot. +-- - **Assigned**: Expresses that the task is assigned to a group of pilots, and that the task is in execution mode. +-- - **Success**: Expresses the successful execution and finalization of the task. +-- - **Failed**: Expresses the failure of a task. +-- - **Abort**: Expresses that the task is aborted by by the player using the abort menu. +-- - **Cancelled**: Expresses that the task is cancelled by HQ or through a logical situation where a cancellation of the task is required. +-- +-- A normal flow of task status would evolve from the **Planned** state, to the **Assigned** state ending either in a **Success** or a **Failed** state. +-- The state completion is by default set to **Success**, if the goals of the task have been reached, but can be overruled by a goal method. +-- +-- Depending on the tactical situation, a task can be **Rejected** or **Cancelled** by the mission governer. +-- It is actually the mission designer who has the flexibility to decide at which conditions a task would be set to **Success**, **Failed** or **Cancelled**. +-- It all depends on the task goals, and the phase/evolution of the task conditions that would accomplish the goals. +-- For example, if the task goal is to merely destroy a target, and the target is mid-mission destroyed by another event than the pilot destroying the target, +-- the task goal could be set to **Failed**, or .. **Cancelled** ... +-- However, it could very well be also acceptable that the task would be flagged as **Success**. +-- +-- The tasking mechanism governs beside the progress also a scoring mechanism, and in case of goal completion without any active pilot involved +-- in the execution of the task, could result in a **Success** task completion status, but no score would be awared, as there were no players involved. +-- +-- ### 1.2. Task status events. +-- +-- The task statuses can be set by using the following methods: +-- +-- - @{#TASK.Success}() - Set the task to **Success** state. +-- - @{#TASK.Fail}() - Set the task to **Failed** state. +-- - @{#TASK.Hold}() - Set the task to **Hold** state. +-- - @{#TASK.Abort}() - Set the task to **Aborted** state, aborting the task. The task may be replanned. +-- - @{#TASK.Cancel}() - Set the task to **Cancelled** state, cancelling the task. +-- +-- The mentioned derived TASK_ classes are implementing the task status transitions out of the box. +-- So no extra logic needs to be written. -- --- ## 1.2) Set and enquire task status (beyond the task state machine processing). +-- ## 2. Goal conditions for a task. -- --- A task needs to implement as a minimum the following task states: +-- Every 30 seconds, a @{#Task.Goal} trigger method is fired. +-- You as a mission designer, can capture the **Goal** event trigger to check your own task goal conditions and take action! -- --- * **Success**: Expresses the successful execution and finalization of the task. --- * **Failed**: Expresses the failure of a task. --- * **Planned**: Expresses that the task is created, but not yet in execution and is not assigned yet. --- * **Assigned**: Expresses that the task is assigned to a Group of players, and that the task is in execution mode. +-- ### 2.1. Goal event handler `OnAfterGoal()`. -- --- A task may also implement the following task states: --- --- * **Rejected**: Expresses that the task is rejected by a player, who was requested to accept the task. --- * **Cancelled**: Expresses that the task is cancelled by HQ or through a logical situation where a cancellation of the task is required. --- --- A task can implement more statusses than the ones outlined above. Please consult the documentation of the specific tasks to understand the different status modelled. --- --- The status of tasks can be set by the methods **State** followed by the task status. An example is `StateAssigned()`. --- The status of tasks can be enquired by the methods **IsState** followed by the task status name. An example is `if IsStateAssigned() then`. +-- And this is a really great feature! Imagine a task which has **several conditions to check** before the task can move into **Success** state. +-- You can do this with the OnAfterGoal method. -- --- ## 1.3) Add scoring when reaching a certain task status: +-- The following code provides an example of such a goal condition check implementation. +-- +-- function Task:OnAfterGoal() +-- if condition == true then +-- self:Success() -- This will flag the task to Succcess when the condition is true. +-- else +-- if condition2 == true and condition3 == true then +-- self:Fail() -- This will flag the task to Failed, when condition2 and condition3 would be true. +-- end +-- end +-- end +-- +-- So the @{#TASK.OnAfterGoal}() event handler would be called every 30 seconds automatically, and within this method, you can now check the conditions and take respective action. +-- +-- ### 2.2. Goal event trigger `Goal()`. +-- +-- If you would need to check a goal at your own defined event timing, then just call the @{#TASK.Goal}() method within your logic. +-- The @{#TASK.OnAfterGoal}() event handler would then directly be called and would execute the logic. +-- Note that you can also delay the goal check by using the delayed event trigger syntax `:__Goal( Dalay )`. +-- +-- +-- ## 3) Add scoring when reaching a certain task status: -- -- Upon reaching a certain task status in a task, additional scoring can be given. If the Mission has a scoring system attached, the scores will be added to the mission scoring. -- Use the method @{#TASK.AddScore}() to add scores when a status is reached. diff --git a/Moose Development/Moose/Tasking/Task_A2A.lua b/Moose Development/Moose/Tasking/Task_A2A.lua index 5fb22050b..bd12b7e43 100644 --- a/Moose Development/Moose/Tasking/Task_A2A.lua +++ b/Moose Development/Moose/Tasking/Task_A2A.lua @@ -8,7 +8,7 @@ -- -- === -- --- @module Tasking.Tasking.Task_A2A +-- @module Tasking.Task_A2A -- @image MOOSE.JPG do -- TASK_A2A diff --git a/Moose Development/Moose/Tasking/Task_A2G.lua b/Moose Development/Moose/Tasking/Task_A2G.lua index 51294075d..637f1fdc4 100644 --- a/Moose Development/Moose/Tasking/Task_A2G.lua +++ b/Moose Development/Moose/Tasking/Task_A2G.lua @@ -457,7 +457,7 @@ do -- TASK_A2G_BAI -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK - -- Defines an Battlefield Air Interdiction task for a human player to be executed. + --- Defines a Battlefield Air Interdiction task for a human player to be executed. -- These tasks are more strategic in nature and are most of the time further away from friendly forces. -- BAI tasks can also be used to express the abscence of friendly forces near the vicinity. -- @@ -551,7 +551,7 @@ do -- TASK_A2G_CAS -- @field Core.Set#SET_UNIT TargetSetUnit -- @extends Tasking.Task#TASK - -- Defines an Close Air Support task for a human player to be executed. + --- Defines an Close Air Support task for a human player to be executed. -- Friendly forces will be in the vicinity within 6km from the enemy. -- -- The TASK_A2G_CAS is used by the @{Tasking.Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create CAS tasks diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index cdcaca1f7..b10e9e1ab 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -44,10 +44,10 @@ do -- TASK_CARGO -- ## 2. Task execution experience from the player perspective -- -- A human player can join the battle field in a client airborne slot or a ground vehicle within the CA module (ALT-J). - -- The player needs to accept the task from the task overview list within the mission, using the radio menus. + -- The player needs to accept the task from the task overview list within the mission, using the menus. -- -- Once the TASK_CARGO is assigned to the player and accepted by the player, the player will obtain - -- an extra **Cargo Handling Radio Menu** that contains the CARGO objects that need to be transported. + -- an extra **Cargo (Radio) Menu** that contains the CARGO objects that need to be transported. -- -- Each CARGO object has a certain state: -- @@ -56,9 +56,9 @@ do -- TASK_CARGO -- * **Boarding**: The CARGO is running or moving towards your Carrier for loading. -- * **UnBoarding**: The CARGO is driving or jumping out of your Carrier and moves to a location in the Deployment Zone. -- - -- Cargo must be transported towards different **Deployment @{Zone}s**. + -- Cargo must be transported towards different Deployment @{Zone}s. -- - -- The Cargo Handling Radio Menu system allows to execute **various actions** to handle the cargo. + -- The Cargo Menu system allows to execute **various actions** to transport the cargo. -- In the menu, you'll find for each CARGO, that is part of the scope of the task, various actions that can be completed. -- Depending on the location of your Carrier unit, the menu options will vary. -- @@ -86,7 +86,7 @@ do -- TASK_CARGO -- -- ### 2.2. Task Action Menu. -- - -- When a player has joined a task, for that player only, it's carrier radio menu will show an additional menu option. + -- When a player has joined a task, for that player only, it's carrier Menu will show an additional menu option. -- It has the name of the task you currently joined and @ player name. -- -- ![Task_Types](../Tasking/Task_Briefing.JPG). @@ -116,7 +116,7 @@ do -- TASK_CARGO -- -- - **Loading**: Stationary cargo (like crates), which are heavy, can only be loaded or sling loaded, meaning, -- your carrier must be close enough to the cargo to be able to load the cargo within the carrier bays. - -- Moose provides you with an additional menu system to load stationary cargo into your carrier bays using the radio menu. + -- Moose provides you with an additional menu system to load stationary cargo into your carrier bays using the menu. -- These menu options will become available, when the carrier is within loading range. -- The Moose cargo will report to the carrier when the range is close enough. The load range is set by the mission designer. -- @@ -147,7 +147,7 @@ do -- TASK_CARGO -- -- The routing messages are formulated in the coordinate format that is currently active as configured in your settings profile. -- ![Task_Types](../Tasking/Task_Cargo_Settings.JPG) - -- Use the Settings Menu to select the coordinate format that you would like to use for location determination. + -- Use the **Settings Menu** to select the coordinate format that you would like to use for location determination. -- -- -- #### 2.3.1. Pickup Cargo. @@ -210,11 +210,11 @@ do -- TASK_CARGO -- - **Unboarding**: Moveable cargo (like infantry or vehicles), can be unboarded, that means, -- the cargo will step out of the carrier and will run to a group location. -- Moose provides you with an additional menu system to unload stationary cargo from the carrier bays, - -- using the radio menu. These menu options will become available, when the carrier is within the deploy zone. + -- using the menu. These menu options will become available, when the carrier is within the deploy zone. -- -- - **Unloading**: Stationary cargo (like crates), which are heavy, can only be unloaded or sling loaded. -- Moose provides you with an additional menu system to unload stationary cargo from the carrier bays, - -- using the radio menu. These menu options will become available, when the carrier is within the deploy zone. + -- using the menu. These menu options will become available, when the carrier is within the deploy zone. -- -- - **Sling Deploying**: Stationary cargo (like crates), which are heavy, can also be sling deployed. -- Once the cargo is within the deploy zone, the cargo can be deployed from the sling onto the ground. @@ -235,12 +235,12 @@ do -- TASK_CARGO -- -- The routing messages are formulated in the coordinate format that is currently active as configured in your settings profile. -- ![Task_Types](../Tasking/Task_Cargo_Settings.JPG) - -- Use the Settings Menu to select the coordinate format that you would like to use for location determination. + -- Use the **Settings Menu** to select the coordinate format that you would like to use for location determination. -- -- ### 2.4. Deploy Cargo. -- -- Various Deployment Zones can be foreseen in the scope of the Cargo transportation. Each deployment zone can be of varying @{Zone} type. - -- The Cargo Handling Radio Menu provides with menu options to execute an action to steer your Carrier to a specific Zone. + -- The Cargo menu provides with menu options to execute an action to steer your Carrier to a specific Zone. -- -- In order to deploy cargo, use the task action menu to select a cargo to route to. -- When selected, the HQ will send you routing messages indicating the location of the deploy zone. @@ -285,7 +285,7 @@ do -- TASK_CARGO -- -- ### Specific TASK_CARGO Events -- - -- Specific Cargo Handling event can be captured, that allow to trigger specific actions! + -- Specific Cargo event can be captured, that allow to trigger specific actions! -- -- * **Boarded**: Triggered when the Cargo has been Boarded into your Carrier. -- * **UnBoarded**: Triggered when the cargo has been Unboarded from your Carrier and has arrived at the Deployment Zone. From 6b04237a3fc8186be6322f713cf84e085a4001b6 Mon Sep 17 00:00:00 2001 From: FlightControl-User Date: Sun, 24 Jun 2018 08:49:36 +0200 Subject: [PATCH 186/420] Fixed Offset issue. --- Moose Development/Moose/Core/Zone.lua | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 4d86a72c9..82c7a0be7 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -1040,21 +1040,22 @@ ZONE_UNIT = { -- theta The azimuth of the zone relative to unit -- relative_to_unit If true, theta is measured clockwise from unit's direction else clockwise from north. If using dx, dy setting this to true makes +x parallel to unit heading. -- dx, dy OR rho, theta may be used, not both. - -- @return #ZONE_UNIT self function ZONE_UNIT:New( ZoneName, ZoneUNIT, Radius, Offset) - -- check if the inputs was reasonable, either (dx, dy) or (rho, theta) can be given, else raise an exception. - if (Offset.dx or Offset.dy) and (Offset.rho or Offset.theta) then - error("Cannot use (dx, dy) with (rho, theta)") + if Offset then + -- check if the inputs was reasonable, either (dx, dy) or (rho, theta) can be given, else raise an exception. + if (Offset.dx or Offset.dy) and (Offset.rho or Offset.theta) then + error("Cannot use (dx, dy) with (rho, theta)") + end + + self.dy = Offset.dy or 0.0 + self.dx = Offset.dx or 0.0 + self.rho = Offset.rho or 0.0 + self.theta = (Offset.theta or 0.0) * math.pi / 180.0 + self.relative_to_unit = Offset.relative_to_unit or false end - self.dy = Offset.dy or 0.0 - self.dx = Offset.dx or 0.0 - self.rho = Offset.rho or 0.0 - self.theta = (Offset.theta or 0.0) * math.pi / 180.0 - self.relative_to_unit = Offset.relative_to_unit or false - local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, ZoneUNIT:GetVec2(), Radius ) ) self:F( { ZoneName, ZoneUNIT:GetVec2(), Radius } ) From 3ed95557050d6ec69e97edf0fd1e9aaa1e10435c Mon Sep 17 00:00:00 2001 From: Van De Velde Date: Sun, 24 Jun 2018 20:47:41 +0200 Subject: [PATCH 187/420] Revert "Merge pull request #929 from Lugghawk/918_Fix_FindByName_Error_Flag" This reverts commit d6cdc098ceedcdb7df4b925e7bf2ae17643f1b29, reversing changes made to 6b04237a3fc8186be6322f713cf84e085a4001b6. --- Moose Development/Moose/Wrapper/Client.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Client.lua b/Moose Development/Moose/Wrapper/Client.lua index 4ca13c802..7df3c0586 100644 --- a/Moose Development/Moose/Wrapper/Client.lua +++ b/Moose Development/Moose/Wrapper/Client.lua @@ -74,7 +74,7 @@ CLIENT = { --- Finds a CLIENT from the _DATABASE using the relevant DCS Unit. -- @param #CLIENT self -- @param #string ClientName Name of the DCS **Unit** as defined within the Mission Editor. --- @param #boolean Error Flag indicating whether or not to throw an error if the CLIENT cannot be found. True by default. Returns `nil` if `Error` is false and no Client was found. +-- @param #string ClientBriefing Text that describes the briefing of the mission when a Player logs into the Client. -- @return #CLIENT -- @usage -- -- Create new Clients. @@ -94,7 +94,7 @@ function CLIENT:Find( DCSUnit, Error ) return ClientFound end - if Error or Error == nil then + if not Error then error( "CLIENT not found for: " .. ClientName ) end end @@ -105,7 +105,7 @@ end -- @param #CLIENT self -- @param #string ClientName Name of the DCS **Unit** as defined within the Mission Editor. -- @param #string ClientBriefing Text that describes the briefing of the mission when a Player logs into the Client. --- @param #boolean Error Flag indicating whether or not to throw an error if the CLIENT cannot be found. True by default. Returns `nil` if `Error` is false and no Client was found. +-- @param #boolean Error A flag that indicates whether an error should be raised if the CLIENT cannot be found. By default an error will be raised. -- @return #CLIENT -- @usage -- -- Create new Clients. @@ -127,7 +127,7 @@ function CLIENT:FindByName( ClientName, ClientBriefing, Error ) return ClientFound end - if Error or Error == nil then + if not Error then error( "CLIENT not found for: " .. ClientName ) end end From f5eb77cbf5de67dc00345eb9eeb7a4524b661cda Mon Sep 17 00:00:00 2001 From: funkyfranky <> Date: Mon, 25 Jun 2018 22:38:47 +0200 Subject: [PATCH 188/420] RAT 2.3.0 RAT: - Added getparking function wrappers to determin free parking spots. - Terminal type can be specified. - respawndelay is used as delay for despawn as well. - commute has new option for star shape routes. DATABASE: - Added neutral coalition support. COORDINATE: - added new search world function - added new get closest parking spot function SPAWN: - updated SpawnAtAirbase function to use getparking wrapper function. DCS: - updated country.id list AIRBASE: - Added Persion Gulf map airports - Added wrapper function for new DCS API getparking() function. --- Moose Development/Moose/Core/Database.lua | 17 +- Moose Development/Moose/Core/Point.lua | 135 +++++- Moose Development/Moose/Core/Spawn.lua | 210 +++++++-- Moose Development/Moose/DCS.lua | 55 +++ Moose Development/Moose/Functional/RAT.lua | 488 +++++++++++++++----- Moose Development/Moose/Wrapper/Airbase.lua | 283 +++++++++++- 6 files changed, 1013 insertions(+), 175 deletions(-) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 82e195f82..3b0fb0337 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -66,6 +66,7 @@ local _DATABASECoalition = { [1] = "Red", [2] = "Blue", + [3] = "Neutral", } local _DATABASECategory = @@ -116,7 +117,7 @@ function DATABASE:New() --- @param #DATABASE self local function CheckPlayers( self ) - local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) } + local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ), AlivePlayersNeutral = coalition.getPlayers( coalition.side.NEUTRAL )} for CoalitionId, CoalitionData in pairs( CoalitionsData ) do --self:E( { "CoalitionData:", CoalitionData } ) for UnitId, UnitData in pairs( CoalitionData ) do @@ -741,7 +742,7 @@ end -- @return #DATABASE self function DATABASE:_RegisterPlayers() - local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) } + local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ), AlivePlayersNeutral = coalition.getPlayers( coalition.side.NEUTRAL ) } for CoalitionId, CoalitionData in pairs( CoalitionsData ) do for UnitId, UnitData in pairs( CoalitionData ) do self:T3( { "UnitData:", UnitData } ) @@ -765,7 +766,7 @@ end -- @return #DATABASE self function DATABASE:_RegisterGroupsAndUnits() - local CoalitionsData = { GroupsRed = coalition.getGroups( coalition.side.RED ), GroupsBlue = coalition.getGroups( coalition.side.BLUE ) } + local CoalitionsData = { GroupsRed = coalition.getGroups( coalition.side.RED ), GroupsBlue = coalition.getGroups( coalition.side.BLUE ), GroupsNeutral = coalition.getGroups( coalition.side.NEUTRAL ) } for CoalitionId, CoalitionData in pairs( CoalitionsData ) do for DCSGroupId, DCSGroup in pairs( CoalitionData ) do @@ -1176,11 +1177,19 @@ function DATABASE:_RegisterTemplates() self.UNITS = {} --Build routines.db.units and self.Navpoints for CoalitionName, coa_data in pairs(env.mission.coalition) do + self:T({CoalitionName=CoalitionName}) - if (CoalitionName == 'red' or CoalitionName == 'blue') and type(coa_data) == 'table' then + if (CoalitionName == 'red' or CoalitionName == 'blue' or CoalitionName == 'neutrals') and type(coa_data) == 'table' then --self.Units[coa_name] = {} local CoalitionSide = coalition.side[string.upper(CoalitionName)] + if CoalitionName=="red" then + CoalitionSide=coalition.side.NEUTRAL + elseif CoalitionName=="blue" then + CoalitionSide=coalition.side.BLUE + else + CoalitionSide=coalition.side.NEUTRAL + end -- build nav points DB self.Navpoints[CoalitionName] = {} diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index b98159460..4cae6049b 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -292,8 +292,76 @@ do -- COORDINATE return x - Precision <= self.x and x + Precision >= self.x and z - Precision <= self.z and z + Precision >= self.z end - + --- Returns if the 2 coordinates are at the same 2D position. + -- @param #COORDINATE self + -- @param #number radius Scan radius in meters. + -- @return True if units were found. + -- @return True if statics were found. + -- @return True if scenery objects were found. + -- @return Unit objects found. + -- @return Static objects found. + -- @return Scenery objects found. + function COORDINATE:ScanObjects(radius) + env.info(string.format("FF Scanning in radius %.1f m.", radius)) + local SphereSearch = { + id = world.VolumeType.SPHERE, + params = { + point = self:GetVec3(), + radius = radius, + } + } + + -- Found stuff. + local Units = {} + local Statics = {} + local Scenery = {} + local gotstatics=false + local gotunits=false + local gotscenery=false + + local function EvaluateZone( ZoneObject ) + + if ZoneObject then + + -- Get category of scanned object. + local ObjectCategory = ZoneObject:getCategory() + + -- Check for unit or static objects + if (ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive()) then + + table.insert(Units, ZoneObject) + gotunits=true + + elseif (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then + + table.insert(Statics, ZoneObject) + gotstatics=true + + elseif ObjectCategory == Object.Category.SCENERY then + + table.insert(Scenery, ZoneObject) + gotscenery=true + + end + + end + + return true + end + + -- Search the world. + world.searchObjects({Object.Category.UNIT, Object.Category.STATIC, Object.Category.SCENERY}, SphereSearch, EvaluateZone) + + for _,unit in pairs(Units) do + env.info(string.format("FF found unit %s", unit:getName())) + end + for _,static in pairs(Statics) do + env.info(string.format("FF found unit %s", static:getName())) + end + + return gotunits, gotstatics, gotscenery, Units, Statics, Scenery + end --- Calculate the distance from a reference @{#COORDINATE}. -- @param #COORDINATE self -- @param #COORDINATE PointVec2Reference The reference @{#COORDINATE}. @@ -946,6 +1014,71 @@ do -- COORDINATE return RoutePoint end + --- Gets the nearest parking spot. + -- @param #COORDINATE self + -- @param #boolean free (Optional) Only look for free parking spots. By default the closest parking spot is returned regardless of whether it is free or not. + -- @param Wrapper.Airbase#AIRBASE airbase (Optional) Search only parking spots at that airbase. + -- @param Wrapper.Airbase#Terminaltype terminaltype Type of the terminal. + -- @return Core.Point#COORDINATE Coordinate of the nearest parking spot. + -- @return #number Distance to closest parking spot. + function COORDINATE:GetClosestParkingSpot(free, airbase, terminaltype) + + local airbases={} + if airbase then + table.insert(airbases,airbase) + else + airbases=AIRBASE:GetAllAirbases() + end + + local _closest=nil --Core.Point#COORDINATE + local _distmin=nil + for _,_airbase in pairs(airbases) do + + local mybase=_airbase --Wrapper.Airbase#AIRBASE + local parkingdata=mybase:GetParkingSpotsTable(terminaltype) + + for _,_spot in pairs(parkingdata) do + + -- Get coordinate if it matches the requirements. + local _coord=nil --Core.Point#COORDINATE + if (free and _spot.Free) or free==nil then + if (terminaltype and _spot.TerminalType==terminaltype) or terminaltype==nil then + _coord=_spot.Coordinate + end + end + + -- Compare distance to closest one found so far. + if _coord then + local _dist=self:Get2DDistance(_coord) + if _distmin==nil then + _closest=_coord + _distmin=_dist + else + local _dist=self:Get2DDistance(_coord) + if _dist<_distmin then + _distmin=_dist + _closest=_coord + end + end + + end + + end + end + + return _closest, _distmin + end + + --- Gets the nearest free parking spot. + -- @param #COORDINATE self + -- @param Wrapper.Airbase#AIRBASE airbase (Optional) Search only parking spots at that airbase. + -- @param Wrapper.Airbase#Terminaltype terminaltype Type of the terminal. + -- @return #COORDINATE Coordinate of the nearest free parking spot. + -- @return #number Distance to closest free parking spot. + function COORDINATE:GetClosestFreeParkingSpot(airbase, terminaltype) + return self:GetClosestParkingSpot(true, airbase, terminaltype) + end + --- Gets the nearest coordinate to a road. -- @param #COORDINATE self -- @return #COORDINATE Coordinate of the nearest road. diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index b07ea4421..5db251ae5 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1218,6 +1218,7 @@ end -- @param Wrapper.Airbase#AIRBASE SpawnAirbase The @{Wrapper.Airbase} where to spawn the group. -- @param #SPAWN.Takeoff Takeoff (optional) The location and takeoff method. Default is Hot. -- @param #number TakeoffAltitude (optional) The altitude above the ground. +-- @param #number TerminalType (optional) The terminal type the aircraft should be spawned at. -- @return Wrapper.Group#GROUP that was spawned. -- @return #nil Nothing was spawned. -- @usage @@ -1237,33 +1238,40 @@ end -- -- Spawn_Heli:SpawnAtAirbase( AIRBASE:FindByName( "Carrier" ), SPAWN.Takeoff.Cold ) -- -function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude ) -- R2.2 +function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalType ) -- R2.2, R2.4 self:F( { self.SpawnTemplatePrefix, SpawnAirbase, Takeoff, TakeoffAltitude } ) - local PointVec3 = SpawnAirbase:GetPointVec3() + -- Get position of airbase. + local PointVec3 = SpawnAirbase:GetCoordinate() self:T2(PointVec3) + -- Set take off type. Default is hot. Takeoff = Takeoff or SPAWN.Takeoff.Hot if self:_GetSpawnIndex( self.SpawnIndex + 1 ) then + -- Get group template. local SpawnTemplate = self.SpawnGroups[self.SpawnIndex].SpawnTemplate if SpawnTemplate then + -- Debug output self:T( { "Current point of ", self.SpawnTemplatePrefix, SpawnAirbase } ) + -- First waypoint of the group. local SpawnPoint = SpawnTemplate.route.points[1] - -- These are only for ships. + -- These are only for ships and FARPS. SpawnPoint.linkUnit = nil SpawnPoint.helipadId = nil SpawnPoint.airdromeId = nil + -- Get airbase ID and category. local AirbaseID = SpawnAirbase:GetID() local AirbaseCategory = SpawnAirbase:GetDesc().category self:F( { AirbaseCategory = AirbaseCategory } ) + -- Set airdromeId. if AirbaseCategory == Airbase.Category.SHIP then SpawnPoint.linkUnit = AirbaseID SpawnPoint.helipadId = AirbaseID @@ -1274,57 +1282,177 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude ) -- R2.2 SpawnPoint.airdromeId = AirbaseID end - SpawnPoint.alt = 0 - - SpawnPoint.type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type + SpawnPoint.alt = 0 + SpawnPoint.type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type SpawnPoint.action = GROUPTEMPLATE.Takeoff[Takeoff][2] -- action + -- Check if we spawn on ground. + local spawnonground=not (Takeoff==SPAWN.Takeoff.Air) + self:E({spawnonground=spawnonground, takeoff=Takeoff, toair=Takeoff==SPAWN.Takeoff.Air}) + + -- Check where we actually spawn if we spawn on ground. + local spawnonship=false + local spawnonfarp=false + local spawnonrunway=false + local spawnonairport=false + if spawnonground then + + -- Spawning at a ship + spawnonship=SpawnAirbase:GetCategory()==1 -- Catetory 1 are ships. + + -- Spawning at a FARP. Catetory 4 are airbases so we need to check that type is FARP as well. + spawnonfarp=SpawnAirbase:GetCategory()==4 and SpawnAirbase:GetTypeName()=="FARP" + + -- Spawning at an airport. + spawnonairport=SpawnAirbase:GetCategory()==4 and SpawnAirbase:GetTypeName()~="FARP" + + -- Spawning on the runway. + spawnonrunway=Takeoff==SPAWN.Takeoff.Runway + end + + + -- Array with parking spots coordinates. + local parkingspots={} + local parkingindex={} + local spots + + -- Spawn happens on ground, i.e. at an airbase, a FARP or a ship. + if spawnonground then + + -- Number of free parking spots. + local nfree=0 + + -- Set terminal type. + local termtype=TerminalType + if spawnonrunway then + termtype=AIRBASE.TerminalType.Runway + end + + -- Number of free parking spots at the airbase. + nfree=SpawnAirbase:GetFreeParkingSpotsNumber(termtype, spawnonship or spawnonfarp or spawnonrunway) + spots=SpawnAirbase:GetFreeParkingSpotsTable(termtype, spawnonship or spawnonfarp or spawnonrunway) + + -- Get parking data. + local parkingdata=SpawnAirbase:GetParkingSpotsTable(termtype) + + self:E(string.format("Parking at %s, terminal type %s:", SpawnAirbase:GetName(), tostring(termtype))) + for _,_spot in pairs(parkingdata) do + self:E(string.format("%s, Termin Index = %3d, Term Type = %03d, Free = %5s, TOAC = %5s, Term ID0 = %3d, Dist2Rwy = %4d", + SpawnAirbase:GetName(), _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy)) + end + self:E(string.format("%s at %s: free parking spots = %d - number of units = %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), nfree, #SpawnTemplate.units)) + + -- Put parking spots in table. These spots are only used if + if nfree >= #SpawnTemplate.units or (spawnonrunway and nfree>0) then + + for i=1,#SpawnTemplate.units do + table.insert(parkingspots, spots[i].Coordinate) + table.insert(parkingindex, spots[i].TerminalID) + end + + else + self:E(string.format("Group %s has no parking spots at %s ==> air start!", self.SpawnTemplatePrefix, SpawnAirbase:GetName())) + + -- Not enough parking spots at the airport ==> Spawn in air. + spawnonground=false + spawnonship=false + spawnonfarp=false + spawnonrunway=false + + -- Set waypoint type/action to turning point. + SpawnPoint.type = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][1] -- type = Turning Point + SpawnPoint.action = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][2] -- action = Turning Point + + -- Adjust altitude to be 500-1000 m above the airbase. + PointVec3.y=PointVec3:GetLandHeight()+math.random(200,1200) + + Takeoff=GROUP.Takeoff.Air + end + + end + -- Translate the position of the Group Template to the Vec3. for UnitID = 1, #SpawnTemplate.units do - self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y ) - - -- These cause a lot of confusion. + self:T2('Before Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) + + -- Template of the current unit. local UnitTemplate = SpawnTemplate.units[UnitID] - - UnitTemplate.parking = nil - UnitTemplate.parking_id = nil - UnitTemplate.alt = 0 - + + -- Tranlate position and preserve the relative position/formation of all aircraft. local SX = UnitTemplate.x local SY = UnitTemplate.y - local BX = SpawnPoint.x - local BY = SpawnPoint.y - local TX = PointVec3.x + ( SX - BX ) - local TY = PointVec3.z + ( SY - BY ) - - UnitTemplate.x = TX - UnitTemplate.y = TY - - if Takeoff == GROUP.Takeoff.Air then - UnitTemplate.alt = PointVec3.y + ( TakeoffAltitude or 200 ) - --else - -- UnitTemplate.alt = PointVec3.y + 10 - end - self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. UnitTemplate.x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. UnitTemplate.y ) - end - - SpawnPoint.x = PointVec3.x - SpawnPoint.y = PointVec3.z - - if Takeoff == GROUP.Takeoff.Air then - SpawnPoint.alt = PointVec3.y + ( TakeoffAltitude or 200 ) - --else - -- SpawnPoint.alt = PointVec3.y + 10 - end + local BX = SpawnTemplate.route.points[1].x + local BY = SpawnTemplate.route.points[1].y + local TX = PointVec3.x + (SX-BX) + local TY = PointVec3.z + (SY-BY) + + if spawnonground then + + -- Ships and FARPS seem to have a build in queue. + if spawnonship or spawnonfarp or spawnonrunway then + + self:T(string.format("Group %s spawning at farp, ship or runway %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName())) + -- Spawn on ship. We take only the position of the ship. + SpawnTemplate.units[UnitID].x = PointVec3.x --TX + SpawnTemplate.units[UnitID].y = PointVec3.z --TY + SpawnTemplate.units[UnitID].alt = PointVec3.y + + else + + self:T(string.format("Group %s spawning at airbase %s on parking spot id %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), parkingindex[UnitID])) + + -- Get coordinates of parking spot. + SpawnTemplate.units[UnitID].x = parkingspots[UnitID].x + SpawnTemplate.units[UnitID].y = parkingspots[UnitID].z + SpawnTemplate.units[UnitID].alt = parkingspots[UnitID].y + + end + + else + + self:T(string.format("Group %s spawning in air at %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName())) + + -- Spawn in air as requested initially. Original template orientation is perserved, altitude is already correctly set. + SpawnTemplate.units[UnitID].x = TX + SpawnTemplate.units[UnitID].y = TY + SpawnTemplate.units[UnitID].alt = PointVec3.y + + end + + -- Parking spot id. + UnitTemplate.parking = nil + UnitTemplate.parking_id = nil + if parkingindex[UnitID] then + UnitTemplate.parking = parkingindex[UnitID] + end + + + -- Place marker at spawn position. + --if self.Debug then + local unitspawn=COORDINATE:New(SpawnTemplate.units[UnitID].x, SpawnTemplate.units[UnitID].alt, SpawnTemplate.units[UnitID].y) + unitspawn:MarkToAll(string.format("%s Spawnplace unit #%d, terminal %s", self.SpawnTemplatePrefix, UnitID, tostring(UnitTemplate.parking))) + --end + + self:T2(string.format("Group %s unit number %d: Parking = %s",self.SpawnTemplatePrefix, UnitID, tostring(UnitTemplate.parking))) + self:T2(string.format("Group %s unit number %d: Parking ID = %s",self.SpawnTemplatePrefix, UnitID, tostring(UnitTemplate.parking_id))) + + self:T2('After Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) + end + + -- Set gereral spawnpoint position. + SpawnPoint.x = PointVec3.x + SpawnPoint.y = PointVec3.z + SpawnPoint.alt = PointVec3.y + SpawnTemplate.x = PointVec3.x SpawnTemplate.y = PointVec3.z + -- Spawn group. local GroupSpawned = self:SpawnWithIndex( self.SpawnIndex ) - -- When spawned in the air, we need to generate a Takeoff Event - + -- When spawned in the air, we need to generate a Takeoff Event. if Takeoff == GROUP.Takeoff.Air then for UnitID, UnitSpawned in pairs( GroupSpawned:GetUnits() ) do SCHEDULER:New( nil, BASE.CreateEventTakeoff, { GroupSpawned, timer.getTime(), UnitSpawned:GetDCSObject() } , 1 ) @@ -1801,7 +1929,7 @@ end --- Get the index from a given group. -- The function will search the name of the group for a #, and will return the number behind the #-mark. function SPAWN:GetSpawnIndexFromGroup( SpawnGroup ) - self:F( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } ) + self:F2( { self.SpawnTemplatePrefix, self.SpawnAliasPrefix, SpawnGroup } ) local IndexString = string.match( SpawnGroup:GetName(), "#(%d*)$" ):sub( 2 ) local Index = tonumber( IndexString ) diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua index 19bb8e47c..253632052 100644 --- a/Moose Development/Moose/DCS.lua +++ b/Moose Development/Moose/DCS.lua @@ -155,6 +155,7 @@ do -- country -- @field UK -- @field FRANCE -- @field GERMANY + -- @field AGGRESSORS -- @field CANADA -- @field SPAIN -- @field THE_NETHERLANDS @@ -167,6 +168,60 @@ do -- country -- @field ABKHAZIA -- @field SOUTH_OSETIA -- @field ITALY + -- @field AUSTRALIA + -- @field SWITZERLAND + -- @field AUSTRIA + -- @field BELARUS + -- @field BULGARIA + -- @field CHEZH_REPUBLIC + -- @field CHINA + -- @field CROATIA + -- @field EGYPT + -- @field FINLAND + -- @field GREECE + -- @field HUNGARY + -- @field INDIA + -- @field IRAN + -- @field IRAQ + -- @field JAPAN + -- @field KAZAKHSTAN + -- @field NORTH_KOREA + -- @field PAKISTAN + -- @field POLAND + -- @field ROMANIA + -- @field SAUDI_ARABIA + -- @field SERBIA + -- @field SLOVAKIA + -- @field SOUTH_KOREA + -- @field SWEDEN + -- @field SYRIA + -- @field YEMEN + -- @field VIETNAM + -- @field VENEZUELA + -- @field TUNISIA + -- @field THAILAND + -- @field SUDAN + -- @field PHILIPPINES + -- @field MOROCCO + -- @field MEXICO + -- @field MALAYSIA + -- @field LIBYA + -- @field JORDAN + -- @field INDONESIA + -- @field HONDURAS + -- @field ETHIOPIA + -- @field CHILE + -- @field BRAZIL + -- @field BAHRAIN + -- @field THIRDREICH + -- @field YUGOSLAVIA + -- @field USSR + -- @field ITALIAN_SOCIAL_REPUBLIC + -- @field ALGERIA + -- @field KUWAIT + -- @field QATAR + -- @field OMAN + -- @field UNITED_ARAB_EMIRATES country = {} -- #country diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 53b4d67f6..7653e9261 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -70,6 +70,7 @@ -- @field #number coalition Coalition of spawn group template. -- @field #number country Country of spawn group template. -- @field #string category Category of aircarft: "plane" or "heli". +-- @field #number groupsize Number of aircraft in group. -- @field #string friendly Possible departure/destination airport: all=blue+red+neutral, same=spawn+neutral, spawnonly=spawn, blue=blue+neutral, blueonly=blue, red=red+neutral, redonly=red. -- @field #table ctable Table with the valid coalitons from choice self.friendly. -- @field #table aircraft Table which holds the basic aircraft properties (speed, range, ...). @@ -146,6 +147,9 @@ -- @field #boolean checkontop Aircraft are checked if they were accidentally spawned on top of another unit. Default is true. -- @field #number rbug_maxretry Number of respawn retries (on ground) at other airports if a group gets accidentally spawned on the runway. Default is 3. -- @field #boolean useparkingdb Parking spots are added to data base once an aircraft has used it. These spots can later be used by other aircraft. Default is true. +-- @field #number termtype Type of terminal to be used when spawning at an airbase. +-- @field #boolean starshape If true, aircraft travel A-->B-->A-->C-->A-->D... for commute. +-- @field #string homebase Home base for commute and return zone. Aircraft will always return to this base but otherwise travel in a star shaped way. -- @extends Core.Spawn#SPAWN --- Implements an easy to use way to randomly fill your map with AI aircraft. @@ -302,6 +306,7 @@ RAT={ coalition = nil, -- Coalition of spawn group template. country = nil, -- Country of the group template. category = nil, -- Category of aircarft: "plane" or "heli". + groupsize=nil, -- Number of aircraft in the group. friendly = "same", -- Possible departure/destination airport: same=spawn+neutral, spawnonly=spawn, blue=blue+neutral, blueonly=blue, red=red+neutral, redonly=red, neutral. ctable = {}, -- Table with the valid coalitons from choice self.friendly. aircraft = {}, -- Table which holds the basic aircraft properties (speed, range, ...). @@ -353,7 +358,7 @@ RAT={ respawn_after_takeoff=false, -- Aircraft will be respawned directly after takeoff. respawn_after_crash=true, -- Aircraft will be respawned after a crash. respawn_inair=true, -- Aircraft are spawned in air if there is no free parking spot on the ground. - respawn_delay=nil, -- Delay in seconds until repawn happens after landing. + respawn_delay=0, -- Delay in seconds until repawn happens after landing. markerids={}, -- Array with marker IDs. waypointdescriptions={}, -- Array with descriptions for waypoint markers. waypointstatus={}, -- Array with status info on waypoints. @@ -377,8 +382,11 @@ RAT={ onboardnum0=1, -- (Optional) Starting value of the automatically appended numbering of aircraft within a flight. Default is one. rbug_maxretry=3, -- Number of respawn retries (on ground) at other airports if a group gets accidentally spawned on the runway. checkonrunway=true, -- Check whether aircraft have been spawned on the runway. - checkontop=true, -- Check whether aircraft have been spawned on top of another unit. - useparkingdb=true, -- Put known parking spots into a data base. + checkontop=false, -- Check whether aircraft have been spawned on top of another unit. + useparkingdb=false, -- Put known parking spots into a data base. + termtype=nil, -- Terminal type. + starshape=false, -- If true, aircraft travel A-->B-->A-->C-->A-->D... for commute. + homebase=nil, -- Home base for commute and return zone. } ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -501,7 +509,7 @@ RAT.id="RAT | " --- RAT version. -- @list version RAT.version={ - version = "2.2.2", + version = "2.3.0", print = true, } @@ -565,7 +573,7 @@ function RAT:New(groupname, alias) end -- Welcome message. - self:F(RAT.id.."Creating new RAT object from template: "..groupname) + self:F(RAT.id..string.format("Creating new RAT object from template: %s.", groupname)) -- Set alias. alias=alias or groupname @@ -578,13 +586,16 @@ function RAT:New(groupname, alias) -- Check the group actually exists. if DCSgroup==nil then - self:E(RAT.id.."ERROR: Group with name "..groupname.." does not exist in the mission editor!") + self:E(RAT.id..string.format("ERROR: Group with name %s does not exist in the mission editor!", groupname)) return nil end -- Store template group. self.templategroup=GROUP:FindByName(groupname) + -- Get number of aircraft in group. + self.groupsize=self.templategroup:GetSize() + -- Set own coalition. self.coalition=DCSgroup:getCoalition() @@ -690,6 +701,7 @@ function RAT:Spawn(naircraft) end text=text..string.format("Min dist to destination: %4.1f\n", self.mindist) text=text..string.format("Max dist to destination: %4.1f\n", self.maxdist) + text=text..string.format("Terminal type: %s\n", tostring(self.termtype)) text=text..string.format("Takeoff type: %i\n", self.takeoff) text=text..string.format("Landing type: %i\n", self.landing) text=text..string.format("Commute: %s\n", tostring(self.commute)) @@ -814,12 +826,12 @@ function RAT:_CheckConsistency() -- Only zones but not takeoff air == > Enable takeoff air. if self.Ndeparture_Zones>0 and self.takeoff~=RAT.wp.air then self.takeoff=RAT.wp.air - self:E(RAT.id.."ERROR: At least one zone defined as departure and takeoff is NOT set to air. Enabling air start!") + self:E(RAT.id..string.format("ERROR: At least one zone defined as departure and takeoff is NOT set to air. Enabling air start for RAT group %s!", self.alias)) end -- No airport and no zone specified. if self.Ndeparture_Airports==0 and self.Ndeparture_Zone==0 then self.random_departure=true - local text="No airports or zones found given in SetDeparture(). Enabling random departure airports!" + local text=string.format("No airports or zones found given in SetDeparture(). Enabling random departure airports for RAT group %s!", self.alias) self:E(RAT.id.."ERROR: "..text) MESSAGE:New(text, 30):ToAll() end @@ -951,6 +963,14 @@ function RAT:SetCountry(id) self.country=id end +--- Set the terminal type the aircraft use when spawning at an airbase. Cf. https://wiki.hoggitworld.com/view/DCS_func_getParking +-- @param #RAT self +-- @param #number termtype Type of terminal. Use enumerator AIRBASE.TerminalType.XXX or check https://wiki.hoggitworld.com/view/DCS_func_getParking for valid numbers. +function RAT:SetTerminalType(termtype) + self:F2(termtype) + self.termtype=termtype +end + --- Set takeoff type. Starting cold at airport, starting hot at airport, starting at runway, starting in the air. -- Default is "takeoff-coldorhot". So there is a 50% chance that the aircraft starts with cold engines and 50% that it starts with hot engines. -- @param #RAT self @@ -1177,10 +1197,16 @@ end --- Aircraft will commute between their departure and destination airports or zones. -- @param #RAT self -function RAT:Commute() +-- @param #boolean starshape If true, keep homebase, i.e. travel A-->B-->A-->C-->A-->D... instead of A-->B-->A-->B-->A... +function RAT:Commute(starshape) self:F2() self.commute=true self.continuejourney=false + if starshape then + self.starshape=starshape + else + self.starshape=false + end end --- Set the delay before first group is spawned. @@ -1203,12 +1229,12 @@ end --- Make aircraft respawn the moment they land rather than at engine shut down. -- @param #RAT self --- @param #number delay (Optional) Delay in seconds until respawn happens after landing. Default is 180 seconds. Minimum is 0.5 seconds. +-- @param #number delay (Optional) Delay in seconds until respawn happens after landing. Default is 180 seconds. Minimum is 1.0 seconds. function RAT:RespawnAfterLanding(delay) self:F2(delay) delay = delay or 180 self.respawn_at_landing=true - delay=math.max(0.5, delay) + delay=math.max(1.0, delay) self.respawn_delay=delay end @@ -1217,7 +1243,7 @@ end -- @param #number delay Delay in seconds until respawn happens. Default is 1 second. Minimum is 1 second. function RAT:SetRespawnDelay(delay) self:F2(delay) - delay = delay or 1 + delay = delay or 1.0 delay=math.max(1.0, delay) self.respawn_delay=delay end @@ -1786,9 +1812,9 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live -- Find parking spot in RAT parking DB. Category 4 should be airports and farps. Ships would be caterory 1. local _spawnpos=_lastpos - if self.useparkingdb and (takeoff==RAT.wp.cold or takeoff==RAT.wp.hot) and departure:GetCategory()==4 and _spawnpos==nil then - _spawnpos=self:_FindParkingSpot(departure) - end +-- if self.useparkingdb and (takeoff==RAT.wp.cold or takeoff==RAT.wp.hot) and departure:GetCategory()==4 and _spawnpos==nil then +-- _spawnpos=self:_FindParkingSpot(departure) +-- end -- Set (another) livery. local livery @@ -1805,7 +1831,7 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live end -- Modify the spawn template to follow the flight plan. - self:_ModifySpawnTemplate(waypoints, livery, _spawnpos) + self:_ModifySpawnTemplate(waypoints, livery, _spawnpos, departure, takeoff) -- Actually spawn the group. local group=self:SpawnWithIndex(self.SpawnIndex) -- Wrapper.Group#GROUP @@ -1889,7 +1915,6 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live self.ratcraft[self.SpawnIndex].nrespawn=nrespawn -- If we start at a parking position, we memorize the parking spot position for future use (DCS bug). - -- TODO: Check for ships and FARPS. if self.useparkingdb and (takeoff==RAT.wp.cold or takeoff==RAT.wp.hot) and departure:GetCategory()==4 then self:_AddParkingSpot(departure, group) end @@ -1930,11 +1955,13 @@ end --- Respawn a group. -- @param #RAT self --- @param Wrapper.Group#GROUP group Group to be repawned. -function RAT:_Respawn(group) +-- @param #number index Spawn index. +-- @param Core.Point#COORDINATE lastpos Last known position of the group. +-- @param #number delay Delay before respawn +function RAT:_Respawn(index, lastpos, delay) -- Get the spawn index from group - local index=self:GetSpawnIndexFromGroup(group) + --local index=self:GetSpawnIndexFromGroup(group) -- Get departure and destination from previous journey. local departure=self.ratcraft[index].departure @@ -1943,7 +1970,7 @@ function RAT:_Respawn(group) local landing=self.ratcraft[index].landing local livery=self.ratcraft[index].livery local lastwp=self.ratcraft[index].waypoints[#self.ratcraft[index].waypoints] - local lastpos=group:GetCoordinate() + --local lastpos=group:GetCoordinate() local _departure=nil local _destination=nil @@ -2004,8 +2031,22 @@ function RAT:_Respawn(group) elseif self.commute then -- We commute between departure and destination. - _departure=destination:GetName() - _destination=departure:GetName() + + if self.starshape==true then + if destination:GetName()==self.homebase then + -- We are at our home base ==> destination is again randomly selected. + _departure=self.homebase + _destination=nil -- destination will be set anew + else + -- We are not a our home base ==> we fly back to our home base. + _departure=destination:GetName() + _destination=self.homebase + end + else + -- Simply switch departure and destination. + _departure=destination:GetName() + _destination=departure:GetName() + end -- Use the same livery for next aircraft. _livery=livery @@ -2067,6 +2108,16 @@ function RAT:_Respawn(group) -- Debug self:T2({departure=_departure, destination=_destination, takeoff=_takeoff, landing=_landing, livery=_livery, lastwp=_lastwp}) + + -- We should give it at least 3 sec since this seems to be the time until free parking spots after despawn are available again (Sirri Island test). + local respawndelay + if delay then + respawndelay=delay + elseif self.respawn_delay then + respawndelay=self.respawn_delay+3 -- despawn happens after self.respawndelay. We add another 3 sec for free parking. + else + respawndelay=3 + end -- Spawn new group. local arg={} @@ -2078,7 +2129,8 @@ function RAT:_Respawn(group) arg.livery=_livery arg.lastwp=_lastwp arg.lastpos=_lastpos - SCHEDULER:New(nil, self._SpawnWithRouteTimer, {arg}, self.respawn_delay or 1) + self:T(RAT.id..string.format("%s delayed respawn in %.1f seconds.", self.alias, respawndelay)) + SCHEDULER:New(nil, self._SpawnWithRouteTimer, {arg}, respawndelay) end @@ -2166,6 +2218,9 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) else departure=self:_PickDeparture(takeoff) + if self.commute and self.starshape==true and self.homebase==nil then + self.homebase=departure:GetName() + end end -- Return nil if no departure could be found. @@ -3031,14 +3086,18 @@ function RAT:Status(message, forID) -- Current time. local Tnow=timer.getTime() + + -- Alive counter. + local nalive=0 - -- Loop over all ratcraft. + -- Loop over all ratcraft. for spawnindex,ratcraft in ipairs(self.ratcraft) do -- Get group. local group=ratcraft.group --Wrapper.Group#GROUP if group and group:IsAlive() then + nalive=nalive+1 -- Gather some information. local prefix=self:_GetPrefixFromGroup(group) @@ -3143,7 +3202,7 @@ function RAT:Status(message, forID) text=text..string.format("\nTime on ground = %6.0f seconds\n", Tg) text=text..string.format("Position change = %8.1f m since %3.0f seconds.", Dg, dTlast) end - self:T2(RAT.id..text) + self:T(RAT.id..text) if message then MESSAGE:New(text, 20):ToAll() end @@ -3158,23 +3217,29 @@ function RAT:Status(message, forID) self:T(RAT.id..text) self:_Despawn(group) end + -- Despawn group if life is < 10% and distance travelled < 100 m. if life<10 and Dtravel<100 then local text=string.format("Damaged group %s is despawned. Life = %3.0f", self.alias, life) self:T(RAT.id..text) self:_Despawn(group) end + end -- Despawn groups after they have reached their destination zones. if ratcraft.despawnme then + local text=string.format("Flight %s will be despawned NOW!", self.alias) self:T(RAT.id..text) -- Despawn old group. - if (not self.norespawn) and (not self.respawn_after_takeoff) then - self:_Respawn(group) + if (not self.norespawn) and (not self.respawn_after_takeoff) then + local idx=self:GetSpawnIndexFromGroup(group) + local coord=group:GetCoordinate() + self:_Respawn(idx, coord, 0) end - self:_Despawn(group) + self:_Despawn(group, 0) + end else @@ -3185,11 +3250,10 @@ function RAT:Status(message, forID) end - if (message and not forID) then - local text=string.format("Alive groups of %s: %d", self.alias, self.alive) - self:T(RAT.id..text) - MESSAGE:New(text, 20):ToAll() - end + -- Alive groups. + local text=string.format("Alive groups of %s: %d, nalive=%d/%d", self.alias, self.alive, nalive, self.ngroups) + self:T(RAT.id..text) + MESSAGE:New(text, 20):ToAllIf(message and not forID) end @@ -3236,7 +3300,7 @@ function RAT:_SetStatus(group, status) local text=string.format("Flight %s: %s.", group:GetName(), status) self:T(RAT.id..text) - if (not (no1 or no2 or no3)) then + if not (no1 or no2 or no3) then MESSAGE:New(text, 10):ToAllIf(self.reportstatus) end @@ -3245,6 +3309,30 @@ function RAT:_SetStatus(group, status) end end +--- Get status of group. +-- @param #RAT self +-- @param Wrapper.Group#GROUP group Group. +-- @return #string status Status of group. +function RAT:GetStatus(group) + + if group and group:IsAlive() then + + -- Get index from groupname. + local index=self:GetSpawnIndexFromGroup(group) + + if self.ratcraft[index] then + + -- Set new status. + return self.ratcraft[index].status + + end + + end + + return "nonexistant" +end + + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Function is executed when a unit is spawned. @@ -3344,7 +3432,7 @@ function RAT:_OnBirth(EventData) -- Check if any unit of the group was spawned on top of another unit in the MOOSE data base. local ontop=false if self.checkontop then - ontop=self:_CheckOnTop(SpawnGroup) + ontop=self:_CheckOnTop(SpawnGroup, 2) end if ontop then @@ -3429,12 +3517,11 @@ function RAT:_OnTakeoff(EventData) self:_SetStatus(SpawnGroup, status) if self.respawn_after_takeoff then - text="Event: Group "..SpawnGroup:GetName().." will be respawned." + text="Event: Group "..SpawnGroup:GetName().." will be respawned after takeoff." self:T(RAT.id..text) -- Respawn group. We respawn with no parameters from the old flight. self:_SpawnWithRoute(nil, nil, nil, nil, nil, nil, nil, nil) - --self:_Respawn(SpawnGroup) end end @@ -3479,7 +3566,9 @@ function RAT:_OnLand(EventData) self:T(RAT.id..text) -- Respawn group. - self:_Respawn(SpawnGroup) + local idx=self:GetSpawnIndexFromGroup(SpawnGroup) + local coord=SpawnGroup:GetCoordinate() + self:_Respawn(idx, coord) end end @@ -3492,6 +3581,7 @@ end --- Function is executed when a unit shuts down its engines. -- @param #RAT self +-- @param Core.Event#EVENTDATA EventData function RAT:_OnEngineShutdown(EventData) self:F3(EventData) self:T3(RAT.id.."Captured event EngineShutdown!") @@ -3503,35 +3593,42 @@ function RAT:_OnEngineShutdown(EventData) -- Get the template name of the group. This can be nil if this was not a spawned group. local EventPrefix = self:_GetPrefixFromGroup(SpawnGroup) - if EventPrefix then - - -- Check that the template name actually belongs to this object. - if EventPrefix == self.alias then - - -- Despawn group only if it on the ground. - if not SpawnGroup:InAir() then - - local text="Event: Group "..SpawnGroup:GetName().." shut down its engines." - self:T(RAT.id..text) + -- Check that the template name actually belongs to this object. + if EventPrefix and EventPrefix == self.alias then + -- Despawn group only if it on the ground. + if not SpawnGroup:InAir() then + + -- Current status. + local currentstate=self:GetStatus(SpawnGroup) + + local text=string.format("Event: Unit %s of group %s shut down its engines. Current state %s.", EventData.IniUnitName, SpawnGroup:GetName(), currentstate) + self:T(RAT.id..text) + + -- Check that this is not the second unit of the group so that we dont trigger re- and despawns twice. + if currentstate~=RAT.status.EventEngineShutdown and currentstate~="Dead" then + -- Set status. local status=RAT.status.EventEngineShutdown self:_SetStatus(SpawnGroup, status) if not self.respawn_at_landing and not self.norespawn then - text="Event: Group "..SpawnGroup:GetName().." will be respawned." + text=string.format("Event: Group %s will be respawned. Current state %s => new state %s.", SpawnGroup:GetName(), currentstate, status) self:T(RAT.id..text) -- Respawn group. - self:_Respawn(SpawnGroup) + local idx=self:GetSpawnIndexFromGroup(SpawnGroup) + local coord=SpawnGroup:GetCoordinate() + self:_Respawn(idx, coord) end - + -- Despawn group. text="Event: Group "..SpawnGroup:GetName().." will be destroyed now." self:T(RAT.id..text) - self:_Despawn(SpawnGroup) + self:_Despawn(SpawnGroup) end + end end @@ -3547,22 +3644,20 @@ function RAT:_OnHit(EventData) self:F3(EventData) self:T(RAT.id..string.format("Captured event Hit by %s! Initiator %s. Target %s", self.alias, tostring(EventData.IniUnitName), tostring(EventData.TgtUnitName))) - local SpawnGroup = EventData.IniGroup --Wrapper.Group#GROUP + local SpawnGroup = EventData.TgtGroup --Wrapper.Group#GROUP if SpawnGroup then -- Get the template name of the group. This can be nil if this was not a spawned group. local EventPrefix = self:_GetPrefixFromGroup(SpawnGroup) - if EventPrefix then - - -- Check that the template name actually belongs to this object. - if EventPrefix == self.alias then + -- Check that the template name actually belongs to this object. + if EventPrefix and EventPrefix == self.alias then + -- Debug info. + self:T(RAT.id..string.format("Event: Group %s was hit. Unit %s.", SpawnGroup:GetName(), tostring(EventData.TgtUnitName))) - -- Debug info. - self:T(RAT.id..string.format("Event: Group %s was hit. Unit %s.", SpawnGroup:GetName(), EventData.IniUnitName)) - - end + local text=string.format("%s, unit %s was hit!", self.alias, EventData.TgtUnitName) + MESSAGE:New(text, 10):ToAllIf(self.reportstatus or self.Debug) end end end @@ -3658,36 +3753,33 @@ function RAT:_OnCrash(EventData) -- Get the template name of the group. This can be nil if this was not a spawned group. local EventPrefix = self:_GetPrefixFromGroup(SpawnGroup) - if EventPrefix then + -- Check that the template name actually belongs to this object. + if EventPrefix and EventPrefix == self.alias then - -- Check that the template name actually belongs to this object. - if EventPrefix == self.alias then - - -- Update number of alive units in the group. - local _i=self:GetSpawnIndexFromGroup(SpawnGroup) - self.ratcraft[_i].nunits=self.ratcraft[_i].nunits-1 - local _n=self.ratcraft[_i].nunits - local _n0=SpawnGroup:GetInitialSize() - - -- Debug info. - local text=string.format("Event: Group %s crashed. Unit %s. Units still alive %d of %d.", SpawnGroup:GetName(), EventData.IniUnitName, _n, _n0) + -- Update number of alive units in the group. + local _i=self:GetSpawnIndexFromGroup(SpawnGroup) + self.ratcraft[_i].nunits=self.ratcraft[_i].nunits-1 + local _n=self.ratcraft[_i].nunits + local _n0=SpawnGroup:GetInitialSize() + + -- Debug info. + local text=string.format("Event: Group %s crashed. Unit %s. Units still alive %d of %d.", SpawnGroup:GetName(), EventData.IniUnitName, _n, _n0) + self:T(RAT.id..text) + + -- Set status. + local status=RAT.status.EventCrash + self:_SetStatus(SpawnGroup, status) + + -- Respawn group if all units are dead. + if _n==0 and self.respawn_after_crash and not self.norespawn then + local text=string.format("No units left of group %s. Group will be respawned now.", SpawnGroup:GetName()) self:T(RAT.id..text) - - -- Set status. - local status=RAT.status.EventCrash - self:_SetStatus(SpawnGroup, status) - - -- Respawn group if all units are dead. - if _n==0 and self.respawn_after_crash and not self.norespawn then - local text=string.format("No units left of group %s. Group will be respawned now.", SpawnGroup:GetName()) - self:T(RAT.id..text) - -- Respawn group. - self:_Respawn(SpawnGroup) - end - - --TODO: Maybe spawn some people at the crash site and send a distress call. - -- And define them as cargo which can be rescued. + -- Respawn group. + local idx=self:GetSpawnIndexFromGroup(SpawnGroup) + local coord=SpawnGroup:GetCoordinate() + self:_Respawn(idx, coord) end + end else @@ -3701,7 +3793,8 @@ end -- Index of ratcraft array is taken from spawned group name. -- @param #RAT self -- @param Wrapper.Group#GROUP group Group to be despawned. -function RAT:_Despawn(group) +-- @param #number delay Delay in seconds before the despawn happens. +function RAT:_Despawn(group, delay) if group ~= nil then @@ -3741,8 +3834,23 @@ function RAT:_Despawn(group) --table.remove(self.ratcraft, index) - -- This will destroy the DCS group and create a single DEAD event. - self:_Destroy(group) + -- We should give it at least 3 sec since this seems to be the time until free parking spots after despawn are available again (Sirri Island test). + local despawndelay=0 + if delay then + -- Explicitly requested delay time. + despawndelay=delay + elseif self.respawn_delay then + -- Despawn afer respawn_delay. Actual respawn happens in +3 seconds to allow for free parking. + despawndelay=self.respawn_delay + end + + -- This will destroy the DCS group and create a single DEAD event. + --if despawndelay>0.5 then + self:T(RAT.id..string.format("%s delayed despawn in %.1f seconds.", self.alias, despawndelay)) + SCHEDULER:New(nil, self._Destroy, {self, group}, despawndelay) + --else + --self:_Destroy(group) + --end -- Remove submenu for this group. if self.f10menu and self.SubMenuName ~= nil then @@ -3764,11 +3872,6 @@ function RAT:_Destroy(group) local DCSGroup = group:GetDCSObject() -- DCS#Group if DCSGroup and DCSGroup:isExist() then - - --local DCSUnit = DCSGroup:getUnit(1) -- DCS#Unit - --if DCSUnit then - -- self:_CreateEventDead(timer.getTime(), DCSUnit) - --end -- Cread one single Dead event and delete units from database. local triggerdead=true @@ -4421,11 +4524,11 @@ end --- Find aircraft that have accidentally been spawned on top of each other. -- @param #RAT self -- @param Wrapper.Group#GROUP group Units of this group will be checked. +-- @param #number distmin Allowed distance in meters between units. Units with a distance smaller than this number are considered to be on top of each other. -- @return #boolean True if group was destroyed because it was on top of another unit. False if otherwise. -function RAT:_CheckOnTop(group) +function RAT:_CheckOnTop(group, distmin) - -- Minimum allowed distance between two units - local distmin=5 + distmin=distmin or 2 for i,uniti in pairs(group:GetUnits()) do local uniti=uniti --Wrapper.Unit#UNIT @@ -4448,15 +4551,13 @@ function RAT:_CheckOnTop(group) if DCSuniti and DCSuniti:isExist() and DCSunitj and DCSunitj:isExist() then -- Distance between units. - local _dist=uniti:GetCoordinate():Get2DDistance(unitj:GetCoordinate()) + local _dist=uniti:GetCoordinate():Get3DDistance(unitj:GetCoordinate()) -- Check for min distance. if _dist < distmin then if not uniti:InAir() and not unitj:InAir() then - --uniti:Destroy() - --self:_CreateEventDead(timer.getTime(), uniti) - --unitj:Destroy() - --self:_CreateEventDead(timer.getTime(), unitj) + -- Trigger immidiate destuction of unit. + self:T(RAT.id..string.format("Unit %s is on top of unit %s. Distance %.2f m.", namei, namej,_dist)) return true end end @@ -4807,7 +4908,6 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - --- Modifies the template of the group to be spawned. -- In particular, the waypoints of the group's flight plan are copied into the spawn template. -- This allows to spawn at airports and also land at other airports, i.e. circumventing the DCS "landing bug". @@ -4815,22 +4915,52 @@ end -- @param #table waypoints The waypoints of the AI flight plan. -- @param #string livery (Optional) Livery of the aircraft. All members of a flight will get the same livery. -- @param Core.Point#COORDINATE spawnplace (Optional) Place where spawning should happen. If not present, first waypoint is taken. -function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace) +-- @param Wrapper.Airbase#AIRBASE departure Departure airbase or zone. +-- @param #number takeoff Takeoff type. +function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, takeoff) self:F2({waypoints=waypoints, livery=livery, spawnplace=spawnplace}) -- The 3D vector of the first waypoint, i.e. where we actually spawn the template group. - local PointVec3 = {x=waypoints[1].x, y=waypoints[1].alt, z=waypoints[1].y} + local PointVec3 = COORDINATE:New(waypoints[1].x, waypoints[1].alt, waypoints[1].y) if spawnplace then - PointVec3 = spawnplace:GetVec3() - self:T({spawnplace=PointVec3}) + PointVec3 = COORDINATE:NewFromCoordinate(spawnplace) end + -- Check if we spawn on ground. + local spawnonground=takeoff==RAT.wp.cold or takeoff==RAT.wp.hot or takeoff==RAT.wp.runway + + -- Check where we actually spawn if we spawn on ground. + local spawnonship=false + local spawnonfarp=false + local spawnonrunway=false + local spawnonairport=false + if spawnonground then + + -- Spawning at a ship + spawnonship=departure:GetCategory()==1 -- Catetory 1 are ships. + + -- Spawning at a FARP. Catetory 4 are airbases so we need to check that type is FARP as well. + spawnonfarp=departure:GetCategory()==4 and departure:GetTypeName()=="FARP" + + -- Spawning at an airport. + spawnonairport=departure:GetCategory()==4 and departure:GetTypeName()~="FARP" + + -- Spawning on the runway. + spawnonrunway=takeoff==RAT.wp.runway + end + + local automatic=false + if automatic and spawnonground then + PointVec3=PointVec3:GetClosestParkingSpot(true, departure) + end + -- Heading from first to seconds waypoints to align units in case of air start. local course = self:_Course(waypoints[1], waypoints[2]) local heading = self:_Heading(course) if self:_GetSpawnIndex(self.SpawnIndex+1) then + -- Get template from group. local SpawnTemplate = self.SpawnGroups[self.SpawnIndex].SpawnTemplate if SpawnTemplate then @@ -4841,27 +4971,139 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace) -- This is used in the SPAWN:SpawnWithIndex() function. Some values are overwritten there! self.SpawnUnControlled=true end + + -- Array with parking spots coordinates. + local parkingspots={} + local parkingindex={} + local spots + + -- Spawn happens on ground, i.e. at an airbase, a FARP or a ship. + if spawnonground then + -- Number of free parking spots. + local nfree=0 + + -- Set terminal type. + local termtype=self.termtype + if spawnonrunway then + termtype=AIRBASE.TerminalType.Runway + end + + -- Number of free parking spots at the airbase. + nfree=departure:GetFreeParkingSpotsNumber(termtype, spawnonship or spawnonfarp or spawnonrunway) + spots=departure:GetFreeParkingSpotsTable(termtype, spawnonship or spawnonfarp or spawnonrunway) + + -- Get parking data. + local parkingdata=departure:GetParkingSpotsTable(termtype) + + self:E(RAT.id..string.format("Parking at %s, terminal type %s:", departure:GetName(), tostring(termtype))) + for _,_spot in pairs(parkingdata) do + self:E(RAT.id..string.format("%s, Termin Index = %3d, Term Type = %03d, Free = %5s, TOAC = %5s, Term ID0 = %3d, Dist2Rwy = %4d", + departure:GetName(), _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy)) + end + self:E(RAT.id..string.format("%s at %s: free parking spots = %d - number of units = %d", self.alias, departure:GetName(), nfree, #SpawnTemplate.units)) + + + -- Put parking spots in table. These spots are only used if + if nfree >= #SpawnTemplate.units or (spawnonrunway and nfree>0) then + + for i=1,#SpawnTemplate.units do + table.insert(parkingspots, spots[i].Coordinate) + table.insert(parkingindex, spots[i].TerminalID) + end + + + if spawnonrunway then + --PointVec3=spots[1] + end + + else + self:E(RAT.id..string.format("RAT group %s has no parking spots at %s ==> air start!", self.alias, departure:GetName())) + + -- Not enough parking spots at the airport ==> Spawn in air. + spawnonground=false + spawnonship=false + spawnonfarp=false + spawnonrunway=false + + -- Set waypoint type/action to turning point. + waypoints[1].type = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][1] -- type = Turning Point + waypoints[1].action = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][2] -- action = Turning Point + + -- Adjust altitude to be 500-1000 m above the airbase. + PointVec3.y=PointVec3:GetLandHeight()+math.random(500,1000) + + end + + end + -- Translate the position of the Group Template to the Vec3. for UnitID = 1, #SpawnTemplate.units do - self:T('Before Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) + self:T2('Before Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) - -- Tranlate position. + -- Template of the current unit. local UnitTemplate = SpawnTemplate.units[UnitID] + + -- Tranlate position and preserve the relative position/formation of all aircraft. local SX = UnitTemplate.x local SY = UnitTemplate.y local BX = SpawnTemplate.route.points[1].x local BY = SpawnTemplate.route.points[1].y local TX = PointVec3.x + (SX-BX) local TY = PointVec3.z + (SY-BY) - SpawnTemplate.units[UnitID].x = TX - SpawnTemplate.units[UnitID].y = TY - SpawnTemplate.units[UnitID].alt = PointVec3.y + + if spawnonground then + + -- Shíps and FARPS seem to have a build in queue. + if spawnonship or spawnonfarp or spawnonrunway or automatic then + + self:T(RAT.id..string.format("RAT group %s spawning at farp, ship or runway %s.", self.alias, departure:GetName())) + + -- Spawn on ship. We take only the position of the ship. + SpawnTemplate.units[UnitID].x = PointVec3.x --TX + SpawnTemplate.units[UnitID].y = PointVec3.z --TY + SpawnTemplate.units[UnitID].alt = PointVec3.y + + else + + self:T(RAT.id..string.format("RAT group %s spawning at airbase %s on parking spot id %d", self.alias, departure:GetName(), parkingindex[UnitID])) + + -- Get coordinates of parking spot. + SpawnTemplate.units[UnitID].x = parkingspots[UnitID].x + SpawnTemplate.units[UnitID].y = parkingspots[UnitID].z + SpawnTemplate.units[UnitID].alt = parkingspots[UnitID].y + + end + + else - if self.Debug then - local unitspawn=COORDINATE:New(TX,PointVec3.y,TY) - unitspawn:MarkToAll(string.format("Spawnplace unit #%d", UnitID)) + self:T(RAT.id..string.format("RAT group %s spawning in air at %s.", self.alias, departure:GetName())) + + -- Spawn in air as requested initially. Original template orientation is perserved, altitude is already correctly set. + SpawnTemplate.units[UnitID].x = TX + SpawnTemplate.units[UnitID].y = TY + SpawnTemplate.units[UnitID].alt = PointVec3.y + end + + -- Place marker at spawn position. + if self.Debug then + local unitspawn=COORDINATE:New(SpawnTemplate.units[UnitID].x, SpawnTemplate.units[UnitID].alt, SpawnTemplate.units[UnitID].y) + unitspawn:MarkToAll(string.format("RAT %s Spawnplace unit #%d", self.alias, UnitID)) + end + + -- Parking spot id. + UnitTemplate.parking = nil + UnitTemplate.parking_id = nil + if parkingindex[UnitID] and not automatic then + UnitTemplate.parking = parkingindex[UnitID] + end + + self:T2(RAT.id..string.format("RAT group %s unit number %d: Parking = %s",self.alias, UnitID, tostring(UnitTemplate.parking))) + self:T2(RAT.id..string.format("RAT group %s unit number %d: Parking ID = %s",self.alias, UnitID, tostring(UnitTemplate.parking_id))) + + + -- Set initial heading. SpawnTemplate.units[UnitID].heading = heading SpawnTemplate.units[UnitID].psi = -heading @@ -4889,15 +5131,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace) SpawnTemplate.CountryID=self.country end - -- Parking spot. - UnitTemplate.parking = nil - UnitTemplate.parking_id = self.parking_id - --self:T(RAT.id.."Parking ID "..tostring(self.parking_id)) - - -- Initial altitude - UnitTemplate.alt=PointVec3.y - - self:T('After Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) + self:T2('After Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) end -- Copy waypoints into spawntemplate. By this we avoid the nasty DCS "landing bug" :) diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index d31779479..ae5b21368 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -106,7 +106,7 @@ AIRBASE.Caucasus = { ["Mozdok"] = "Mozdok", ["Beslan"] = "Beslan", } - +-- --- @field Nevada -- -- These are all airbases of Nevada: @@ -150,7 +150,7 @@ AIRBASE.Nevada = { ["Tonopah_Airport"] = "Tonopah Airport", ["Tonopah_Test_Range_Airfield"] = "Tonopah Test Range Airfield", } - +-- --- @field Normandy -- -- These are all airbases of Normandy: @@ -219,6 +219,56 @@ AIRBASE.Normandy = { ["Tangmere"] = "Tangmere", ["Ford"] = "Ford", } +-- +--- @field PersianGulf +-- +-- These are all airbases of the Persion Gulf Map: +-- +-- * AIRBASE.PersianGulf.Fujairah_Intl +-- * AIRBASE.PersianGulf.Qeshm_Island +-- * AIRBASE.PersianGulf.Sir_Abu_Nuayr +-- * AIRBASE.PersianGulf.Abu_Musa_Island_Airport +-- * AIRBASE.PersianGulf.Bandar_Abbas_Intl +-- * AIRBASE.PersianGulf.Bandar_Lengeh +-- * AIRBASE.PersianGulf.Tunb_Island_AFB +-- * AIRBASE.PersianGulf.Havadarya +-- * AIRBASE.PersianGulf.Lar_Airbase +-- * AIRBASE.PersianGulf.Sirri_Island +-- * AIRBASE.PersianGulf.Tunb_Kochak +-- * AIRBASE.PersianGulf.Al_Dhafra_AB +-- * AIRBASE.PersianGulf.Dubai_Intl +-- * AIRBASE.PersianGulf.Al_Maktoum_Intl +-- * AIRBASE.PersianGulf.Khasab +-- * AIRBASE.PersianGulf.Al_Minhad_AB + -- * AIRBASE.PersianGulf.Sharjah_Intl +AIRBASE.PersianGulf = { + ["Fujairah_Intl"] = "Fujairah Intl", + ["Qeshm_Island"] = "Qeshm Island", + ["Sir_Abu_Nuayr"] = "Sir Abu Nuayr", + ["Abu_Musa_Island_Airport"] = "Abu Musa Island Airport", + ["Bandar_Abbas_Intl"] = "Bandar Abbas Intl", + ["Bandar_Lengeh"] = "Bandar Lengeh", + ["Tunb_Island_AFB"] = "Tunb Island AFB", + ["Havadarya"] = "Havadarya", + ["Lar_Airbase"] = "Lar Airbase", + ["Sirri_Island"] = "Sirri Island", + ["Tunb_Kochak"] = "Tunb Kochak", + ["Al_Dhafra_AB"] = "Al Dhafra AB", + ["Dubai_Intl"] = "Dubai Intl", + ["Al_Maktoum_Intl"] = "Al Maktoum Intl", + ["Khasab"] = "Khasab", + ["Al_Minhad_AB"] = "Al Minhad AB", + ["Sharjah_Intl"] = "Sharjah Intl", + } +-- +--- @field Terminal Types of parking spots. +AIRBASE.TerminalType = { + Runway=16, + Helicopter=40, + HardenedShelter=68, + OpenOrShelter=72, + OpenAir=104, +} -- Registration. @@ -257,6 +307,9 @@ function AIRBASE:FindByName( AirbaseName ) return AirbaseFound end +--- Get the DCS object of an airbase +-- @param #AIRBASE self +-- @return DCS#Airbase DCS airbase object. function AIRBASE:GetDCSObject() local DCSAirbase = Airbase.getByName( self.AirbaseName ) @@ -274,5 +327,231 @@ function AIRBASE:GetZone() return self.AirbaseZone end +--- Get all airbases of the current map. This includes ships and FARPS. +-- @param #AIRBASE self +-- @param DCS#Coalition coalition (Optional) Return only airbases belonging to the specified coalition. By default, all airbases of the map are returned. +-- @return #table Table containing all airbase objects of the current map. +function AIRBASE:GetAllAirbases(coalition) + + local airbases={} + for _,airbase in pairs(_DATABASE.AIRBASES) do + if (coalition~=nil and self:GetCoalition()==coalition) or coalition==nil then + table.insert(airbases, airbase) + end + end + + return airbases +end + +--- Returns a table of parking data for a given airbase. If the optional parameter *available* is true only available parking will be returned, otherwise all parking at the base is returned. Term types have the following enumerated values: +-- +-- * 16 : Valid spawn points on runway +-- * 40 : Helicopter only spawn +-- * 68 : Hardened Air Shelter +-- * 72 : Open/Shelter air airplane only +-- * 104: Open air spawn +-- +-- Note that only Caucuses will return 68 as it is the only map currently with hardened air shelters. +-- 104 are also generally larger, but does not guarantee a large aircraft like the B-52 or a C-130 are capable of spawning there. +-- +-- Table entries: +-- +-- * Term_index is the id for the parking +-- * vTerminal pos is its vec3 position in the world +-- * fDistToRW is the distance to the take-off position for the active runway from the parking. +-- +-- @param #AIRBASE self +-- @param #boolean available If true, only available parking spots will be returned. +-- @return #table Table with parking data. See https://wiki.hoggitworld.com/view/DCS_func_getParking +function AIRBASE:GetParkingData(available) + + -- Get DCS airbase object. + local DCSAirbase=self:GetDCSObject() + + -- Get parking data. + local parkingdata=nil + if DCSAirbase then + parkingdata=DCSAirbase:getParking(available) + end + + return parkingdata +end + +--- Get number parking spots at an airbase. Optionally, for a specific terminal type. Spots on runway are exculded if not explicitly requested by terminal type +-- @param #AIRBASE self +-- @param #number termtype Terminal type. +-- @return #number Number of free parking spots at this airbase. +function AIRBASE:GetParkingSpotsNumber(termtype) + + -- Get free parking spots data. + local parkingdata=self:GetParkingData(false) + + local nfree=0 + for _,parkingspot in pairs(parkingdata) do + -- Spots on runway are not counted unless explicitly requested. + if (termtype~=nil and parkingspot.Term_Type==termtype) or (termtype==nil and parkingspot.Term_Type~=AIRBASE.TerminalType.Runway) then + nfree=nfree+1 + end + end + + return nfree +end + +--- Get number of free parking spots at an airbase. +-- @param #AIRBASE self +-- @param #number termtype Terminal type. +-- @param #boolean allowTOAC If true, spots are considered free even though TO_AC is true. Default is off which is saver to avoid spawning aircraft on top of each other. Option might be enabled for FARPS and ships. +-- @return #number Number of free parking spots at this airbase. +function AIRBASE:GetFreeParkingSpotsNumber(termtype, allowTOAC) + + -- Get free parking spots data. + local parkingdata=self:GetParkingData(true) + + local nfree=0 + for _,parkingspot in pairs(parkingdata) do + -- Spots on runway are not counted unless explicitly requested. + if (termtype~=nil and parkingspot.Term_Type==termtype) or (termtype==nil and parkingspot.Term_Type~=AIRBASE.TerminalType.Runway) then + if (allowTOAC and allowTOAC==true) or parkingspot.TO_AC==false then + nfree=nfree+1 + end + end + end + + return nfree +end + +--- Get the coordinates of free parking spots at an airbase. +-- @param #AIRBASE self +-- @param #number termtype Terminal type. +-- @param #boolean allowTOAC If true, spots are considered free even though TO_AC is true. Default is off which is saver to avoid spawning aircraft on top of each other. Option might be enabled for FARPS and ships. +-- @return #table Table of coordinates of the free parking spots. +function AIRBASE:GetFreeParkingSpotsCoordinates(termtype, allowTOAC) + + -- Get free parking spots data. + local parkingdata=self:GetParkingData(true) + + -- Put coordinates of free spots into table. + local spots={} + for _,parkingspot in pairs(parkingdata) do + -- Coordinates on runway are not returned unless explicitly requested. + if (termtype and parkingspot.Term_Type==termtype) or (termtype==nil and parkingspot.Term_Type~=AIRBASE.TerminalType.Runway) then + if (allowTOAC and allowTOAC==true) or parkingspot.TO_AC==false then + table.insert(spots, COORDINATE:NewFromVec3(parkingspot.vTerminalPos)) + end + end + end + + return spots +end + +--- Get the coordinates of all parking spots at an airbase. Optionally only those of a specific terminal type. Spots on runways are excluded if not explicitly requested by terminal type. +-- @param #AIRBASE self +-- @param #number termtype (Optional) Terminal type. Default all. +-- @return #table Table of coordinates of parking spots. +function AIRBASE:GetParkingSpotsCoordinates(termtype) + + -- Get all parking spots data. + local parkingdata=self:GetParkingData(false) + + -- Put coordinates of free spots into table. + local spots={} + for _,parkingspot in pairs(parkingdata) do + -- Coordinates on runway are not returned unless explicitly requested. + if (termtype and parkingspot.Term_Type==termtype) or (termtype==nil and parkingspot.Term_Type~=AIRBASE.TerminalType.Runway) then + local _coord=COORDINATE:NewFromVec3(parkingspot.vTerminalPos) + local gotunits,gotstatics,gotscenery,_,statics,_=_coord:ScanObjects(.5) + env.info(string.format("FF scan: terminal index %03d, type = %03d, gotunits=%s, gotstatics=%s, gotscenery=%s", parkingspot.Term_Index+1, parkingspot.Term_Type, tostring(gotunits), tostring(gotstatics), tostring(gotscenery))) + if gotstatics then + for _,static in pairs(statics) do + env.info(string.format("FF Static name= %s", tostring(static:getName()))) + end + end + table.insert(spots, _coord) + end + end + + return spots +end +--- Get a table containing the coordinates, terminal index and terminal type of free parking spots at an airbase. +-- @param #AIRBASE self +-- @param #number termtype Terminal type. +-- @return #table Table free parking spots. Table has the elements ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy". +function AIRBASE:GetParkingSpotsTable(termtype) + + -- Get parking data of all spots (free or occupied) + local parkingdata=self:GetParkingData(false) + -- Get parking data of all free spots. + local parkingfree=self:GetParkingData(true) + + -- Function to ckeck if any parking spot is free. + local function _isfree(_tocheck) + for _,_spot in pairs(parkingfree) do + if _spot.Term_Index==_tocheck.Term_Index then + return true + end + end + return false + end + + -- Put coordinates of free spots into table. + local freespots={} + for _,_spot in pairs(parkingdata) do + if (termtype and _spot.Term_Type==termtype) or (termtype==nil and _spot.Term_Type~=AIRBASE.TerminalType.Runway) then + local _free=_isfree(_spot) + local _coord=COORDINATE:NewFromVec3(_spot.vTerminalPos) + table.insert(freespots, {Coordinate=_coord, TerminalID=_spot.Term_Index, TerminalType=_spot.Term_Type, TOAC=_spot.TO_AC, Free=_free, TerminalID0=_spot.Term_Index_0, DistToRwy=_spot.fDistToRW}) + end + end + + return freespots +end + +--- Get a table containing the coordinates, terminal index and terminal type of free parking spots at an airbase. +-- @param #AIRBASE self +-- @param #number termtype Terminal type. +-- @param #boolean allowTOAC If true, spots are considered free even though TO_AC is true. Default is off which is saver to avoid spawning aircraft on top of each other. Option might be enabled for FARPS and ships. +-- @return #table Table free parking spots. Table has the elements ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy". +function AIRBASE:GetFreeParkingSpotsTable(termtype, allowTOAC) + + -- Get parking data of all free spots. + local parkingfree=self:GetParkingData(true) + + -- Put coordinates of free spots into table. + local freespots={} + for _,_spot in pairs(parkingfree) do + if (termtype and _spot.Term_Type==termtype) or (termtype==nil and _spot.Term_Type~=AIRBASE.TerminalType.Runway) then + if (allowTOAC and allowTOAC==true) or _spot.TO_AC==false then + local _coord=COORDINATE:NewFromVec3(_spot.vTerminalPos) + table.insert(freespots, {Coordinate=_coord, TerminalID=_spot.Term_Index, TerminalType=_spot.Term_Type, TOAC=_spot.TO_AC, Free=true, TerminalID0=_spot.Term_Index_0, DistToRwy=_spot.fDistToRW}) + end + end + end + + return freespots +end + +--- Place markers of parking spots on the F10 map. +-- @param #AIRBASE self +-- @param #number termtype Terminal type for which marks should be placed. +function AIRBASE:MarkParkingSpots(termtype) + + local parkingdata=self:GetParkingSpotsTable(termtype) + + local airbasename=self:GetName() + self:E(string.format("Parking spots at %s for termial type %s:", airbasename, tostring(termtype))) + + for _,_spot in pairs(parkingdata) do + + -- Mark text. + local _text=string.format("%s, Term Index = %3d, Term Type = %03d, Free = %5s, TOAC = %5s, Term ID0 = %3d, Dist2Rwy = %.1f", + airbasename, _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy) + + -- Create mark on the F10 map. + _spot.Coordinate:MarkToAll(_text) + + -- Info to DCS.log file. + self:E(_text) + end +end \ No newline at end of file From 08ea3cd219e1b0998b7410726c8a2264b1fd55af Mon Sep 17 00:00:00 2001 From: funkyfranky <> Date: Tue, 26 Jun 2018 23:53:21 +0200 Subject: [PATCH 189/420] New sophisticated FindParkingSpot routine. AIRBASE: - Added new find parking spot routine. Taking into accound dimension of AC etc. - Added more termial type combinations. COORDINATE: - minor changes in scanobjects() function. RAT and SPAWN - improved modifyspawntemplate() function ==> new find routine, helos --- Moose Development/Moose/Core/Point.lua | 15 +- Moose Development/Moose/Core/Spawn.lua | 126 +++++--- Moose Development/Moose/Functional/RAT.lua | 104 +++--- Moose Development/Moose/Wrapper/Airbase.lua | 340 ++++++++++++++++---- 4 files changed, 427 insertions(+), 158 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 4cae6049b..e99cd3e7e 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -302,7 +302,7 @@ do -- COORDINATE -- @return Static objects found. -- @return Scenery objects found. function COORDINATE:ScanObjects(radius) - env.info(string.format("FF Scanning in radius %.1f m.", radius)) + self:T(string.format("Scanning in radius %.1f m.", radius)) local SphereSearch = { id = world.VolumeType.SPHERE, @@ -328,7 +328,8 @@ do -- COORDINATE local ObjectCategory = ZoneObject:getCategory() -- Check for unit or static objects - if (ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive()) then + --if (ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive()) then + if (ObjectCategory == Object.Category.UNIT and ZoneObject:isExist()) then table.insert(Units, ZoneObject) gotunits=true @@ -354,14 +355,18 @@ do -- COORDINATE world.searchObjects({Object.Category.UNIT, Object.Category.STATIC, Object.Category.SCENERY}, SphereSearch, EvaluateZone) for _,unit in pairs(Units) do - env.info(string.format("FF found unit %s", unit:getName())) + self:T(string.format("Scan found unit %s", unit:getName())) end for _,static in pairs(Statics) do - env.info(string.format("FF found unit %s", static:getName())) + self:T(string.format("Scan found static %s", static:getName())) + end + for _,scenery in pairs(Scenery) do + self:T(string.format("Scan found scenery %s", scenery:getName())) end - return gotunits, gotstatics, gotscenery, Units, Statics, Scenery + return gotunits, gotstatics, gotscenery, Units, Statics, Scenery end + --- Calculate the distance from a reference @{#COORDINATE}. -- @param #COORDINATE self -- @param #COORDINATE PointVec2Reference The reference @{#COORDINATE}. diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 5db251ae5..3258feb80 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1219,8 +1219,7 @@ end -- @param #SPAWN.Takeoff Takeoff (optional) The location and takeoff method. Default is Hot. -- @param #number TakeoffAltitude (optional) The altitude above the ground. -- @param #number TerminalType (optional) The terminal type the aircraft should be spawned at. --- @return Wrapper.Group#GROUP that was spawned. --- @return #nil Nothing was spawned. +-- @return Wrapper.Group#GROUP that was spawned or nil when nothing was spawned. -- @usage -- Spawn_Plane = SPAWN:New( "Plane" ) -- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Cold ) @@ -1237,9 +1236,11 @@ end -- Spawn_Heli:SpawnAtAirbase( AIRBASE:FindByName( "FARP Air" ), SPAWN.Takeoff.Air ) -- -- Spawn_Heli:SpawnAtAirbase( AIRBASE:FindByName( "Carrier" ), SPAWN.Takeoff.Cold ) +-- +-- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Cold, nil, AIRBASE.TerminalType.OpenBig ) -- function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalType ) -- R2.2, R2.4 - self:F( { self.SpawnTemplatePrefix, SpawnAirbase, Takeoff, TakeoffAltitude } ) + self:F( { self.SpawnTemplatePrefix, SpawnAirbase, Takeoff, TakeoffAltitude, TerminalType } ) -- Get position of airbase. local PointVec3 = SpawnAirbase:GetCoordinate() @@ -1257,6 +1258,10 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT -- Debug output self:T( { "Current point of ", self.SpawnTemplatePrefix, SpawnAirbase } ) + + -- Template group and unit. + local TemplateGroup = GROUP:FindByName(self.SpawnTemplatePrefix) + local ishelo=TemplateGroup:GetUnit(1):HasAttribute("Helicopters") -- First waypoint of the group. local SpawnPoint = SpawnTemplate.route.points[1] @@ -1282,36 +1287,31 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT SpawnPoint.airdromeId = AirbaseID end + -- Set waypoint type/action. SpawnPoint.alt = 0 SpawnPoint.type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type SpawnPoint.action = GROUPTEMPLATE.Takeoff[Takeoff][2] -- action - -- Check if we spawn on ground. local spawnonground=not (Takeoff==SPAWN.Takeoff.Air) - self:E({spawnonground=spawnonground, takeoff=Takeoff, toair=Takeoff==SPAWN.Takeoff.Air}) + self:T({spawnonground=spawnonground, TOtype=Takeoff, TOair=Takeoff==SPAWN.Takeoff.Air}) -- Check where we actually spawn if we spawn on ground. local spawnonship=false local spawnonfarp=false local spawnonrunway=false - local spawnonairport=false - if spawnonground then - - -- Spawning at a ship - spawnonship=SpawnAirbase:GetCategory()==1 -- Catetory 1 are ships. - - -- Spawning at a FARP. Catetory 4 are airbases so we need to check that type is FARP as well. - spawnonfarp=SpawnAirbase:GetCategory()==4 and SpawnAirbase:GetTypeName()=="FARP" - - -- Spawning at an airport. - spawnonairport=SpawnAirbase:GetCategory()==4 and SpawnAirbase:GetTypeName()~="FARP" - - -- Spawning on the runway. + local spawnonairport=false + if spawnonground then + if AirbaseCategory == Airbase.Category.SHIP then + spawnonship=true + elseif AirbaseCategory == Airbase.Category.HELIPAD then + spawnonfarp=true + elseif AirbaseCategory == Airbase.Category.AIRDROME then + spawnonairport=true + end spawnonrunway=Takeoff==SPAWN.Takeoff.Runway end - - + -- Array with parking spots coordinates. local parkingspots={} local parkingindex={} @@ -1330,20 +1330,39 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT end -- Number of free parking spots at the airbase. - nfree=SpawnAirbase:GetFreeParkingSpotsNumber(termtype, spawnonship or spawnonfarp or spawnonrunway) - spots=SpawnAirbase:GetFreeParkingSpotsTable(termtype, spawnonship or spawnonfarp or spawnonrunway) + if spawnonship or spawnonfarp or spawnonrunway then + -- These places work procedural and have some kind of build in queue ==> Less effort. + nfree=SpawnAirbase:GetFreeParkingSpotsNumber(termtype, spawnonship or spawnonfarp or spawnonrunway) + spots=SpawnAirbase:GetFreeParkingSpotsTable(termtype, spawnonship or spawnonfarp or spawnonrunway) + else + if ishelo and termtype==nil then + -- Helo is spawned. + -- Try helo spots first. + spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly) + nfree=#spots + if nfree<#SpawnTemplate.units then + -- Not enough helo ports. Let's try all terminal types. + spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterUsable) + nfree=#spots + end + else + -- Fixed wing aircraft is spawned. + --TODO: Add some default cases for transport, bombers etc. if no explicit terminal type is provided. + spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype) + nfree=#spots + end + end -- Get parking data. local parkingdata=SpawnAirbase:GetParkingSpotsTable(termtype) - - self:E(string.format("Parking at %s, terminal type %s:", SpawnAirbase:GetName(), tostring(termtype))) + self:T(string.format("Parking at %s, terminal type %s:", SpawnAirbase:GetName(), tostring(termtype))) for _,_spot in pairs(parkingdata) do - self:E(string.format("%s, Termin Index = %3d, Term Type = %03d, Free = %5s, TOAC = %5s, Term ID0 = %3d, Dist2Rwy = %4d", + self:T(string.format("%s, Termin Index = %3d, Term Type = %03d, Free = %5s, TOAC = %5s, Term ID0 = %3d, Dist2Rwy = %4d", SpawnAirbase:GetName(), _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy)) end - self:E(string.format("%s at %s: free parking spots = %d - number of units = %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), nfree, #SpawnTemplate.units)) + self:T(string.format("%s at %s: free parking spots = %d - number of units = %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), nfree, #SpawnTemplate.units)) - -- Put parking spots in table. These spots are only used if + -- Put parking spots in table. These spots are only used if spawing at an airbase. if nfree >= #SpawnTemplate.units or (spawnonrunway and nfree>0) then for i=1,#SpawnTemplate.units do @@ -1352,22 +1371,34 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT end else - self:E(string.format("Group %s has no parking spots at %s ==> air start!", self.SpawnTemplatePrefix, SpawnAirbase:GetName())) - - -- Not enough parking spots at the airport ==> Spawn in air. - spawnonground=false - spawnonship=false - spawnonfarp=false - spawnonrunway=false + if not self.SpawnUnControlled then + self:E(string.format("WARNING: Group %s has no parking spots at %s ==> air start!", self.SpawnTemplatePrefix, SpawnAirbase:GetName())) - -- Set waypoint type/action to turning point. - SpawnPoint.type = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][1] -- type = Turning Point - SpawnPoint.action = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][2] -- action = Turning Point - - -- Adjust altitude to be 500-1000 m above the airbase. - PointVec3.y=PointVec3:GetLandHeight()+math.random(200,1200) - - Takeoff=GROUP.Takeoff.Air + -- Not enough parking spots at the airport ==> Spawn in air. + spawnonground=false + spawnonship=false + spawnonfarp=false + spawnonrunway=false + + -- Set waypoint type/action to turning point. + SpawnPoint.type = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][1] -- type = Turning Point + SpawnPoint.action = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][2] -- action = Turning Point + + -- Adjust altitude to be 500-1000 m above the airbase. + PointVec3.x=PointVec3.x+math.random(-500,500) + PointVec3.z=PointVec3.z+math.random(-500,500) + if ishelo then + PointVec3.y=PointVec3:GetLandHeight()+math.random(200,1200) + else + -- Randomize position so that multiple AC wont be spawned on top even in air. + PointVec3.y=PointVec3:GetLandHeight()+math.random(500,5000) + end + + Takeoff=GROUP.Takeoff.Air + else + self:E(string.format("WARNING: Group %s has no parking spots at %s ==> Uncontrolled spawning ==> No spawn!", self.SpawnTemplatePrefix, SpawnAirbase:GetName())) + return nil + end end end @@ -1428,16 +1459,9 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT UnitTemplate.parking = parkingindex[UnitID] end - - -- Place marker at spawn position. - --if self.Debug then - local unitspawn=COORDINATE:New(SpawnTemplate.units[UnitID].x, SpawnTemplate.units[UnitID].alt, SpawnTemplate.units[UnitID].y) - unitspawn:MarkToAll(string.format("%s Spawnplace unit #%d, terminal %s", self.SpawnTemplatePrefix, UnitID, tostring(UnitTemplate.parking))) - --end - + -- Debug output. self:T2(string.format("Group %s unit number %d: Parking = %s",self.SpawnTemplatePrefix, UnitID, tostring(UnitTemplate.parking))) - self:T2(string.format("Group %s unit number %d: Parking ID = %s",self.SpawnTemplatePrefix, UnitID, tostring(UnitTemplate.parking_id))) - + self:T2(string.format("Group %s unit number %d: Parking ID = %s",self.SpawnTemplatePrefix, UnitID, tostring(UnitTemplate.parking_id))) self:T2('After Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) end diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 7653e9261..16381f87e 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -1831,7 +1831,10 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live end -- Modify the spawn template to follow the flight plan. - self:_ModifySpawnTemplate(waypoints, livery, _spawnpos, departure, takeoff) + local successful=self:_ModifySpawnTemplate(waypoints, livery, _spawnpos, departure, takeoff) + if not successful then + return nil + end -- Actually spawn the group. local group=self:SpawnWithIndex(self.SpawnIndex) -- Wrapper.Group#GROUP @@ -4925,30 +4928,30 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take if spawnplace then PointVec3 = COORDINATE:NewFromCoordinate(spawnplace) end + + -- Template group and unit. + local TemplateGroup = GROUP:FindByName(self.SpawnTemplatePrefix) -- Check if we spawn on ground. local spawnonground=takeoff==RAT.wp.cold or takeoff==RAT.wp.hot or takeoff==RAT.wp.runway - + -- Check where we actually spawn if we spawn on ground. local spawnonship=false local spawnonfarp=false local spawnonrunway=false - local spawnonairport=false + local spawnonairport=false if spawnonground then - - -- Spawning at a ship - spawnonship=departure:GetCategory()==1 -- Catetory 1 are ships. - - -- Spawning at a FARP. Catetory 4 are airbases so we need to check that type is FARP as well. - spawnonfarp=departure:GetCategory()==4 and departure:GetTypeName()=="FARP" - - -- Spawning at an airport. - spawnonairport=departure:GetCategory()==4 and departure:GetTypeName()~="FARP" - - -- Spawning on the runway. + local AirbaseCategory = departure:GetDesc().category + if AirbaseCategory == Airbase.Category.SHIP then + spawnonship=true + elseif AirbaseCategory == Airbase.Category.HELIPAD then + spawnonfarp=true + elseif AirbaseCategory == Airbase.Category.AIRDROME then + spawnonairport=true + end spawnonrunway=takeoff==RAT.wp.runway end - + local automatic=false if automatic and spawnonground then PointVec3=PointVec3:GetClosestParkingSpot(true, departure) @@ -4990,19 +4993,37 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take end -- Number of free parking spots at the airbase. - nfree=departure:GetFreeParkingSpotsNumber(termtype, spawnonship or spawnonfarp or spawnonrunway) - spots=departure:GetFreeParkingSpotsTable(termtype, spawnonship or spawnonfarp or spawnonrunway) - + if spawnonship or spawnonfarp or spawnonrunway then + -- These places work procedural and have some kind of build in queue ==> Less effort. + nfree=departure:GetFreeParkingSpotsNumber(termtype, spawnonship or spawnonfarp or spawnonrunway) + spots=departure:GetFreeParkingSpotsTable(termtype, spawnonship or spawnonfarp or spawnonrunway) + else + if self.category==RAT.cat.heli and termtype==nil then + -- Helo is spawned. + -- Try helo spots first. + spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly) + nfree=#spots + if nfree<#SpawnTemplate.units then + -- Not enough helo ports. Let's try all terminal types. + spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterUsable) + nfree=#spots + end + else + -- Fixed wing aircraft is spawned. + --TODO: Add some default cases for transport, bombers etc. if no explicit terminal type is provided. + spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, termtype) + nfree=#spots + end + end + -- Get parking data. local parkingdata=departure:GetParkingSpotsTable(termtype) - - self:E(RAT.id..string.format("Parking at %s, terminal type %s:", departure:GetName(), tostring(termtype))) + self:T(RAT.id..string.format("Parking at %s, terminal type %s:", departure:GetName(), tostring(termtype))) for _,_spot in pairs(parkingdata) do - self:E(RAT.id..string.format("%s, Termin Index = %3d, Term Type = %03d, Free = %5s, TOAC = %5s, Term ID0 = %3d, Dist2Rwy = %4d", + self:T(RAT.id..string.format("%s, Termin Index = %3d, Term Type = %03d, Free = %5s, TOAC = %5s, Term ID0 = %3d, Dist2Rwy = %4d", departure:GetName(), _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy)) end - self:E(RAT.id..string.format("%s at %s: free parking spots = %d - number of units = %d", self.alias, departure:GetName(), nfree, #SpawnTemplate.units)) - + self:T(RAT.id..string.format("%s at %s: free parking spots = %d - number of units = %d", self.alias, departure:GetName(), nfree, #SpawnTemplate.units)) -- Put parking spots in table. These spots are only used if if nfree >= #SpawnTemplate.units or (spawnonrunway and nfree>0) then @@ -5011,28 +5032,31 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take table.insert(parkingspots, spots[i].Coordinate) table.insert(parkingindex, spots[i].TerminalID) end - - + if spawnonrunway then --PointVec3=spots[1] end else - self:E(RAT.id..string.format("RAT group %s has no parking spots at %s ==> air start!", self.alias, departure:GetName())) - - -- Not enough parking spots at the airport ==> Spawn in air. - spawnonground=false - spawnonship=false - spawnonfarp=false - spawnonrunway=false - - -- Set waypoint type/action to turning point. - waypoints[1].type = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][1] -- type = Turning Point - waypoints[1].action = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][2] -- action = Turning Point - - -- Adjust altitude to be 500-1000 m above the airbase. - PointVec3.y=PointVec3:GetLandHeight()+math.random(500,1000) + if self.respawn_inair or self.uncontrolled then + self:E(RAT.id..string.format("WARNING: RAT group %s has no parking spots at %s ==> air start!", self.alias, departure:GetName())) + -- Not enough parking spots at the airport ==> Spawn in air. + spawnonground=false + spawnonship=false + spawnonfarp=false + spawnonrunway=false + + -- Set waypoint type/action to turning point. + waypoints[1].type = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][1] -- type = Turning Point + waypoints[1].action = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][2] -- action = Turning Point + + -- Adjust altitude to be 500-1000 m above the airbase. + PointVec3.y=PointVec3:GetLandHeight()+math.random(500,1000) + else + self:E(RAT.id..string.format("WARNING: RAT group %s has no parking spots at %s. Air start deactivated or uncontrolled AC!", self.alias, departure:GetName())) + return nil + end end end @@ -5160,6 +5184,8 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take self:T(SpawnTemplate) end end + + return true end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index ae5b21368..2b02fbd5b 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -106,10 +106,8 @@ AIRBASE.Caucasus = { ["Mozdok"] = "Mozdok", ["Beslan"] = "Beslan", } --- ---- @field Nevada --- --- These are all airbases of Nevada: + +--- These are all airbases of Nevada: -- -- * AIRBASE.Nevada.Creech_AFB -- * AIRBASE.Nevada.Groom_Lake_AFB @@ -129,7 +127,7 @@ AIRBASE.Caucasus = { -- * AIRBASE.Nevada.Pahute_Mesa_Airstrip -- * AIRBASE.Nevada.Tonopah_Airport -- * AIRBASE.Nevada.Tonopah_Test_Range_Airfield --- +-- @field Nevada AIRBASE.Nevada = { ["Creech_AFB"] = "Creech AFB", ["Groom_Lake_AFB"] = "Groom Lake AFB", @@ -150,10 +148,8 @@ AIRBASE.Nevada = { ["Tonopah_Airport"] = "Tonopah Airport", ["Tonopah_Test_Range_Airfield"] = "Tonopah Test Range Airfield", } --- ---- @field Normandy --- --- These are all airbases of Normandy: + +--- These are all airbases of Normandy: -- -- * AIRBASE.Normandy.Saint_Pierre_du_Mont -- * AIRBASE.Normandy.Lignerolles @@ -186,6 +182,7 @@ AIRBASE.Nevada = { -- * AIRBASE.Normandy.Funtington -- * AIRBASE.Normandy.Tangmere -- * AIRBASE.Normandy.Ford +-- @field Normandy AIRBASE.Normandy = { ["Saint_Pierre_du_Mont"] = "Saint Pierre du Mont", ["Lignerolles"] = "Lignerolles", @@ -219,28 +216,27 @@ AIRBASE.Normandy = { ["Tangmere"] = "Tangmere", ["Ford"] = "Ford", } --- ---- @field PersianGulf + +--- These are all airbases of the Persion Gulf Map: -- --- These are all airbases of the Persion Gulf Map: --- --- * AIRBASE.PersianGulf.Fujairah_Intl --- * AIRBASE.PersianGulf.Qeshm_Island --- * AIRBASE.PersianGulf.Sir_Abu_Nuayr --- * AIRBASE.PersianGulf.Abu_Musa_Island_Airport --- * AIRBASE.PersianGulf.Bandar_Abbas_Intl --- * AIRBASE.PersianGulf.Bandar_Lengeh --- * AIRBASE.PersianGulf.Tunb_Island_AFB --- * AIRBASE.PersianGulf.Havadarya --- * AIRBASE.PersianGulf.Lar_Airbase --- * AIRBASE.PersianGulf.Sirri_Island --- * AIRBASE.PersianGulf.Tunb_Kochak --- * AIRBASE.PersianGulf.Al_Dhafra_AB --- * AIRBASE.PersianGulf.Dubai_Intl --- * AIRBASE.PersianGulf.Al_Maktoum_Intl --- * AIRBASE.PersianGulf.Khasab --- * AIRBASE.PersianGulf.Al_Minhad_AB - -- * AIRBASE.PersianGulf.Sharjah_Intl +-- * AIRBASE.PersianGulf.Fujairah_Intl +-- * AIRBASE.PersianGulf.Qeshm_Island +-- * AIRBASE.PersianGulf.Sir_Abu_Nuayr +-- * AIRBASE.PersianGulf.Abu_Musa_Island_Airport +-- * AIRBASE.PersianGulf.Bandar_Abbas_Intl +-- * AIRBASE.PersianGulf.Bandar_Lengeh +-- * AIRBASE.PersianGulf.Tunb_Island_AFB +-- * AIRBASE.PersianGulf.Havadarya +-- * AIRBASE.PersianGulf.Lar_Airbase +-- * AIRBASE.PersianGulf.Sirri_Island +-- * AIRBASE.PersianGulf.Tunb_Kochak +-- * AIRBASE.PersianGulf.Al_Dhafra_AB +-- * AIRBASE.PersianGulf.Dubai_Intl +-- * AIRBASE.PersianGulf.Al_Maktoum_Intl +-- * AIRBASE.PersianGulf.Khasab +-- * AIRBASE.PersianGulf.Al_Minhad_AB +-- * AIRBASE.PersianGulf.Sharjah_Intl +-- @field PersianGulf AIRBASE.PersianGulf = { ["Fujairah_Intl"] = "Fujairah Intl", ["Qeshm_Island"] = "Qeshm Island", @@ -260,14 +256,29 @@ AIRBASE.PersianGulf = { ["Al_Minhad_AB"] = "Al Minhad AB", ["Sharjah_Intl"] = "Sharjah Intl", } --- ---- @field Terminal Types of parking spots. + +--- Terminal Types of parking spots. See also https://wiki.hoggitworld.com/view/DCS_func_getParking +-- +-- Supported types are: +-- +-- * AIRBASE.TerminalType.Runway: Valid spawn points on runway. +-- * AIRBASE.TerminalType.HelicopterOnly: Special spots for Helicopers. +-- * AIRBASE.TerminalType.Shelter: Hardened Air Shelter. Currently only on Caucaus map. +-- * AIRBASE.TerminalType.OpenMed: Open/Shelter air airplane only. +-- * AIRBASE.TerminalType.OpenBig: Open air spawn points. Generally larger but does not guarantee large aircraft are capable of spawning there. +-- * AIRBASE.TerminalType.OpenMedOrBig: Combines OpenMed and OpenBig spots. +-- * AIRBASE.TerminalType.HelicopterUnsable: Combines HelicopterOnly, OpenMed and OpenBig. +-- * AIRBASE.TerminalType.FighterAircraft: Combines Shelter. OpenMed and OpenBig spots. +-- @field TerminalType AIRBASE.TerminalType = { Runway=16, - Helicopter=40, - HardenedShelter=68, - OpenOrShelter=72, - OpenAir=104, + HelicopterOnly=40, + Shelter=68, + OpenMed=72, + OpenBig=104, + OpenMedOrBig=176, + HelicopterUsable=216, + FighterAircraft=244, } -- Registration. @@ -343,6 +354,7 @@ function AIRBASE:GetAllAirbases(coalition) return airbases end + --- Returns a table of parking data for a given airbase. If the optional parameter *available* is true only available parking will be returned, otherwise all parking at the base is returned. Term types have the following enumerated values: -- -- * 16 : Valid spawn points on runway @@ -364,6 +376,7 @@ end -- @param #boolean available If true, only available parking spots will be returned. -- @return #table Table with parking data. See https://wiki.hoggitworld.com/view/DCS_func_getParking function AIRBASE:GetParkingData(available) + self:F2(available) -- Get DCS airbase object. local DCSAirbase=self:GetDCSObject() @@ -374,27 +387,27 @@ function AIRBASE:GetParkingData(available) parkingdata=DCSAirbase:getParking(available) end + self:T2({parkingdata=parkingdata}) return parkingdata end ---- Get number parking spots at an airbase. Optionally, for a specific terminal type. Spots on runway are exculded if not explicitly requested by terminal type +--- Get number of parking spots at an airbase. Optionally, a specific terminal type can be requested. -- @param #AIRBASE self --- @param #number termtype Terminal type. --- @return #number Number of free parking spots at this airbase. +-- @param #number termtype Terminal type of which the number of spots is counted. Default all spots but spawn points on runway. +-- @return #number Number of parking spots at this airbase. function AIRBASE:GetParkingSpotsNumber(termtype) -- Get free parking spots data. local parkingdata=self:GetParkingData(false) - local nfree=0 + local nspots=0 for _,parkingspot in pairs(parkingdata) do - -- Spots on runway are not counted unless explicitly requested. - if (termtype~=nil and parkingspot.Term_Type==termtype) or (termtype==nil and parkingspot.Term_Type~=AIRBASE.TerminalType.Runway) then - nfree=nfree+1 + if self:_CheckTerminalType(parkingspot.Term_Type, termtype) then + nspots=nspots+1 end end - return nfree + return nspots end --- Get number of free parking spots at an airbase. @@ -410,7 +423,7 @@ function AIRBASE:GetFreeParkingSpotsNumber(termtype, allowTOAC) local nfree=0 for _,parkingspot in pairs(parkingdata) do -- Spots on runway are not counted unless explicitly requested. - if (termtype~=nil and parkingspot.Term_Type==termtype) or (termtype==nil and parkingspot.Term_Type~=AIRBASE.TerminalType.Runway) then + if self:_CheckTerminalType(parkingspot.Term_Type, termtype) then if (allowTOAC and allowTOAC==true) or parkingspot.TO_AC==false then nfree=nfree+1 end @@ -434,7 +447,7 @@ function AIRBASE:GetFreeParkingSpotsCoordinates(termtype, allowTOAC) local spots={} for _,parkingspot in pairs(parkingdata) do -- Coordinates on runway are not returned unless explicitly requested. - if (termtype and parkingspot.Term_Type==termtype) or (termtype==nil and parkingspot.Term_Type~=AIRBASE.TerminalType.Runway) then + if self:_CheckTerminalType(parkingspot.Term_Type, termtype) then if (allowTOAC and allowTOAC==true) or parkingspot.TO_AC==false then table.insert(spots, COORDINATE:NewFromVec3(parkingspot.vTerminalPos)) end @@ -456,18 +469,17 @@ function AIRBASE:GetParkingSpotsCoordinates(termtype) -- Put coordinates of free spots into table. local spots={} for _,parkingspot in pairs(parkingdata) do + -- Coordinates on runway are not returned unless explicitly requested. - if (termtype and parkingspot.Term_Type==termtype) or (termtype==nil and parkingspot.Term_Type~=AIRBASE.TerminalType.Runway) then + if self:_CheckTerminalType(parkingspot.Term_Type, termtype) then + + -- Get coordinate from Vec3 terminal position. local _coord=COORDINATE:NewFromVec3(parkingspot.vTerminalPos) - local gotunits,gotstatics,gotscenery,_,statics,_=_coord:ScanObjects(.5) - env.info(string.format("FF scan: terminal index %03d, type = %03d, gotunits=%s, gotstatics=%s, gotscenery=%s", parkingspot.Term_Index+1, parkingspot.Term_Type, tostring(gotunits), tostring(gotstatics), tostring(gotscenery))) - if gotstatics then - for _,static in pairs(statics) do - env.info(string.format("FF Static name= %s", tostring(static:getName()))) - end - end + + -- Add to table. table.insert(spots, _coord) end + end return spots @@ -495,17 +507,17 @@ function AIRBASE:GetParkingSpotsTable(termtype) return false end - -- Put coordinates of free spots into table. - local freespots={} + -- Put coordinates of parking spots into table. + local spots={} for _,_spot in pairs(parkingdata) do - if (termtype and _spot.Term_Type==termtype) or (termtype==nil and _spot.Term_Type~=AIRBASE.TerminalType.Runway) then + if self:_CheckTerminalType(_spot.Term_Type, termtype) then local _free=_isfree(_spot) local _coord=COORDINATE:NewFromVec3(_spot.vTerminalPos) - table.insert(freespots, {Coordinate=_coord, TerminalID=_spot.Term_Index, TerminalType=_spot.Term_Type, TOAC=_spot.TO_AC, Free=_free, TerminalID0=_spot.Term_Index_0, DistToRwy=_spot.fDistToRW}) + table.insert(spots, {Coordinate=_coord, TerminalID=_spot.Term_Index, TerminalType=_spot.Term_Type, TOAC=_spot.TO_AC, Free=_free, TerminalID0=_spot.Term_Index_0, DistToRwy=_spot.fDistToRW}) end end - return freespots + return spots end --- Get a table containing the coordinates, terminal index and terminal type of free parking spots at an airbase. @@ -521,7 +533,7 @@ function AIRBASE:GetFreeParkingSpotsTable(termtype, allowTOAC) -- Put coordinates of free spots into table. local freespots={} for _,_spot in pairs(parkingfree) do - if (termtype and _spot.Term_Type==termtype) or (termtype==nil and _spot.Term_Type~=AIRBASE.TerminalType.Runway) then + if self:_CheckTerminalType(_spot.Term_Type, termtype) then if (allowTOAC and allowTOAC==true) or _spot.TO_AC==false then local _coord=COORDINATE:NewFromVec3(_spot.vTerminalPos) table.insert(freespots, {Coordinate=_coord, TerminalID=_spot.Term_Index, TerminalType=_spot.Term_Type, TOAC=_spot.TO_AC, Free=true, TerminalID0=_spot.Term_Index_0, DistToRwy=_spot.fDistToRW}) @@ -537,21 +549,223 @@ end -- @param #number termtype Terminal type for which marks should be placed. function AIRBASE:MarkParkingSpots(termtype) + -- Get parking data from getParking() wrapper function. local parkingdata=self:GetParkingSpotsTable(termtype) + -- Get airbase name. local airbasename=self:GetName() self:E(string.format("Parking spots at %s for termial type %s:", airbasename, tostring(termtype))) for _,_spot in pairs(parkingdata) do -- Mark text. - local _text=string.format("%s, Term Index = %3d, Term Type = %03d, Free = %5s, TOAC = %5s, Term ID0 = %3d, Dist2Rwy = %.1f", - airbasename, _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy) + local _text=string.format("Term Index=%d, Term Type=%d, Free=%s, TOAC=%s, Term ID0=%d, Dist2Rwy=%.1f m", + _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy) -- Create mark on the F10 map. _spot.Coordinate:MarkToAll(_text) -- Info to DCS.log file. + local _text=string.format("%s, Term Index=%3d, Term Type=%03d, Free=%5s, TOAC=%5s, Term ID0=%3d, Dist2Rwy=%.1f m", + airbasename, _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy) self:E(_text) end -end \ No newline at end of file +end + + +--- Helper function to check for the correct terminal type including "artificial" ones. +-- @param #AIRBASE self +-- @param #number Term_Type Termial type from getParking routine. +-- @param #number termtype Terminal type from AIRBASE.TerminalType enumerator. +-- @return True if terminal types match. +function AIRBASE:_CheckTerminalType(Term_Type, termtype) + + -- Nill check for Term_Type. + if Term_Type==nil then + return false + end + + -- If no terminal type is requested, we return true. BUT runways are excluded unless explicitly requested. + if termtype==nil then + if Term_Type==AIRBASE.TerminalType.Runway then + return false + else + return true + end + end + + -- Init no match. + local match=false + + -- Standar case. + if Term_Type==termtype then + match=true + end + + -- Artificial cases. Combination of terminal types. + if termtype==AIRBASE.TerminalType.OpenMedOrBig then + if Term_Type==AIRBASE.TerminalType.OpenMed or Term_Type==AIRBASE.TerminalType.OpenBig then + match=true + end + elseif termtype==AIRBASE.TerminalType.HelicopterUsable then + if Term_Type==AIRBASE.TerminalType.OpenMed or Term_Type==AIRBASE.TerminalType.OpenBig or Term_Type==AIRBASE.TerminalType.HelicopterOnly then + match=true + end + elseif termtype==AIRBASE.TerminalType.FighterAircraft then + if Term_Type==AIRBASE.TerminalType.OpenMed or Term_Type==AIRBASE.TerminalType.OpenBig or Term_Type==AIRBASE.TerminalType.Shelter then + match=true + end + end + + return match +end + +--- Seach an unoccupied parking spot at a specific airport and for a specific aircraft. +-- @param #AIRBASE self +-- @param Wrapper.Group#GROUP group Aircraft group for which the parking spots are requested. +-- @param #number terminaltype (Optional) Only search spots at a specific terminal type. Default is all types execpt on runway. +-- @param #number scanradius (Optional) Radius in meters around parking spot to scan for obstacles. Default 30 m. +-- @param #boolean scanunits (Optional) Scan for units as obstacles. Default true. +-- @param #boolean scanstatics (Optional) Scan for statics as obstacles. Default true. +-- @param #boolean scanscenery (Optional) Scan for scenery as obstacles. Default false. Can cause problems with e.g. shelters. +-- @return #table Table of coordinates and terminal IDs of the parking spots or nil, if no valid parking spots for the whole group could be found. +function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, scanunits, scanstatics, scanscenery) + + -- Init default + scanradius=scanradius or 30 + if scanunits==nil then + scanunits=true + end + if scanstatics==nil then + scanstatics=true + end + if scanscenery==nil then + scanscenery=false + end + + -- Get the size of an object. + local function _GetObjectSize(unit,mooseobject) + if mooseobject then + unit=unit:GetDCSObject() + end + if unit and unit:isExist() then + local DCSdesc=unit:getDesc() + local x=DCSdesc.box.max.x+math.abs(DCSdesc.box.min.x) + local y=DCSdesc.box.max.y+math.abs(DCSdesc.box.min.y) --height + local z=DCSdesc.box.max.z+math.abs(DCSdesc.box.min.z) + return math.max(x,z), x , y, z + end + return 0,0,0,0 + end + + -- Function calculating the overlap of two (square) objects. + local function _overlap(mooseobject, dcsobject, dist) + local l1=_GetObjectSize(mooseobject, true) + local l2=_GetObjectSize(dcsobject) + local safedist=(l1/2+l2/2)*1.1 + local safe = (dist > safedist) + self:T3(string.format("l1=%.1f l2=%.1f s=%.1f d=%.1f ==> safe=%s", l1,l2,safedist,dist,tostring(safe))) + return safe + end + + -- Get airport name. + local airport=self:GetName() + + -- Get free parking spot data table. + --local parkingdata=self:GetFreeParkingSpotsTable(terminaltype, true) + local parkingdata=self:GetParkingSpotsTable(terminaltype) + + -- Get the aircraft size, i.e. it's longest side of x,z. + local aircraft=group:GetUnit(1) + local nspots=group:GetSize() + local _aircraftsize, ax,ay,az=_GetObjectSize(aircraft, true) + + -- Debug info. + self:T(string.format("Looking for %d parking spot(s) at %s for aircraft of size %.1f m (x=%.1f,y=%.1f,z=%.1f) at termial type %s.", nspots, airport, _aircraftsize, ax,ay,az, tostring(terminaltype))) + + -- Table of valid spots. + local validspots={} + local nvalid=0 + + -- Loop over all known parking spots + for _,parkingspot in pairs(parkingdata) do + + -- Check for requested terminal type if any. + if self:_CheckTerminalType(parkingspot.TerminalType, terminaltype) then + end + + -- Coordinate of the parking spot. + local _spot=parkingspot.Coordinate --Core.Point#COORDINATE + + -- Scan a radius of 50 meters around the spot. + local _,_,_,_units,_statics,_sceneries=_spot:ScanObjects(scanradius) + + + -- Loop over objects within scan radius. + local occupied=false + + -- Check all units. + for _,unit in pairs(_units) do + + local _vec3=unit:getPoint() + local _coord=COORDINATE:NewFromVec3(_vec3) + local _dist=_coord:Get2DDistance(_spot) + local _safe=_overlap(aircraft, unit, _dist) + + --local l,x,y,z=_GetObjectSize(unit) + --_coord:MarkToAll(string.format("scan found unit %s\nx=%.2f y=%.2f z=%.2f\nl=%.2f d=%.2f safe=%s", unit:getName(), x, y, z, l, _dist, tostring(_safe))) + + if scanunits and not _safe then + occupied=true + end + end + + -- Check all statics. + for _,static in pairs(_statics) do + local _vec3=static:getPoint() + local _coord=COORDINATE:NewFromVec3(_vec3) + local _dist=_coord:Get2DDistance(_spot) + local _safe=_overlap(aircraft, static, _dist) + + --local l,x,y,z=_GetObjectSize(static) + --_coord:MarkToAll(string.format("scan found static %s\nx=%.2f y=%.2f z=%.2f\nl=%.2f d=%.2f safe=%s", static:getName(),x,y,z,l,_dist, tostring(_safe))) + + if scanstatics and not _safe then + occupied=true + end + end + + -- Check all scenery. + for _,scenery in pairs(_sceneries) do + local _vec3=scenery:getPoint() + local _coord=COORDINATE:NewFromVec3(_vec3) + local _dist=_coord:Get2DDistance(_spot) + local _safe=_overlap(aircraft, scenery, _dist) + + --local l,x,y,z=_GetObjectSize(scenery) + --_coord:MarkToAll(string.format("scan found scenery %s\nx=%.2f y=%.2f z=%.2f\nl=%.2f d=%.2f", scenery:getName(),x,y,z,l,_dist)) + + if scanscenery and not _safe then + occupied=true + end + end + + --_spot:MarkToAll(string.format("Parking spot %d free=%s", parkingspot.TerminalID, tostring(not occupied))) + if occupied then + self:T(string.format("%s: Parking spot id %d occupied.", airport, parkingspot.TerminalID)) + else + self:T(string.format("%s: Parking spot id %d free.", airport, parkingspot.TerminalID)) + if nvalid Date: Thu, 28 Jun 2018 00:20:48 +0200 Subject: [PATCH 190/420] Spawn improvements COORDINATE: - Improved GetClosestParkingSpot functions. - Improved ScanObject function. SPAWN: - Improved SpawnAtAirbase function RAT: - Improved spawn function - Added some takeoff type user functions. UTILS: - Added clock vs second functions - Added split function AIRBASE: - Added checkonrunway function - Other improvemetns. --- Moose Development/Moose/Core/Point.lua | 104 +++++-- Moose Development/Moose/Core/Spawn.lua | 95 ++++-- Moose Development/Moose/Functional/RAT.lua | 141 ++++++--- Moose Development/Moose/Utilities/Utils.lua | 96 ++++++ Moose Development/Moose/Wrapper/Airbase.lua | 321 ++++++++++++-------- 5 files changed, 531 insertions(+), 226 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index e99cd3e7e..e007998ce 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -294,15 +294,18 @@ do -- COORDINATE --- Returns if the 2 coordinates are at the same 2D position. -- @param #COORDINATE self - -- @param #number radius Scan radius in meters. + -- @param #number radius (Optional) Scan radius in meters. Default 100 m. + -- @param #boolean scanunits (Optional) If true scan for units. Default true. + -- @param #boolean scanstatics (Optional) If true scan for static objects. Default true. + -- @param #boolean scanscenery (Optional) If true scan for scenery objects. Default false. -- @return True if units were found. -- @return True if statics were found. -- @return True if scenery objects were found. -- @return Unit objects found. -- @return Static objects found. -- @return Scenery objects found. - function COORDINATE:ScanObjects(radius) - self:T(string.format("Scanning in radius %.1f m.", radius)) + function COORDINATE:ScanObjects(radius, scanunits, scanstatics, scanscenery) + self:F(string.format("Scanning in radius %.1f m.", radius)) local SphereSearch = { id = world.VolumeType.SPHERE, @@ -312,6 +315,30 @@ do -- COORDINATE } } + -- Defaults + radius=radius or 100 + if scanunits==nil then + scanunits=true + end + if scanstatics==nil then + scanstatics=true + end + if scanscenery==nil then + scanscenery=false + end + + --{Object.Category.UNIT, Object.Category.STATIC, Object.Category.SCENERY} + local scanobjects={} + if scanunits then + table.insert(scanobjects, Object.Category.UNIT) + end + if scanstatics then + table.insert(scanobjects, Object.Category.STATIC) + end + if scanscenery then + table.insert(scanobjects, Object.Category.SCENERY) + end + -- Found stuff. local Units = {} local Statics = {} @@ -352,7 +379,7 @@ do -- COORDINATE end -- Search the world. - world.searchObjects({Object.Category.UNIT, Object.Category.STATIC, Object.Category.SCENERY}, SphereSearch, EvaluateZone) + world.searchObjects(scanobjects, SphereSearch, EvaluateZone) for _,unit in pairs(Units) do self:T(string.format("Scan found unit %s", unit:getName())) @@ -361,7 +388,7 @@ do -- COORDINATE self:T(string.format("Scan found static %s", static:getName())) end for _,scenery in pairs(Scenery) do - self:T(string.format("Scan found scenery %s", scenery:getName())) + self:T(string.format("Scan found scenery %s", scenery:getTypeName())) end return gotunits, gotstatics, gotscenery, Units, Statics, Scenery @@ -1021,22 +1048,28 @@ do -- COORDINATE --- Gets the nearest parking spot. -- @param #COORDINATE self - -- @param #boolean free (Optional) Only look for free parking spots. By default the closest parking spot is returned regardless of whether it is free or not. - -- @param Wrapper.Airbase#AIRBASE airbase (Optional) Search only parking spots at that airbase. - -- @param Wrapper.Airbase#Terminaltype terminaltype Type of the terminal. + -- @param Wrapper.Airbase#AIRBASE airbase (Optional) Search only parking spots at this airbase. + -- @param Wrapper.Airbase#Terminaltype terminaltype (Optional) Type of the terminal. Default any execpt valid spawn points on runway. + -- @param #boolean free (Optional) If true, returns the closest free spot. If false, returns the closest occupied spot. If nil, returns the closest spot regardless of free or occupied. -- @return Core.Point#COORDINATE Coordinate of the nearest parking spot. - -- @return #number Distance to closest parking spot. - function COORDINATE:GetClosestParkingSpot(free, airbase, terminaltype) - + -- @return #number Terminal ID. + -- @return #number Distance to closest parking spot in meters. + function COORDINATE:GetClosestParkingSpot(airbase, terminaltype, free) + + -- Get airbase table. local airbases={} if airbase then table.insert(airbases,airbase) else - airbases=AIRBASE:GetAllAirbases() + airbases=AIRBASE.GetAllAirbases() end + -- Init. local _closest=nil --Core.Point#COORDINATE + local _termID=nil local _distmin=nil + + -- Loop over all airbases. for _,_airbase in pairs(airbases) do local mybase=_airbase --Wrapper.Airbase#AIRBASE @@ -1044,44 +1077,51 @@ do -- COORDINATE for _,_spot in pairs(parkingdata) do - -- Get coordinate if it matches the requirements. - local _coord=nil --Core.Point#COORDINATE - if (free and _spot.Free) or free==nil then - if (terminaltype and _spot.TerminalType==terminaltype) or terminaltype==nil then - _coord=_spot.Coordinate - end - end - - -- Compare distance to closest one found so far. - if _coord then + -- Check for parameters. + if (free==true and _spot.Free==true) or (free==false and _spot.Free==false) or free==nil then + + local _coord=_spot.Coordinate --Core.Point#COORDINATE + local _dist=self:Get2DDistance(_coord) if _distmin==nil then _closest=_coord _distmin=_dist - else - local _dist=self:Get2DDistance(_coord) + _termID=_spot.TerminalID + else if _dist<_distmin then _distmin=_dist _closest=_coord + _termID=_spot.TerminalID end end - - end - + + end end end - - return _closest, _distmin + + return _closest, _termID, _distmin end --- Gets the nearest free parking spot. -- @param #COORDINATE self -- @param Wrapper.Airbase#AIRBASE airbase (Optional) Search only parking spots at that airbase. - -- @param Wrapper.Airbase#Terminaltype terminaltype Type of the terminal. + -- @param Wrapper.Airbase#Terminaltype terminaltype (Optional) Type of the terminal. -- @return #COORDINATE Coordinate of the nearest free parking spot. - -- @return #number Distance to closest free parking spot. + -- @return #number Terminal ID. + -- @return #number Distance to closest free parking spot in meters. function COORDINATE:GetClosestFreeParkingSpot(airbase, terminaltype) - return self:GetClosestParkingSpot(true, airbase, terminaltype) + return self:GetClosestParkingSpot(airbase, terminaltype, true) + end + + --- Gets the nearest occupied parking spot. + -- @param #COORDINATE self + -- @param Wrapper.Airbase#AIRBASE airbase (Optional) Search only parking spots at that airbase. + -- @param Wrapper.Airbase#Terminaltype terminaltype (Optional) Type of the terminal. + -- @return #COORDINATE Coordinate of the nearest occupied parking spot. + -- @return #number Terminal ID. + -- @return #number Distance to closest occupied parking spot in meters. + function COORDINATE:GetClosestOccupiedParkingSpot(airbase, terminaltype) + return self:GetClosestParkingSpot(airbase, terminaltype, false) end --- Gets the nearest coordinate to a road. diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 3258feb80..ec7e7414d 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1218,7 +1218,8 @@ end -- @param Wrapper.Airbase#AIRBASE SpawnAirbase The @{Wrapper.Airbase} where to spawn the group. -- @param #SPAWN.Takeoff Takeoff (optional) The location and takeoff method. Default is Hot. -- @param #number TakeoffAltitude (optional) The altitude above the ground. --- @param #number TerminalType (optional) The terminal type the aircraft should be spawned at. +-- @param Wrapper.Airbase#AIRBASE.TerminalType TerminalType (optional) The terminal type the aircraft should be spawned at. See @{Wrapper.Airbase#AIRBASE.TerminalType}. +-- @param #boolean EmergencyAirSpawn (optional) If true (default), groups are spawned in air if there is no parking spot at the airbase. If false, nothing is spawned if no parking spot is available. -- @return Wrapper.Group#GROUP that was spawned or nil when nothing was spawned. -- @usage -- Spawn_Plane = SPAWN:New( "Plane" ) @@ -1239,7 +1240,7 @@ end -- -- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Cold, nil, AIRBASE.TerminalType.OpenBig ) -- -function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalType ) -- R2.2, R2.4 +function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalType, EmergencyAirSpawn ) -- R2.2, R2.4 self:F( { self.SpawnTemplatePrefix, SpawnAirbase, Takeoff, TakeoffAltitude, TerminalType } ) -- Get position of airbase. @@ -1249,6 +1250,11 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT -- Set take off type. Default is hot. Takeoff = Takeoff or SPAWN.Takeoff.Hot + -- By default, groups are spawned in air if no parking spot is available. + if EmergencyAirSpawn==nil then + EmergencyAirSpawn=true + end + if self:_GetSpawnIndex( self.SpawnIndex + 1 ) then -- Get group template. @@ -1259,9 +1265,13 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT -- Debug output self:T( { "Current point of ", self.SpawnTemplatePrefix, SpawnAirbase } ) - -- Template group and unit. + -- Template group, unit and its attributes. local TemplateGroup = GROUP:FindByName(self.SpawnTemplatePrefix) - local ishelo=TemplateGroup:GetUnit(1):HasAttribute("Helicopters") + local TemplateUnit=TemplateGroup:GetUnit(1) + local ishelo=TemplateUnit:HasAttribute("Helicopters") + local isbomber=TemplateUnit:HasAttribute("Bombers") + local istransport=TemplateUnit:HasAttribute("Transports") + local isfighter=TemplateUnit:HasAttribute("Battleplanes") -- First waypoint of the group. local SpawnPoint = SpawnTemplate.route.points[1] @@ -1332,32 +1342,64 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT -- Number of free parking spots at the airbase. if spawnonship or spawnonfarp or spawnonrunway then -- These places work procedural and have some kind of build in queue ==> Less effort. + self:T(string.format("Group %s is spawned on farp/ship/runway %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName())) nfree=SpawnAirbase:GetFreeParkingSpotsNumber(termtype, spawnonship or spawnonfarp or spawnonrunway) spots=SpawnAirbase:GetFreeParkingSpotsTable(termtype, spawnonship or spawnonfarp or spawnonrunway) else - if ishelo and termtype==nil then - -- Helo is spawned. - -- Try helo spots first. - spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly) - nfree=#spots - if nfree<#SpawnTemplate.units then - -- Not enough helo ports. Let's try all terminal types. - spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterUsable) + if ishelo then + if termtype==nil then + -- Helo is spawned. Try exclusive helo spots first. + self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterOnly)) + spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly) nfree=#spots + if nfree<#SpawnTemplate.units then + -- Not enough helo ports. Let's try also other terminal types. + self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterUsable)) + spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterUsable) + nfree=#spots + end + else + -- No terminal type specified. We try all spots except shelters. + self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), termtype)) + spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype) + nfree=#spots end else -- Fixed wing aircraft is spawned. + if termtype==nil then --TODO: Add some default cases for transport, bombers etc. if no explicit terminal type is provided. - spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype) - nfree=#spots + --TODO: We don't want Bombers to spawn in shelters. But I don't know a good attribute for just fighers. + --TODO: Some attributes are "Helicopters", "Bombers", "Transports", "Battleplanes". Need to check it out. + if isbomber or istransport then + -- First we fill the potentially bigger spots. + self:T(string.format("Transport/bomber group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.OpenBig)) + spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenBig) + nfree=#spots + if nfree<#SpawnTemplate.units then + -- Now we try the smaller ones. + self:T(string.format("Transport/bomber group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.OpenMedOrBig)) + spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenMedOrBig) + nfree=#spots + end + else + self:T(string.format("Fighter group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.FighterAircraft)) + spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.FighterAircraft) + nfree=#spots + end + else + -- Terminal type explicitly given. + self:T(string.format("Plane group %s is at %s using terminal type %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), tostring(termtype))) + spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype) + nfree=#spots + end end end -- Get parking data. local parkingdata=SpawnAirbase:GetParkingSpotsTable(termtype) - self:T(string.format("Parking at %s, terminal type %s:", SpawnAirbase:GetName(), tostring(termtype))) + self:T2(string.format("Parking at %s, terminal type %s:", SpawnAirbase:GetName(), tostring(termtype))) for _,_spot in pairs(parkingdata) do - self:T(string.format("%s, Termin Index = %3d, Term Type = %03d, Free = %5s, TOAC = %5s, Term ID0 = %3d, Dist2Rwy = %4d", + self:T2(string.format("%s, Termin Index = %3d, Term Type = %03d, Free = %5s, TOAC = %5s, Term ID0 = %3d, Dist2Rwy = %4d", SpawnAirbase:GetName(), _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy)) end self:T(string.format("%s at %s: free parking spots = %d - number of units = %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), nfree, #SpawnTemplate.units)) @@ -1371,7 +1413,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT end else - if not self.SpawnUnControlled then + if EmergencyAirSpawn and not self.SpawnUnControlled then self:E(string.format("WARNING: Group %s has no parking spots at %s ==> air start!", self.SpawnTemplatePrefix, SpawnAirbase:GetName())) -- Not enough parking spots at the airport ==> Spawn in air. @@ -1388,15 +1430,15 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT PointVec3.x=PointVec3.x+math.random(-500,500) PointVec3.z=PointVec3.z+math.random(-500,500) if ishelo then - PointVec3.y=PointVec3:GetLandHeight()+math.random(200,1200) + PointVec3.y=PointVec3:GetLandHeight()+math.random(100,1000) else -- Randomize position so that multiple AC wont be spawned on top even in air. - PointVec3.y=PointVec3:GetLandHeight()+math.random(500,5000) + PointVec3.y=PointVec3:GetLandHeight()+math.random(500,2500) end Takeoff=GROUP.Takeoff.Air else - self:E(string.format("WARNING: Group %s has no parking spots at %s ==> Uncontrolled spawning ==> No spawn!", self.SpawnTemplatePrefix, SpawnAirbase:GetName())) + self:E(string.format("WARNING: Group %s has no parking spots at %s ==> No emergency air start or uncontrolled spawning ==> No spawn!", self.SpawnTemplatePrefix, SpawnAirbase:GetName())) return nil end end @@ -1475,23 +1517,26 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT -- Spawn group. local GroupSpawned = self:SpawnWithIndex( self.SpawnIndex ) - + -- When spawned in the air, we need to generate a Takeoff Event. if Takeoff == GROUP.Takeoff.Air then for UnitID, UnitSpawned in pairs( GroupSpawned:GetUnits() ) do SCHEDULER:New( nil, BASE.CreateEventTakeoff, { GroupSpawned, timer.getTime(), UnitSpawned:GetDCSObject() } , 1 ) end end - - return GroupSpawned + + -- Check if we accidentally spawned on the runway. Needs to be schedules, because group is not immidiately alive. + if Takeoff~=SPAWN.Takeoff.Runway and Takeoff~=SPAWN.Takeoff.Air and spawnonairport then + SCHEDULER:New(nil, AIRBASE.CheckOnRunWay, {SpawnAirbase, GroupSpawned, 25, true} , 1.0) + end + + return GroupSpawned end end return nil end - - --- Will spawn a group from a Vec3 in 3D space. -- This method is mostly advisable to be used if you want to simulate spawning units in the air, like helicopters or airplanes. -- Note that each point in the route assigned to the spawning group is reset to the point of the spawn. diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 16381f87e..a3c54a281 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -129,7 +129,6 @@ -- @field #string livery Livery of the aircraft set by user. -- @field #string skill Skill of AI. -- @field #boolean ATCswitch Enable/disable ATC if set to true/false. --- @field #string parking_id String with a special parking ID for the aircraft. -- @field #boolean radio If true/false disables radio messages from the RAT groups. -- @field #number frequency Radio frequency used by the RAT groups. -- @field #string modulation Ratio modulation. Either "FM" or "AM". @@ -365,7 +364,6 @@ RAT={ livery=nil, -- Livery of the aircraft. skill="High", -- Skill of AI. ATCswitch=true, -- Enable ATC. - parking_id=nil, -- Specific parking ID when aircraft are spawned at airports. radio=nil, -- If true/false disables radio messages from the RAT groups. frequency=nil, -- Radio frequency used by the RAT groups. modulation=nil, -- Ratio modulation. Either "FM" or "AM". @@ -997,6 +995,36 @@ function RAT:SetTakeoff(type) self.takeoff=_Type end +--- Set takeoff type cold. Aircraft will spawn at a parking spot with engines off. +-- @param #RAT self +function RAT:SetTakeoffCold() + self.takeoff=RAT.wp.cold +end + +--- Set takeoff type to hot. Aircraft will spawn at a parking spot with engines on. +-- @param #RAT self +function RAT:SetTakeoffHot() + self.takeoff=RAT.wp.hot +end + +--- Set takeoff type to runway. Aircraft will spawn directly on the runway. +-- @param #RAT self +function RAT:SetTakeoffRunway() + self.takeoff=RAT.wp.runway +end + +--- Set takeoff type to cold or hot. Aircraft will spawn at a parking spot with 50:50 change of engines on or off. +-- @param #RAT self +function RAT:SetTakeoffColdOrHot() + self.takeoff=RAT.wp.coldorhot +end + +--- Set takeoff type to air. Aircraft will spawn in the air. +-- @param #RAT self +function RAT:SetTakeoffAir() + self.takeoff=RAT.wp.air +end + --- Set possible departure ports. This can be an airport or a zone defined in the mission editor. -- @param #RAT self -- @param #string departurenames Name or table of names of departure airports or zones. @@ -1332,15 +1360,6 @@ function RAT:ParkingSpotDB(switch) self.useparkingdb=switch end ---- Set parking id of aircraft. --- @param #RAT self --- @param #string id Parking ID of the aircraft. -function RAT:SetParkingID(id) - self:F2(id) - self.parking_id=id - self:T(RAT.id.."Setting parking ID to "..self.parking_id) -end - --- Enable Radio. Overrules the ME setting. -- @param #RAT self function RAT:RadioON() @@ -4920,8 +4939,9 @@ end -- @param Core.Point#COORDINATE spawnplace (Optional) Place where spawning should happen. If not present, first waypoint is taken. -- @param Wrapper.Airbase#AIRBASE departure Departure airbase or zone. -- @param #number takeoff Takeoff type. +-- @return #boolean True if modification was successful or nil if not, e.g. when no parking space was found and spawn in air is disabled. function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, takeoff) - self:F2({waypoints=waypoints, livery=livery, spawnplace=spawnplace}) + self:F2({waypoints=waypoints, livery=livery, spawnplace=spawnplace, departure=departure, takeoff=takeoff}) -- The 3D vector of the first waypoint, i.e. where we actually spawn the template group. local PointVec3 = COORDINATE:New(waypoints[1].x, waypoints[1].alt, waypoints[1].y) @@ -4931,6 +4951,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take -- Template group and unit. local TemplateGroup = GROUP:FindByName(self.SpawnTemplatePrefix) + local TemplateUnit=TemplateGroup:GetUnit(1) -- Check if we spawn on ground. local spawnonground=takeoff==RAT.wp.cold or takeoff==RAT.wp.hot or takeoff==RAT.wp.runway @@ -4986,41 +5007,76 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take -- Number of free parking spots. local nfree=0 - -- Set terminal type. + -- Set terminal type. Can also be nil. local termtype=self.termtype if spawnonrunway then termtype=AIRBASE.TerminalType.Runway end - -- Number of free parking spots at the airbase. + -- Get free parking spots depending on where we spawn. if spawnonship or spawnonfarp or spawnonrunway then -- These places work procedural and have some kind of build in queue ==> Less effort. + self:T(RAT.id..string.format("Group %s is spawned on farp/ship/runway %s.", self.alias, departure:GetName())) nfree=departure:GetFreeParkingSpotsNumber(termtype, spawnonship or spawnonfarp or spawnonrunway) spots=departure:GetFreeParkingSpotsTable(termtype, spawnonship or spawnonfarp or spawnonrunway) else - if self.category==RAT.cat.heli and termtype==nil then - -- Helo is spawned. - -- Try helo spots first. - spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly) - nfree=#spots - if nfree<#SpawnTemplate.units then - -- Not enough helo ports. Let's try all terminal types. - spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterUsable) + -- Helo is spawned. + if self.category==RAT.cat.heli then + if termtype==nil then + -- Try exclusive helo spots first. + self:T(RAT.id..string.format("Helo group %s is spawned at %s using terminal type %d.", self.alias, departure:GetName(), AIRBASE.TerminalType.HelicopterOnly)) + spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly) nfree=#spots + if nfree<#SpawnTemplate.units then + -- Not enough helo ports. Let's try also other terminal types. + self:T(RAT.id..string.format("Helo group %s is spawned at %s using terminal type %d.", self.alias, departure:GetName(), AIRBASE.TerminalType.HelicopterOnly)) + spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterUsable) + nfree=#spots + end + else + -- Terminal type specified explicitly. + self:T(RAT.id..string.format("Helo group %s is at %s using terminal type %d.", self.alias, departure:GetName(), termtype)) + spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, termtype) + nfree=#spots end else -- Fixed wing aircraft is spawned. + if termtype==nil then --TODO: Add some default cases for transport, bombers etc. if no explicit terminal type is provided. - spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, termtype) - nfree=#spots + --TODO: We don't want Bombers to spawn in shelters. But I don't know a good attribute for just fighers. + --TODO: Some attributes are "Helicopters", "Bombers", "Transports", "Battleplanes". Need to check it out. + local bomber=TemplateUnit:HasAttribute("Bombers") + local transport=TemplateUnit:HasAttribute("Transports") + if bomber or transport then + -- First we fill the potentially bigger spots. + self:T(RAT.id..string.format("Transport/bomber group %s is spawned at %s using terminal type %d.", self.alias, departure:GetName(), AIRBASE.TerminalType.OpenBig)) + spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenBig) + nfree=#spots + if nfree<#SpawnTemplate.units then + -- Now we try the smaller ones. + self:T(RAT.id..string.format("Transport/bomber group %s is at %s using terminal type %d.", self.alias, departure:GetName(), AIRBASE.TerminalType.OpenMed)) + spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenMed) + nfree=#spots + end + else + self:T(RAT.id..string.format("Fighter group %s is spawned at %s using terminal type %d.", self.alias, departure:GetName(), AIRBASE.TerminalType.FighterAircraft)) + spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.FighterAircraft) + nfree=#spots + end + else + -- Terminal type explicitly given. + self:T(RAT.id..string.format("Plane group %s is spawned at %s using terminal type %d.", self.alias, departure:GetName(), termtype)) + spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, termtype) + nfree=#spots + end end end - -- Get parking data. + -- Get parking data (just for debugging). local parkingdata=departure:GetParkingSpotsTable(termtype) - self:T(RAT.id..string.format("Parking at %s, terminal type %s:", departure:GetName(), tostring(termtype))) + self:T2(RAT.id..string.format("Parking at %s, terminal type %s:", departure:GetName(), tostring(termtype))) for _,_spot in pairs(parkingdata) do - self:T(RAT.id..string.format("%s, Termin Index = %3d, Term Type = %03d, Free = %5s, TOAC = %5s, Term ID0 = %3d, Dist2Rwy = %4d", + self:T2(RAT.id..string.format("%s, Termin Index = %3d, Term Type = %03d, Free = %5s, TOAC = %5s, Term ID0 = %3d, Dist2Rwy = %4d", departure:GetName(), _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy)) end self:T(RAT.id..string.format("%s at %s: free parking spots = %d - number of units = %d", self.alias, departure:GetName(), nfree, #SpawnTemplate.units)) @@ -5032,10 +5088,6 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take table.insert(parkingspots, spots[i].Coordinate) table.insert(parkingindex, spots[i].TerminalID) end - - if spawnonrunway then - --PointVec3=spots[1] - end else if self.respawn_inair or self.uncontrolled then @@ -5051,8 +5103,14 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take waypoints[1].type = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][1] -- type = Turning Point waypoints[1].action = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][2] -- action = Turning Point - -- Adjust altitude to be 500-1000 m above the airbase. - PointVec3.y=PointVec3:GetLandHeight()+math.random(500,1000) + -- Adjust and randomize position and altitude of the spawn point. + PointVec3.x=PointVec3.x+math.random(-500,500) + PointVec3.z=PointVec3.z+math.random(-500,500) + if self.category==RAT.cat.heli then + PointVec3.y=PointVec3:GetLandHeight()+math.random(100,1000) + else + PointVec3.y=PointVec3:GetLandHeight()+math.random(500,2500) + end else self:E(RAT.id..string.format("WARNING: RAT group %s has no parking spots at %s. Air start deactivated or uncontrolled AC!", self.alias, departure:GetName())) return nil @@ -5063,7 +5121,6 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take -- Translate the position of the Group Template to the Vec3. for UnitID = 1, #SpawnTemplate.units do - self:T2('Before Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) -- Template of the current unit. local UnitTemplate = SpawnTemplate.units[UnitID] @@ -5080,34 +5137,28 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take -- Shíps and FARPS seem to have a build in queue. if spawnonship or spawnonfarp or spawnonrunway or automatic then - self:T(RAT.id..string.format("RAT group %s spawning at farp, ship or runway %s.", self.alias, departure:GetName())) -- Spawn on ship. We take only the position of the ship. SpawnTemplate.units[UnitID].x = PointVec3.x --TX SpawnTemplate.units[UnitID].y = PointVec3.z --TY - SpawnTemplate.units[UnitID].alt = PointVec3.y - + SpawnTemplate.units[UnitID].alt = PointVec3.y else - self:T(RAT.id..string.format("RAT group %s spawning at airbase %s on parking spot id %d", self.alias, departure:GetName(), parkingindex[UnitID])) -- Get coordinates of parking spot. SpawnTemplate.units[UnitID].x = parkingspots[UnitID].x SpawnTemplate.units[UnitID].y = parkingspots[UnitID].z - SpawnTemplate.units[UnitID].alt = parkingspots[UnitID].y - + SpawnTemplate.units[UnitID].alt = parkingspots[UnitID].y end - else - + else self:T(RAT.id..string.format("RAT group %s spawning in air at %s.", self.alias, departure:GetName())) -- Spawn in air as requested initially. Original template orientation is perserved, altitude is already correctly set. SpawnTemplate.units[UnitID].x = TX SpawnTemplate.units[UnitID].y = TY - SpawnTemplate.units[UnitID].alt = PointVec3.y - + SpawnTemplate.units[UnitID].alt = PointVec3.y end -- Place marker at spawn position. @@ -5123,6 +5174,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take UnitTemplate.parking = parkingindex[UnitID] end + -- Debug info. self:T2(RAT.id..string.format("RAT group %s unit number %d: Parking = %s",self.alias, UnitID, tostring(UnitTemplate.parking))) self:T2(RAT.id..string.format("RAT group %s unit number %d: Parking ID = %s",self.alias, UnitID, tostring(UnitTemplate.parking_id))) @@ -5155,7 +5207,6 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take SpawnTemplate.CountryID=self.country end - self:T2('After Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) end -- Copy waypoints into spawntemplate. By this we avoid the nasty DCS "landing bug" :) diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 5dab11d52..3ac8ca74e 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -495,4 +495,100 @@ function UTILS.BeaufortScale(speed) bd="Hurricane" end return bn,bd +end + +--- Split string at seperators. C.f. http://stackoverflow.com/questions/1426954/split-string-in-lua +-- @param #string str Sting to split. +-- @param #string sep Speparator for split. +-- @return #table Split text. +function UTILS.Split(str, sep) + local result = {} + local regex = ("([^%s]+)"):format(sep) + for each in str:gmatch(regex) do + table.insert(result, each) + end + return result +end + +--- Convert time in seconds to hours, minutes and seconds. +-- @param #number seconds Time in seconds, e.g. from timer.getAbsTime() function. +-- @return #string Time in format Hours:Minutes:Seconds+Days (HH:MM:SS+D). +function UTILS.SecondsToClock(seconds) + + -- Nil check. + if seconds==nil then + return nil + end + + -- Seconds + local seconds = tonumber(seconds) + + -- Seconds of this day. + local _seconds=seconds%(60*60*24) + + if seconds <= 0 then + return nil + else + local hours = string.format("%02.f", math.floor(_seconds/3600)) + local mins = string.format("%02.f", math.floor(_seconds/60 - (hours*60))) + local secs = string.format("%02.f", math.floor(_seconds - hours*3600 - mins *60)) + local days = string.format("%d", seconds/(60*60*24)) + return hours..":"..mins..":"..secs.."+"..days + end +end + +--- Convert clock time from hours, minutes and seconds to seconds. +-- @param #string clock String of clock time. E.g., "06:12:35" or "5:1:30+1". Format is (H)H:(M)M:((S)S)(+D) H=Hours, M=Minutes, S=Seconds, D=Days. +-- @param #number Seconds. Corresponds to what you cet from timer.getAbsTime() function. +function UTILS.ClockToSeconds(clock) + + -- Nil check. + if clock==nil then + return nil + end + + -- Seconds init. + local seconds=0 + + -- Split additional days. + local dsplit=UTILS.split(clock, "+") + + -- Convert days to seconds. + if #dsplit>1 then + seconds=seconds+tonumber(dsplit[2])*60*60*24 + end + + -- Split hours, minutes, seconds + local tsplit=UTILS.Split(dsplit[1], ":") + + -- Get time in seconds + local i=1 + for _,time in ipairs(tsplit) do + if i==1 then + -- Hours + seconds=seconds+tonumber(time)*60*60 + elseif i==2 then + -- Minutes + seconds=seconds+tonumber(time)*60 + elseif i==3 then + -- Seconds + seconds=seconds+tonumber(time) + end + i=i+1 + end + + return seconds +end + +--- Display clock and mission time on screen as a message to all. +-- @param #number duration Duration in seconds how long the time is displayed. Default is 5 seconds. +function UTILS.DisplayMissionTime(duration) + duration=duration or 5 + local Tnow=timer.getAbsTime() + local mission_time=Tnow-timer.getTime0() + local mission_time_minutes=mission_time/60 + local mission_time_seconds=mission_time%60 + local local_time=UTILS.SecondsToClock(Tnow) + local text=string.format("Time: %s - %02d:%02d", local_time, mission_time_minutes, mission_time_seconds) + MESSAGE:New(text, duration):ToAll() end \ No newline at end of file diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 2b02fbd5b..143dbf368 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -339,10 +339,9 @@ function AIRBASE:GetZone() end --- Get all airbases of the current map. This includes ships and FARPS. --- @param #AIRBASE self -- @param DCS#Coalition coalition (Optional) Return only airbases belonging to the specified coalition. By default, all airbases of the map are returned. -- @return #table Table containing all airbase objects of the current map. -function AIRBASE:GetAllAirbases(coalition) +function AIRBASE.GetAllAirbases(coalition) local airbases={} for _,airbase in pairs(_DATABASE.AIRBASES) do @@ -402,7 +401,7 @@ function AIRBASE:GetParkingSpotsNumber(termtype) local nspots=0 for _,parkingspot in pairs(parkingdata) do - if self:_CheckTerminalType(parkingspot.Term_Type, termtype) then + if AIRBASE._CheckTerminalType(parkingspot.Term_Type, termtype) then nspots=nspots+1 end end @@ -423,7 +422,7 @@ function AIRBASE:GetFreeParkingSpotsNumber(termtype, allowTOAC) local nfree=0 for _,parkingspot in pairs(parkingdata) do -- Spots on runway are not counted unless explicitly requested. - if self:_CheckTerminalType(parkingspot.Term_Type, termtype) then + if AIRBASE._CheckTerminalType(parkingspot.Term_Type, termtype) then if (allowTOAC and allowTOAC==true) or parkingspot.TO_AC==false then nfree=nfree+1 end @@ -447,7 +446,7 @@ function AIRBASE:GetFreeParkingSpotsCoordinates(termtype, allowTOAC) local spots={} for _,parkingspot in pairs(parkingdata) do -- Coordinates on runway are not returned unless explicitly requested. - if self:_CheckTerminalType(parkingspot.Term_Type, termtype) then + if AIRBASE._CheckTerminalType(parkingspot.Term_Type, termtype) then if (allowTOAC and allowTOAC==true) or parkingspot.TO_AC==false then table.insert(spots, COORDINATE:NewFromVec3(parkingspot.vTerminalPos)) end @@ -471,7 +470,7 @@ function AIRBASE:GetParkingSpotsCoordinates(termtype) for _,parkingspot in pairs(parkingdata) do -- Coordinates on runway are not returned unless explicitly requested. - if self:_CheckTerminalType(parkingspot.Term_Type, termtype) then + if AIRBASE._CheckTerminalType(parkingspot.Term_Type, termtype) then -- Get coordinate from Vec3 terminal position. local _coord=COORDINATE:NewFromVec3(parkingspot.vTerminalPos) @@ -510,7 +509,7 @@ function AIRBASE:GetParkingSpotsTable(termtype) -- Put coordinates of parking spots into table. local spots={} for _,_spot in pairs(parkingdata) do - if self:_CheckTerminalType(_spot.Term_Type, termtype) then + if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) then local _free=_isfree(_spot) local _coord=COORDINATE:NewFromVec3(_spot.vTerminalPos) table.insert(spots, {Coordinate=_coord, TerminalID=_spot.Term_Index, TerminalType=_spot.Term_Type, TOAC=_spot.TO_AC, Free=_free, TerminalID0=_spot.Term_Index_0, DistToRwy=_spot.fDistToRW}) @@ -533,7 +532,7 @@ function AIRBASE:GetFreeParkingSpotsTable(termtype, allowTOAC) -- Put coordinates of free spots into table. local freespots={} for _,_spot in pairs(parkingfree) do - if self:_CheckTerminalType(_spot.Term_Type, termtype) then + if AIRBASE._CheckTerminalType(_spot.Term_Type, termtype) then if (allowTOAC and allowTOAC==true) or _spot.TO_AC==false then local _coord=COORDINATE:NewFromVec3(_spot.vTerminalPos) table.insert(freespots, {Coordinate=_coord, TerminalID=_spot.Term_Index, TerminalType=_spot.Term_Type, TOAC=_spot.TO_AC, Free=true, TerminalID0=_spot.Term_Index_0, DistToRwy=_spot.fDistToRW}) @@ -572,55 +571,8 @@ function AIRBASE:MarkParkingSpots(termtype) end end - ---- Helper function to check for the correct terminal type including "artificial" ones. --- @param #AIRBASE self --- @param #number Term_Type Termial type from getParking routine. --- @param #number termtype Terminal type from AIRBASE.TerminalType enumerator. --- @return True if terminal types match. -function AIRBASE:_CheckTerminalType(Term_Type, termtype) - - -- Nill check for Term_Type. - if Term_Type==nil then - return false - end - - -- If no terminal type is requested, we return true. BUT runways are excluded unless explicitly requested. - if termtype==nil then - if Term_Type==AIRBASE.TerminalType.Runway then - return false - else - return true - end - end - - -- Init no match. - local match=false - - -- Standar case. - if Term_Type==termtype then - match=true - end - - -- Artificial cases. Combination of terminal types. - if termtype==AIRBASE.TerminalType.OpenMedOrBig then - if Term_Type==AIRBASE.TerminalType.OpenMed or Term_Type==AIRBASE.TerminalType.OpenBig then - match=true - end - elseif termtype==AIRBASE.TerminalType.HelicopterUsable then - if Term_Type==AIRBASE.TerminalType.OpenMed or Term_Type==AIRBASE.TerminalType.OpenBig or Term_Type==AIRBASE.TerminalType.HelicopterOnly then - match=true - end - elseif termtype==AIRBASE.TerminalType.FighterAircraft then - if Term_Type==AIRBASE.TerminalType.OpenMed or Term_Type==AIRBASE.TerminalType.OpenBig or Term_Type==AIRBASE.TerminalType.Shelter then - match=true - end - end - - return match -end - ---- Seach an unoccupied parking spot at a specific airport and for a specific aircraft. +--- Seach unoccupied parking spots at the airbase for a specific group of aircraft. The routine also optionally checks for other unit, static and scenery options in a certain radius around the parking spot. +-- The dimension of the spawned aircraft and of the potential obstacle are taken into account. Note that the routine can only return so many spots that are free. -- @param #AIRBASE self -- @param Wrapper.Group#GROUP group Aircraft group for which the parking spots are requested. -- @param #number terminaltype (Optional) Only search spots at a specific terminal type. Default is all types execpt on runway. @@ -628,7 +580,7 @@ end -- @param #boolean scanunits (Optional) Scan for units as obstacles. Default true. -- @param #boolean scanstatics (Optional) Scan for statics as obstacles. Default true. -- @param #boolean scanscenery (Optional) Scan for scenery as obstacles. Default false. Can cause problems with e.g. shelters. --- @return #table Table of coordinates and terminal IDs of the parking spots or nil, if no valid parking spots for the whole group could be found. +-- @return #table Table of coordinates and terminal IDs of free parking spots. Each table entry has the elements .Coordinate and .TerminalID. function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, scanunits, scanstatics, scanscenery) -- Init default @@ -643,6 +595,9 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, scanscenery=false end + -- Mark all found obstacles on F10 map. + local markobstacles=false + -- Get the size of an object. local function _GetObjectSize(unit,mooseobject) if mooseobject then @@ -681,7 +636,7 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, local _aircraftsize, ax,ay,az=_GetObjectSize(aircraft, true) -- Debug info. - self:T(string.format("Looking for %d parking spot(s) at %s for aircraft of size %.1f m (x=%.1f,y=%.1f,z=%.1f) at termial type %s.", nspots, airport, _aircraftsize, ax,ay,az, tostring(terminaltype))) + self:E(string.format("Looking for %d parking spot(s) at %s for aircraft of size %.1f m (x=%.1f,y=%.1f,z=%.1f) at termial type %s.", nspots, airport, _aircraftsize, ax, ay, az, tostring(terminaltype))) -- Table of valid spots. local validspots={} @@ -691,74 +646,81 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, for _,parkingspot in pairs(parkingdata) do -- Check for requested terminal type if any. - if self:_CheckTerminalType(parkingspot.TerminalType, terminaltype) then - end + if AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) then - -- Coordinate of the parking spot. - local _spot=parkingspot.Coordinate --Core.Point#COORDINATE + -- Coordinate of the parking spot. + local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE + local _termid=parkingspot.TerminalID + + -- Scan a radius of 50 meters around the spot. + local _,_,_,_units,_statics,_sceneries=_spot:ScanObjects(scanradius, scanunits, scanstatics, scanscenery) + + -- Loop over objects within scan radius. + local occupied=false + + -- Check all units. + for _,unit in pairs(_units) do + + local _vec3=unit:getPoint() + local _coord=COORDINATE:NewFromVec3(_vec3) + local _dist=_coord:Get2DDistance(_spot) + local _safe=_overlap(aircraft, unit, _dist) - -- Scan a radius of 50 meters around the spot. - local _,_,_,_units,_statics,_sceneries=_spot:ScanObjects(scanradius) - - - -- Loop over objects within scan radius. - local occupied=false - - -- Check all units. - for _,unit in pairs(_units) do - - local _vec3=unit:getPoint() - local _coord=COORDINATE:NewFromVec3(_vec3) - local _dist=_coord:Get2DDistance(_spot) - local _safe=_overlap(aircraft, unit, _dist) - - --local l,x,y,z=_GetObjectSize(unit) - --_coord:MarkToAll(string.format("scan found unit %s\nx=%.2f y=%.2f z=%.2f\nl=%.2f d=%.2f safe=%s", unit:getName(), x, y, z, l, _dist, tostring(_safe))) - - if scanunits and not _safe then - occupied=true - end - end - - -- Check all statics. - for _,static in pairs(_statics) do - local _vec3=static:getPoint() - local _coord=COORDINATE:NewFromVec3(_vec3) - local _dist=_coord:Get2DDistance(_spot) - local _safe=_overlap(aircraft, static, _dist) - - --local l,x,y,z=_GetObjectSize(static) - --_coord:MarkToAll(string.format("scan found static %s\nx=%.2f y=%.2f z=%.2f\nl=%.2f d=%.2f safe=%s", static:getName(),x,y,z,l,_dist, tostring(_safe))) - - if scanstatics and not _safe then - occupied=true - end - end - - -- Check all scenery. - for _,scenery in pairs(_sceneries) do - local _vec3=scenery:getPoint() - local _coord=COORDINATE:NewFromVec3(_vec3) - local _dist=_coord:Get2DDistance(_spot) - local _safe=_overlap(aircraft, scenery, _dist) - - --local l,x,y,z=_GetObjectSize(scenery) - --_coord:MarkToAll(string.format("scan found scenery %s\nx=%.2f y=%.2f z=%.2f\nl=%.2f d=%.2f", scenery:getName(),x,y,z,l,_dist)) - - if scanscenery and not _safe then - occupied=true - end - end - - --_spot:MarkToAll(string.format("Parking spot %d free=%s", parkingspot.TerminalID, tostring(not occupied))) - if occupied then - self:T(string.format("%s: Parking spot id %d occupied.", airport, parkingspot.TerminalID)) - else - self:T(string.format("%s: Parking spot id %d free.", airport, parkingspot.TerminalID)) - if nvalid radius %.1f m. Despawn = %s.", self:GetName(), unit:GetName(), group:GetName(),_i, dist, radius, tostring(despawn))) + --unit:FlareGreen() + end + + end + else + self:T(string.format("%s, checking if unit %s of group %s is on runway. Unit is NOT alive.",self:GetName(), unit:GetName(), group:GetName())) + end + end + else + self:T(string.format("%s, checking if group %s is on runway. Group is NOT alive.",self:GetName(), group:GetName())) + end + + return false +end + +--- Helper function to check for the correct terminal type including "artificial" ones. +-- @param #number Term_Type Termial type from getParking routine. +-- @param #number termtype Terminal type from AIRBASE.TerminalType enumerator. +-- @return #boolean True if terminal types match. +function AIRBASE._CheckTerminalType(Term_Type, termtype) + + -- Nill check for Term_Type. + if Term_Type==nil then + return false + end + + -- If no terminal type is requested, we return true. BUT runways are excluded unless explicitly requested. + if termtype==nil then + if Term_Type==AIRBASE.TerminalType.Runway then + return false + else + return true + end + end + + -- Init no match. + local match=false + + -- Standar case. + if Term_Type==termtype then + match=true + end + + -- Artificial cases. Combination of terminal types. + if termtype==AIRBASE.TerminalType.OpenMedOrBig then + if Term_Type==AIRBASE.TerminalType.OpenMed or Term_Type==AIRBASE.TerminalType.OpenBig then + match=true + end + elseif termtype==AIRBASE.TerminalType.HelicopterUsable then + if Term_Type==AIRBASE.TerminalType.OpenMed or Term_Type==AIRBASE.TerminalType.OpenBig or Term_Type==AIRBASE.TerminalType.HelicopterOnly then + match=true + end + elseif termtype==AIRBASE.TerminalType.FighterAircraft then + if Term_Type==AIRBASE.TerminalType.OpenMed or Term_Type==AIRBASE.TerminalType.OpenBig or Term_Type==AIRBASE.TerminalType.Shelter then + match=true + end + end + + return match +end \ No newline at end of file From 169dcfe3f6dcb1e32b93cb215eeabfc0a96782a5 Mon Sep 17 00:00:00 2001 From: Jonathan Toppins Date: Thu, 28 Jun 2018 00:17:26 -0400 Subject: [PATCH 191/420] a2a-dispatcher: add check for not in air in OnEventEngineShutdown This fixes the problem of planes owned by gcicap would immediately despawn when shot causing their engine to quit. While not ultimately satisfying as planes shot while taxiing will still immediately despawn. One is now able to see their air victories in all their glory, including the fiery crash into the ground. Bug: 932 Signed-off-by: Jonathan Toppins --- Moose Development/Moose/AI/AI_A2A_Dispatcher.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index d8018b2fe..a85366d77 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -1049,7 +1049,8 @@ do -- AI_A2A_DISPATCHER if Squadron then self:F( { SquadronName = Squadron.Name } ) local LandingMethod = self:GetSquadronLanding( Squadron.Name ) - if LandingMethod == AI_A2A_DISPATCHER.Landing.AtEngineShutdown then + if LandingMethod == AI_A2A_DISPATCHER.Landing.AtEngineShutdown and + not DefenderUnit:InAir() then local DefenderSize = Defender:GetSize() if DefenderSize == 1 then self:RemoveDefenderFromSquadron( Squadron, Defender ) From c952f134d86574c63a0aa2e6b286a8739a39ee05 Mon Sep 17 00:00:00 2001 From: Jonathan Toppins Date: Wed, 27 Jun 2018 21:32:11 -0400 Subject: [PATCH 192/420] a2a-dispatcher: remove fuel check in OnEventLand() handler This effectivally reverts the change made in commit ea96a5e0a38b ("Planes remaining at airfield fixed") to declutter airbases of aircraft which land at incorrect bases. The problem with the logic is an RTB command will not be issued until the fuel threshold is reached. Therefore the check will always be true resulting the the unit always being deleted. Bug: 875 Signed-off-by: Jonathan Toppins --- Moose Development/Moose/AI/AI_A2A_Dispatcher.lua | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index d8018b2fe..1bb841b15 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -1033,10 +1033,6 @@ do -- AI_A2A_DISPATCHER DefenderUnit:Destroy() return end - if DefenderUnit:GetFuel() <= self.DefenderDefault.FuelThreshold then - DefenderUnit:Destroy() - return - end end end From d46da07f61ce2163c41517c485279f66214adf3c Mon Sep 17 00:00:00 2001 From: funkyfranky <> Date: Thu, 28 Jun 2018 20:59:09 +0200 Subject: [PATCH 193/420] Improvements RAT, SPAWAN, AIRBASE SPAWN: - Added options to find parking spots function. RAT: - Added user functions for parking. - Added return of self for user functions. - Added new check on runway function and removed old version. - Removed old RAT parking spot DB. AIRBASE: - Added verysafe option for find free parking function. - Other minor changes. --- Moose Development/Moose/Core/Spawn.lua | 23 +- Moose Development/Moose/Functional/RAT.lua | 628 +++++++++++--------- Moose Development/Moose/Wrapper/Airbase.lua | 127 ++-- 3 files changed, 441 insertions(+), 337 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index ec7e7414d..67d1cf753 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1339,6 +1339,13 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT termtype=AIRBASE.TerminalType.Runway end + -- Scan options. Might make that input somehow. + local scanradius=50 + local scanunits=true + local scanstatics=true + local scanscenery=false + local verysafe=false + -- Number of free parking spots at the airbase. if spawnonship or spawnonfarp or spawnonrunway then -- These places work procedural and have some kind of build in queue ==> Less effort. @@ -1350,18 +1357,18 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT if termtype==nil then -- Helo is spawned. Try exclusive helo spots first. self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterOnly)) - spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly) + spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe) nfree=#spots if nfree<#SpawnTemplate.units then -- Not enough helo ports. Let's try also other terminal types. self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterUsable)) - spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterUsable) + spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterUsable, scanradius, scanunits, scanstatics, scanscenery, verysafe) nfree=#spots end else -- No terminal type specified. We try all spots except shelters. self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), termtype)) - spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype) + spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe) nfree=#spots end else @@ -1373,23 +1380,23 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT if isbomber or istransport then -- First we fill the potentially bigger spots. self:T(string.format("Transport/bomber group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.OpenBig)) - spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenBig) + spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenBig, scanradius, scanunits, scanstatics, scanscenery, verysafe) nfree=#spots if nfree<#SpawnTemplate.units then -- Now we try the smaller ones. self:T(string.format("Transport/bomber group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.OpenMedOrBig)) - spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenMedOrBig) + spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenMedOrBig, scanradius, scanunits, scanstatics, scanscenery, verysafe) nfree=#spots end else self:T(string.format("Fighter group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.FighterAircraft)) - spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.FighterAircraft) + spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.FighterAircraft, scanradius, scanunits, scanstatics, scanscenery, verysafe) nfree=#spots end else -- Terminal type explicitly given. self:T(string.format("Plane group %s is at %s using terminal type %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), tostring(termtype))) - spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype) + spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe) nfree=#spots end end @@ -1527,7 +1534,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT -- Check if we accidentally spawned on the runway. Needs to be schedules, because group is not immidiately alive. if Takeoff~=SPAWN.Takeoff.Runway and Takeoff~=SPAWN.Takeoff.Air and spawnonairport then - SCHEDULER:New(nil, AIRBASE.CheckOnRunWay, {SpawnAirbase, GroupSpawned, 25, true} , 1.0) + SCHEDULER:New(nil, AIRBASE.CheckOnRunWay, {SpawnAirbase, GroupSpawned, 75, true} , 1.0) end return GroupSpawned diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index a3c54a281..081106680 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -20,7 +20,7 @@ -- * When a unit is removed a new unit with a different flight plan is respawned. -- * Aircraft can report their status during the route. -- * All of the above can be customized by the user if necessary. --- * All current (Caucasus, Nevada, Normandy) and future maps are supported. +-- * All current (Caucasus, Nevada, Normandy, Persian Gulf) and future maps are supported. -- -- The RAT class creates an entry in the F10 radio menu which allows to -- @@ -62,7 +62,7 @@ -- @type RAT -- @field #string ClassName Name of the Class. -- @field #boolean Debug Turn debug messages on or off. --- @field Core.Group#GROUP templategroup Group serving as template for the RAT aircraft. +-- @field Wrapper.Group#GROUP templategroup Group serving as template for the RAT aircraft. -- @field #string alias Alias for spawned group. -- @field #boolean spawninitialized If RAT:Spawn() was already called this RAT object is set to true to prevent users to call it again. -- @field #number spawndelay Delay time in seconds before first spawning happens. @@ -111,6 +111,8 @@ -- @field #number FLminuser Minimum flight level set by user. -- @field #number FLmaxuser Maximum flight level set by user. -- @field #boolean commute Aircraft commute between departure and destination, i.e. when respawned the departure airport becomes the new destiation. +-- @field #boolean starshape If true, aircraft travel A-->B-->A-->C-->A-->D... for commute. +-- @field #string homebase Home base for commute and return zone. Aircraft will always return to this base but otherwise travel in a star shaped way. -- @field #boolean continuejourney Aircraft will continue their journey, i.e. get respawned at their destination with a new random destination. -- @field #number ngroups Number of groups to be spawned in total. -- @field #number alive Number of groups which are alive. @@ -143,12 +145,14 @@ -- @field #string onboardnum Sets the onboard number prefix. Same as setting "TAIL #" in the mission editor. -- @field #number onboardnum0 (Optional) Starting value of the automatically appended numbering of aircraft within a flight. Default is 1. -- @field #boolean checkonrunway Aircraft are checked if they were accidentally spawned on the runway. Default is true. +-- @field #number onrunwayradius Distance (in meters) from a runway spawn point until a unit is considered to have accidentally been spawned on a runway. Default is 75 m. +-- @field #number onrunwaymaxretry Number of respawn retries (on ground) at other airports if a group gets accidentally spawned on the runway. Default is 3. -- @field #boolean checkontop Aircraft are checked if they were accidentally spawned on top of another unit. Default is true. --- @field #number rbug_maxretry Number of respawn retries (on ground) at other airports if a group gets accidentally spawned on the runway. Default is 3. --- @field #boolean useparkingdb Parking spots are added to data base once an aircraft has used it. These spots can later be used by other aircraft. Default is true. --- @field #number termtype Type of terminal to be used when spawning at an airbase. --- @field #boolean starshape If true, aircraft travel A-->B-->A-->C-->A-->D... for commute. --- @field #string homebase Home base for commute and return zone. Aircraft will always return to this base but otherwise travel in a star shaped way. +-- @field #number ontopradius Radius in meters until which a unit is considered to be on top of another. Default is 2 m. +-- @field Wrapper.Airbase#AIRBASE.TerminalType termtype Type of terminal to be used when spawning at an airbase. +-- @field #number parkingscanradius Radius in meters until which parking spots are scanned for obstacles like other units, statics or scenery. +-- @field #boolean parkingscanscenery If true, area around parking spots is scanned for scenery objects. Default is false. +-- @field #boolean parkingverysafe If true, parking spots are considered as non-free until a possible aircraft has left and taken off. Default false. -- @extends Core.Spawn#SPAWN --- Implements an easy to use way to randomly fill your map with AI aircraft. @@ -346,6 +350,8 @@ RAT={ FLmaxuser=nil, -- Maximum flight level set by user. FLuser=nil, -- Flight level set by users explicitly. commute=false, -- Aircraft commute between departure and destination, i.e. when respawned the departure airport becomes the new destiation. + starshape=false, -- If true, aircraft travel A-->B-->A-->C-->A-->D... for commute. + homebase=nil, -- Home base for commute. continuejourney=false, -- Aircraft will continue their journey, i.e. get respawned at their destination with a new random destination. alive=0, -- Number of groups which are alive. ngroups=nil, -- Number of groups to be spawned in total. @@ -378,13 +384,15 @@ RAT={ activate_max=1, -- Max number of uncontrolle aircraft, which will be activated at a time. onboardnum=nil, -- Tail number. onboardnum0=1, -- (Optional) Starting value of the automatically appended numbering of aircraft within a flight. Default is one. - rbug_maxretry=3, -- Number of respawn retries (on ground) at other airports if a group gets accidentally spawned on the runway. checkonrunway=true, -- Check whether aircraft have been spawned on the runway. + onrunwayradius=75, -- Distance from a runway spawn point until a unit is considered to have accidentally been spawned on a runway. + onrunwaymaxretry=3, -- Number of respawn retries (on ground) at other airports if a group gets accidentally spawned on the runway. checkontop=false, -- Check whether aircraft have been spawned on top of another unit. - useparkingdb=false, -- Put known parking spots into a data base. + ontopradius=2, -- Radius in meters until which a unit is considered to be on top of another. termtype=nil, -- Terminal type. - starshape=false, -- If true, aircraft travel A-->B-->A-->C-->A-->D... for commute. - homebase=nil, -- Home base for commute and return zone. + parkingscanradius=50, -- Scan radius. + parkingscanscenery=false, -- Scan parking spots for scenery obstacles. + parkingverysafe=false, -- Very safe option. } ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -496,10 +504,6 @@ RAT.markerid=0 -- @field #string MenuF10 RAT.MenuF10=nil ---- RAT parking spots data base. --- @list parking -RAT.parking={} - --- Some ID to identify who we are in output of the DCS.log file. -- @field #string id RAT.id="RAT | " @@ -511,7 +515,6 @@ RAT.version={ print = true, } - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --TODO list: @@ -548,14 +551,15 @@ RAT.version={ --DONE: Find way to respawn aircraft at same position where the last was despawned for commute and journey. --TODO: Check that same alias is not given twice. Need to store previous ones and compare. +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Constructor New ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Create a new RAT object. -- @param #RAT self -- @param #string groupname Name of the group as defined in the mission editor. This group is serving as a template for all spawned units. -- @param #string alias (Optional) Alias of the group. This is and optional parameter but must(!) be used if the same template group is used for more than one RAT object. --- @return #RAT Object of RAT class. --- @return #nil If the group does not exist in the mission editor. +-- @return #RAT Object of RAT class or nil if the group does not exist in the mission editor. -- @usage yak1:RAT("RAT_YAK") will create a RAT object called "yak1". The template group in the mission editor must have the name "RAT_YAK". -- @usage yak2:RAT("RAT_YAK", "Yak2") will create a RAT object "yak2". The template group in the mission editor must have the name "RAT_YAK" but the group will be called "Yak2" in e.g. the F10 menu. function RAT:New(groupname, alias) @@ -606,11 +610,14 @@ function RAT:New(groupname, alias) return self end +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Spawn function ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Triggers the spawning of AI aircraft. Note that all additional options should be set before giving the spawn command. -- @param #RAT self -- @param #number naircraft (Optional) Number of aircraft to spawn. Default is one aircraft. +-- @return #boolean True if spawning was successful or nil if nothing was spawned. -- @usage yak:Spawn(5) will spawn five aircraft. By default aircraft will spawn at neutral and red airports if the template group is part of the red coaliton. function RAT:Spawn(naircraft) @@ -737,9 +744,8 @@ function RAT:Spawn(naircraft) text=text..string.format("Radio modulation : %s\n", tostring(self.frequency)) text=text..string.format("Tail # prefix : %s\n", tostring(self.onboardnum)) text=text..string.format("Check on runway: %s\n", tostring(self.checkonrunway)) + text=text..string.format("Max respawn attempts: %s\n", tostring(self.onrunwaymaxretry)) text=text..string.format("Check on top: %s\n", tostring(self.checkontop)) - text=text..string.format("Max respawn attempts: %s\n", tostring(self.rbug_maxretry)) - text=text..string.format("Parking DB: %s\n", tostring(self.useparkingdb)) text=text..string.format("Uncontrolled: %s\n", tostring(self.uncontrolled)) if self.uncontrolled and self.activate_uncontrolled then text=text..string.format("Uncontrolled max : %4.1f\n", self.activate_max) @@ -799,8 +805,12 @@ function RAT:Spawn(naircraft) if self.uncontrolled and self.activate_uncontrolled then SCHEDULER:New(nil, self._ActivateUncontrolled, {self}, self.activate_delay, self.activate_delta, self.activate_frand) end + + return true end +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Consistency Check ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Function checks consistency of user input and automatically adjusts parameters if necessary. @@ -916,10 +926,15 @@ function RAT:_CheckConsistency() end end +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- User functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + --- Set the friendly coalitions from which the airports can be used as departure and destination. -- @param #RAT self -- @param #string friendly "same"=own coalition+neutral (default), "sameonly"=own coalition only, "neutral"=all neutral airports. -- Default is "same", so aircraft will use airports of the coalition their spawn template has plus all neutral airports. +-- @return #RAT RAT self object. -- @usage yak:SetCoalition("neutral") will spawn aircraft randomly on all neutral airports. -- @usage yak:SetCoalition("sameonly") will spawn aircraft randomly on airports belonging to the same coalition only as the template. function RAT:SetCoalition(friendly) @@ -931,11 +946,21 @@ function RAT:SetCoalition(friendly) else self.friendly=RAT.coal.same end + return self end --- Set coalition of RAT group. You can make red templates blue and vice versa. +-- Note that a country is also set automatically if it has not done before via RAT:SetCountry. +-- +-- * For blue, the country is set to USA. +-- * For red, the country is set to RUSSIA. +-- * For neutral, the country is set to SWITZERLAND. +-- +-- This is important, since it is ultimately the COUNTRY that determines the coalition of the aircraft. +-- You can set the country explicitly via the RAT:SetCountry() function if necessary. -- @param #RAT self --- @param #string color Color of coalition, i.e. "red" or blue". +-- @param #string color Color of coalition, i.e. "red" or blue" or "neutral". +-- @return #RAT RAT self object. function RAT:SetCoalitionAircraft(color) self:F2(color) if color:lower()=="blue" then @@ -950,29 +975,87 @@ function RAT:SetCoalitionAircraft(color) end elseif color:lower()=="neutral" then self.coalition=coalition.side.NEUTRAL + if not self.country then + self.country=country.id.SWITZERLAND + end end + return self end ---- Set country of RAT group. This overrules the coalition settings. +--- Set country of RAT group. +-- See https://wiki.hoggitworld.com/view/DCS_enum_country +-- +-- This overrules the coalition settings. So if you want your group to be of a specific coalition, you have to set a country that is part of that coalition. -- @param #RAT self --- @param #number id DCS country enumerator ID. For example country.id.USA or country.id.RUSSIA. +-- @param #DCS.country.id id DCS country enumerator ID. For example country.id.USA or country.id.RUSSIA. +-- @return #RAT RAT self object. function RAT:SetCountry(id) self:F2(id) self.country=id + return self end --- Set the terminal type the aircraft use when spawning at an airbase. Cf. https://wiki.hoggitworld.com/view/DCS_func_getParking -- @param #RAT self --- @param #number termtype Type of terminal. Use enumerator AIRBASE.TerminalType.XXX or check https://wiki.hoggitworld.com/view/DCS_func_getParking for valid numbers. +-- @param Wrapper.Airbase#AIRBASE.TerminalType termtype Type of terminal. Use enumerator AIRBASE.TerminalType.XXX. +-- @return #RAT RAT self object. function RAT:SetTerminalType(termtype) self:F2(termtype) self.termtype=termtype + return self +end + +--- Set the scan radius around parking spots. Parking spot is considered to be occupied if any obstacle is found with the radius. +-- @param #RAT self +-- @param #number radius Radius in meters. Default 50 m. +-- @return #RAT RAT self object. +function RAT:SetParkingScanRadius(radius) + self:F2(radius) + self.parkingscanradius=radius or 50 + return self +end + +--- Enables scanning for scenery objects around parking spots which might block the spot. +-- @param #RAT self +-- @return #RAT RAT self object. +function RAT:SetParkingScanSceneryON() + self:F2() + self.parkingscanscenery=true + return self +end + +--- Disables scanning for scenery objects around parking spots which might block the spot. This is also the default setting. +-- @param #RAT self +-- @return #RAT RAT self object. +function RAT:SetParkingScanSceneryOFF() + self:F2() + self.parkingscanscenery=false + return self +end + +--- A parking spot is not free until a possible aircraft has left and taken off. +-- @param #RAT self +-- @return #RAT RAT self object. +function RAT:SetParkingSpotSafeON() + self:F2() + self.parkingverysafe=true + return self +end + +--- A parking spot is free as soon as possible aircraft has left the place. This is the default. +-- @param #RAT self +-- @return #RAT RAT self object. +function RAT:SetParkingSpotSafeOFF() + self:F2() + self.parkingverysafe=false + return self end --- Set takeoff type. Starting cold at airport, starting hot at airport, starting at runway, starting in the air. -- Default is "takeoff-coldorhot". So there is a 50% chance that the aircraft starts with cold engines and 50% that it starts with hot engines. -- @param #RAT self -- @param #string type Type can be "takeoff-cold" or "cold", "takeoff-hot" or "hot", "takeoff-runway" or "runway", "air". +-- @return #RAT RAT self object. -- @usage RAT:Takeoff("hot") will spawn RAT objects at airports with engines started. -- @usage RAT:Takeoff("cold") will spawn RAT objects at airports with engines off. -- @usage RAT:Takeoff("air") will spawn RAT objects in air over random airports or within pre-defined zones. @@ -993,41 +1076,54 @@ function RAT:SetTakeoff(type) end self.takeoff=_Type + + return self end --- Set takeoff type cold. Aircraft will spawn at a parking spot with engines off. -- @param #RAT self +-- @return #RAT RAT self object. function RAT:SetTakeoffCold() self.takeoff=RAT.wp.cold + return self end --- Set takeoff type to hot. Aircraft will spawn at a parking spot with engines on. -- @param #RAT self +-- @return #RAT RAT self object. function RAT:SetTakeoffHot() self.takeoff=RAT.wp.hot + return self end --- Set takeoff type to runway. Aircraft will spawn directly on the runway. -- @param #RAT self +-- @return #RAT RAT self object. function RAT:SetTakeoffRunway() self.takeoff=RAT.wp.runway + return self end --- Set takeoff type to cold or hot. Aircraft will spawn at a parking spot with 50:50 change of engines on or off. -- @param #RAT self +-- @return #RAT RAT self object. function RAT:SetTakeoffColdOrHot() self.takeoff=RAT.wp.coldorhot + return self end --- Set takeoff type to air. Aircraft will spawn in the air. -- @param #RAT self +-- @return #RAT RAT self object. function RAT:SetTakeoffAir() self.takeoff=RAT.wp.air + return self end --- Set possible departure ports. This can be an airport or a zone defined in the mission editor. -- @param #RAT self -- @param #string departurenames Name or table of names of departure airports or zones. +-- @return #RAT RAT self object. -- @usage RAT:SetDeparture("Sochi-Adler") will spawn RAT objects at Sochi-Adler airport. -- @usage RAT:SetDeparture({"Sochi-Adler", "Gudauta"}) will spawn RAT aircraft radomly at Sochi-Adler or Gudauta airport. -- @usage RAT:SetDeparture({"Zone A", "Gudauta"}) will spawn RAT aircraft in air randomly within Zone A, which has to be defined in the mission editor, or within a zone around Gudauta airport. Note that this also requires RAT:takeoff("air") to be set. @@ -1063,11 +1159,13 @@ function RAT:SetDeparture(departurenames) end + return self end --- Set name of destination airports or zones for the AI aircraft. -- @param #RAT self -- @param #string destinationnames Name of the destination airport or table of destination airports. +-- @return #RAT RAT self object. -- @usage RAT:SetDestination("Krymsk") makes all aircraft of this RAT oject fly to Krymsk airport. function RAT:SetDestination(destinationnames) self:F2(destinationnames) @@ -1101,10 +1199,12 @@ function RAT:SetDestination(destinationnames) end + return self end --- Destinations are treated as zones. Aircraft will not land but rather be despawned when they reach a random point in the zone. -- @param #RAT self +-- @return #RAT RAT self object. function RAT:DestinationZone() self:F2() @@ -1113,20 +1213,25 @@ function RAT:DestinationZone() -- Landing type is "air" because we don't actually land at the airport. self.landing=RAT.wp.air + + return self end --- Aircraft will fly to a random point within a zone and then return to its departure airport or zone. -- @param #RAT self +-- @return #RAT RAT self object. function RAT:ReturnZone() self:F2() -- Destination is a zone. Needs special care. self.returnzone=true + return self end --- Include all airports which lie in a zone as possible destinations. -- @param #RAT self -- @param Core.Zone#ZONE zone Zone in which the departure airports lie. Has to be a MOOSE zone. +-- @return #RAT RAT self object. function RAT:SetDestinationsFromZone(zone) self:F2(zone) @@ -1135,11 +1240,14 @@ function RAT:SetDestinationsFromZone(zone) -- Set zone. self.destination_Azone=zone + + return self end --- Include all airports which lie in a zone as possible destinations. -- @param #RAT self -- @param Core.Zone#ZONE zone Zone in which the destination airports lie. Has to be a MOOSE zone. +-- @return #RAT RAT self object. function RAT:SetDeparturesFromZone(zone) self:F2(zone) @@ -1148,25 +1256,32 @@ function RAT:SetDeparturesFromZone(zone) -- Set zone. self.departure_Azone=zone + + return self end --- Add all friendly airports to the list of possible departures. -- @param #RAT self +-- @return #RAT RAT self object. function RAT:AddFriendlyAirportsToDepartures() self:F2() self.addfriendlydepartures=true + return self end --- Add all friendly airports to the list of possible destinations -- @param #RAT self +-- @return #RAT RAT self object. function RAT:AddFriendlyAirportsToDestinations() self:F2() self.addfriendlydestinations=true + return self end --- Airports, FARPs and ships explicitly excluded as departures and destinations. -- @param #RAT self -- @param #string ports Name or table of names of excluded airports. +-- @return #RAT RAT self object. function RAT:ExcludedAirports(ports) self:F2(ports) if type(ports)=="string" then @@ -1174,11 +1289,13 @@ function RAT:ExcludedAirports(ports) else self.excluded_ports=ports end + return self end --- Set skill of AI aircraft. Default is "High". -- @param #RAT self -- @param #string skill Skill, options are "Average", "Good", "High", "Excellent" and "Random". Parameter is case insensitive. +-- @return #RAT RAT self object. function RAT:SetAISkill(skill) self:F2(skill) if skill:lower()=="average" then @@ -1192,11 +1309,13 @@ function RAT:SetAISkill(skill) else self.skill="High" end + return self end --- Set livery of aircraft. If more than one livery is specified in a table, the actually used one is chosen randomly from the selection. -- @param #RAT self -- @param #table skins Name of livery or table of names of liveries. +-- @return #RAT RAT self object. function RAT:Livery(skins) self:F2(skins) if type(skins)=="string" then @@ -1204,28 +1323,34 @@ function RAT:Livery(skins) else self.livery=skins end + return self end --- Change aircraft type. This is a dirty hack which allows to change the aircraft type of the template group. -- Note that all parameters like cruise speed, climb rate, range etc are still taken from the template group which likely leads to strange behaviour. -- @param #RAT self -- @param #string actype Type of aircraft which is spawned independent of the template group. Use with care and expect problems! +-- @return #RAT RAT self object. function RAT:ChangeAircraft(actype) self:F2(actype) self.actype=actype + return self end --- Aircraft will continue their journey from their destination. This means they are respawned at their destination and get a new random destination. -- @param #RAT self +-- @return #RAT RAT self object. function RAT:ContinueJourney() self:F2() self.continuejourney=true self.commute=false + return self end --- Aircraft will commute between their departure and destination airports or zones. -- @param #RAT self -- @param #boolean starshape If true, keep homebase, i.e. travel A-->B-->A-->C-->A-->D... instead of A-->B-->A-->B-->A... +-- @return #RAT RAT self object. function RAT:Commute(starshape) self:F2() self.commute=true @@ -1235,188 +1360,248 @@ function RAT:Commute(starshape) else self.starshape=false end + return self end --- Set the delay before first group is spawned. -- @param #RAT self -- @param #number delay Delay in seconds. Default is 5 seconds. Minimum delay is 0.5 seconds. +-- @return #RAT RAT self object. function RAT:SetSpawnDelay(delay) self:F2(delay) delay=delay or 5 self.spawndelay=math.max(0.5, delay) + return self end --- Set the interval between spawnings of the template group. -- @param #RAT self -- @param #number interval Interval in seconds. Default is 5 seconds. Minimum is 0.5 seconds. +-- @return #RAT RAT self object. function RAT:SetSpawnInterval(interval) self:F2(interval) interval=interval or 5 self.spawninterval=math.max(0.5, interval) + return self end --- Make aircraft respawn the moment they land rather than at engine shut down. -- @param #RAT self -- @param #number delay (Optional) Delay in seconds until respawn happens after landing. Default is 180 seconds. Minimum is 1.0 seconds. +-- @return #RAT RAT self object. function RAT:RespawnAfterLanding(delay) self:F2(delay) delay = delay or 180 self.respawn_at_landing=true delay=math.max(1.0, delay) self.respawn_delay=delay + return self end --- Sets the delay between despawning and respawning aircraft. -- @param #RAT self -- @param #number delay Delay in seconds until respawn happens. Default is 1 second. Minimum is 1 second. +-- @return #RAT RAT self object. function RAT:SetRespawnDelay(delay) self:F2(delay) delay = delay or 1.0 delay=math.max(1.0, delay) self.respawn_delay=delay + return self end --- Aircraft will not get respawned when they finished their route. -- @param #RAT self +-- @return #RAT RAT self object. function RAT:NoRespawn() self:F2() self.norespawn=true + return self end --- Number of tries to respawn an aircraft in case it has accitentally been spawned on runway. -- @param #RAT self -- @param #number n Number of retries. Default is 3. +-- @return #RAT RAT self object. function RAT:SetMaxRespawnTriedWhenSpawnedOnRunway(n) self:F2(n) n=n or 3 - self.rbug_maxretry=n + self.onrunwaymaxretry=n + return self end --- Aircraft will be respawned directly after take-off. -- @param #RAT self +-- @return #RAT RAT self object. function RAT:RespawnAfterTakeoff() self:F2() self.respawn_after_takeoff=true + return self end --- Aircraft will be respawned after they crashed or get shot down. This is the default behavior. -- @param #RAT self +-- @return #RAT RAT self object. function RAT:RespawnAfterCrashON() self:F2() self.respawn_after_crash=true + return self end --- Aircraft will not be respawned after they crashed or get shot down. -- @param #RAT self +-- @return #RAT RAT self object. function RAT:RespawnAfterCrashOFF() self:F2() self.respawn_after_crash=false + return self end --- If aircraft cannot be spawned on parking spots, it is allowed to spawn them in air above the same airport. Note that this is also the default behavior. -- @param #RAT self +-- @return #RAT RAT self object. function RAT:RespawnInAirAllowed() self:F2() self.respawn_inair=true + return self end --- If aircraft cannot be spawned on parking spots, it is NOT allowed to spawn them in air. This has only impact if aircraft are supposed to be spawned on the ground (and not in a zone). -- @param #RAT self +-- @return #RAT RAT self object. function RAT:RespawnInAirNotAllowed() self:F2() self.respawn_inair=false + return self end --- Check if aircraft have accidentally been spawned on the runway. If so they will be removed immediatly. -- @param #RAT self -- @param #boolean switch If true, check is performed. If false, this check is omitted. -function RAT:CheckOnRunway(switch) +-- @param #number radius Distance in meters until a unit is considered to have spawned accidentally on the runway. Default is 75 m. +-- @return #RAT RAT self object. +function RAT:CheckOnRunway(switch, distance) self:F2(switch) if switch==nil then switch=true end self.checkonrunway=switch + self.onrunwayradius=distance or 75 + return self end --- Check if aircraft have accidentally been spawned on top of each other. If yes, they will be removed immediately. -- @param #RAT self -- @param #boolean switch If true, check is performed. If false, this check is omitted. -function RAT:CheckOnTop(switch) +-- @param #number radius Radius in meters until which a unit is considered to be on top of each other. Default is 2 m. +-- @return #RAT RAT self object. +function RAT:CheckOnTop(switch, radius) self:F2(switch) if switch==nil then switch=true end self.checkontop=switch + self.ontopradius=radius or 2 + return self end ---- Put parking spot coordinates in a data base for future use of aircraft. +--- Put parking spot coordinates in a data base for future use of aircraft. (Obsolete! API function will be removed soon.) -- @param #RAT self -- @param #boolean switch If true, parking spots are memorized. This is also the default setting. +-- @return #RAT RAT self object. function RAT:ParkingSpotDB(switch) - self:F2(switch) - if switch==nil then - switch=true - end - self.useparkingdb=switch + self:E("RAT ParkingSpotDB function is obsolete and will be removed soon!") + return self end --- Enable Radio. Overrules the ME setting. -- @param #RAT self +-- @return #RAT RAT self object. function RAT:RadioON() self:F2() self.radio=true + return self end --- Disable Radio. Overrules the ME setting. -- @param #RAT self +-- @return #RAT RAT self object. function RAT:RadioOFF() self:F2() self.radio=false + return self end --- Set radio frequency. -- @param #RAT self -- @param #number frequency Radio frequency. +-- @return #RAT RAT self object. function RAT:RadioFrequency(frequency) self:F2(frequency) self.frequency=frequency + return self end ---- Spawn aircraft in uncontrolled state. Aircraft will only sit at their parking spots. They can be activated randomly by the RAT:ActivateUncontrolled() function. +--- Set radio modulation. Default is AM. -- @param #RAT self -function RAT:Uncontrolled() - self:F2() - self.uncontrolled=true -end - ---- Aircraft are invisible. --- @param #RAT self -function RAT:Invisible() - self:F2() - self.invisible=true -end - ---- Aircraft are immortal. --- @param #RAT self -function RAT:Immortal() - self:F2() - self.immortal=true +-- @param #string modulation Either "FM" or "AM". If no value is given, modulation is set to AM. +-- @return #RAT RAT self object. +function RAT:RadioModulation(modulation) + self:F2(modulation) + if modulation=="AM" then + self.modulation=radio.modulation.AM + elseif modulation=="FM" then + self.modulation=radio.modulation.FM + else + self.modulation=radio.modulation.AM + end + return self end --- Radio menu On. Default is off. -- @param #RAT self +-- @return #RAT RAT self object. function RAT:RadioMenuON() self:F2() self.f10menu=true + return self end --- Radio menu Off. This is the default setting. -- @param #RAT self +-- @return #RAT RAT self object. function RAT:RadioMenuOFF() self:F2() self.f10menu=false + return self end +--- Aircraft are invisible. +-- @param #RAT self +-- @return #RAT RAT self object. +function RAT:Invisible() + self:F2() + self.invisible=true + return self +end + +--- Aircraft are immortal. +-- @param #RAT self +-- @return #RAT RAT self object. +function RAT:Immortal() + self:F2() + self.immortal=true + return self +end + +--- Spawn aircraft in uncontrolled state. Aircraft will only sit at their parking spots. They can be activated randomly by the RAT:ActivateUncontrolled() function. +-- @param #RAT self +-- @return #RAT RAT self object. +function RAT:Uncontrolled() + self:F2() + self.uncontrolled=true + return self +end --- Activate uncontrolled aircraft. -- @param #RAT self @@ -1424,6 +1609,7 @@ end -- @param #number delay Time delay in seconds before (first) aircraft is activated. Default is 1 second. -- @param #number delta Time difference in seconds before next aircraft is activated. Default is 1 second. -- @param #number frand Factor [0,...,1] for randomization of time difference between aircraft activations. Default is 0, i.e. no randomization. +-- @return #RAT RAT self object. function RAT:ActivateUncontrolled(maxactivated, delay, delta, frand) self:F2({max=maxactivated, delay=delay, delta=delta, rand=frand}) @@ -1442,66 +1628,63 @@ function RAT:ActivateUncontrolled(maxactivated, delay, delta, frand) -- Ensure frand is in [0,...,1] self.activate_frand=math.max(self.activate_frand,0) self.activate_frand=math.min(self.activate_frand,1) -end - ---- Set radio modulation. Default is AM. --- @param #RAT self --- @param #string modulation Either "FM" or "AM". If no value is given, modulation is set to AM. -function RAT:RadioModulation(modulation) - self:F2(modulation) - if modulation=="AM" then - self.modulation=radio.modulation.AM - elseif modulation=="FM" then - self.modulation=radio.modulation.FM - else - self.modulation=radio.modulation.AM - end + + return self end --- Set the time after which inactive groups will be destroyed. -- @param #RAT self -- @param #number time Time in seconds. Default is 600 seconds = 10 minutes. Minimum is 60 seconds. +-- @return #RAT RAT self object. function RAT:TimeDestroyInactive(time) self:F2(time) time=time or self.Tinactive time=math.max(time, 60) self.Tinactive=time + return self end --- Set the maximum cruise speed of the aircraft. -- @param #RAT self -- @param #number speed Speed in km/h. +-- @return #RAT RAT self object. function RAT:SetMaxCruiseSpeed(speed) self:F2(speed) -- Convert to m/s. self.Vcruisemax=speed/3.6 + return self end --- Set the climb rate. This automatically sets the climb angle. -- @param #RAT self -- @param #number rate Climb rate in ft/min. Default is 1500 ft/min. Minimum is 100 ft/min. Maximum is 15,000 ft/min. +-- @return #RAT RAT self object. function RAT:SetClimbRate(rate) self:F2(rate) rate=rate or self.Vclimb rate=math.max(rate, 100) rate=math.min(rate, 15000) self.Vclimb=rate + return self end --- Set the angle of descent. Default is 3.6 degrees, which corresponds to 3000 ft descent after one mile of travel. -- @param #RAT self -- @param #number angle Angle of descent in degrees. Minimum is 0.5 deg. Maximum 50 deg. +-- @return #RAT RAT self object. function RAT:SetDescentAngle(angle) self:F2(angle) angle=angle or self.AlphaDescent angle=math.max(angle, 0.5) angle=math.min(angle, 50) self.AlphaDescent=angle + return self end --- Set rules of engagement (ROE). Default is weapon hold. This is a peaceful class. -- @param #RAT self -- @param #string roe "hold" = weapon hold, "return" = return fire, "free" = weapons free. +-- @return #RAT RAT self object. function RAT:SetROE(roe) self:F2(roe) if roe=="return" then @@ -1511,11 +1694,13 @@ function RAT:SetROE(roe) else self.roe=RAT.ROE.weaponhold end + return self end --- Set reaction to threat (ROT). Default is no reaction, i.e. aircraft will simply ignore all enemies. -- @param #RAT self -- @param #string rot "noreaction" = no reaction to threats, "passive" = passive defence, "evade" = evade enemy attacks. +-- @return #RAT RAT self object. function RAT:SetROT(rot) self:F2(rot) if rot=="passive" then @@ -1525,184 +1710,225 @@ function RAT:SetROT(rot) else self.rot=RAT.ROT.noreaction end + return self end --- Set the name of the F10 submenu. Default is the name of the template group. -- @param #RAT self -- @param #string name Submenu name. +-- @return #RAT RAT self object. function RAT:MenuName(name) self:F2(name) self.SubMenuName=tostring(name) + return self end --- Enable ATC, which manages the landing queue for RAT aircraft if they arrive simultaniously at the same airport. -- @param #RAT self --- @param #boolean switch Enable ATC (true) or Disable ATC (false). No argument means ATC enabled. +-- @param #boolean switch Enable ATC (true) or Disable ATC (false). No argument means ATC enabled. +-- @return #RAT RAT self object. function RAT:EnableATC(switch) self:F2(switch) if switch==nil then switch=true end self.ATCswitch=switch + return self end --- Turn messages from ATC on or off. Default is on. This setting effects all RAT objects and groups! -- @param #RAT self --- @param #boolean switch Enable (true) or disable (false) messages from ATC. +-- @param #boolean switch Enable (true) or disable (false) messages from ATC. +-- @return #RAT RAT self object. function RAT:ATC_Messages(switch) self:F2(switch) if switch==nil then switch=true end RAT.ATC.messages=switch + return self end --- Max number of planes that get landing clearance of the RAT ATC. This setting effects all RAT objects and groups! -- @param #RAT self -- @param #number n Number of aircraft that are allowed to land simultaniously. Default is 2. +-- @return #RAT RAT self object. function RAT:ATC_Clearance(n) self:F2(n) RAT.ATC.Nclearance=n or 2 + return self end --- Delay between granting landing clearance for simultanious landings. This setting effects all RAT objects and groups! -- @param #RAT self -- @param #number time Delay time when the next aircraft will get landing clearance event if the previous one did not land yet. Default is 240 sec. +-- @return #RAT RAT self object. function RAT:ATC_Delay(time) self:F2(time) RAT.ATC.delay=time or 240 + return self end --- Set minimum distance between departure and destination. Default is 5 km. -- Minimum distance should not be smaller than maybe ~500 meters to ensure that departure and destination are different. -- @param #RAT self -- @param #number dist Distance in km. +-- @return #RAT RAT self object. function RAT:SetMinDistance(dist) self:F2(dist) -- Distance in meters. Absolute minimum is 500 m. self.mindist=math.max(500, dist*1000) + return self end --- Set maximum distance between departure and destination. Default is 5000 km but aircarft range is also taken into account automatically. -- @param #RAT self -- @param #number dist Distance in km. +-- @return #RAT RAT self object. function RAT:SetMaxDistance(dist) self:F2(dist) -- Distance in meters. self.maxdist=dist*1000 + return self end --- Turn debug messages on or off. Default is off. -- @param #RAT self -- @param #boolean switch Turn debug on=true or off=false. No argument means on. +-- @return #RAT RAT self object. function RAT:_Debug(switch) self:F2(switch) if switch==nil then switch=true end self.Debug=switch + return self end --- Enable debug mode. More output in dcs.log file and onscreen messages to all. -- @param #RAT self +-- @return #RAT RAT self object. function RAT:Debugmode() self:F2() self.Debug=true + return self end --- Aircraft report status update messages along the route. -- @param #RAT self -- @param #boolean switch Swtich reports on (true) or off (false). No argument is on. +-- @return #RAT RAT self object. function RAT:StatusReports(switch) self:F2(switch) if switch==nil then switch=true end self.reportstatus=switch + return self end --- Place markers of waypoints on the F10 map. Default is off. -- @param #RAT self -- @param #boolean switch true=yes, false=no. +-- @return #RAT RAT self object. function RAT:PlaceMarkers(switch) self:F2(switch) if switch==nil then switch=true end self.placemarkers=switch + return self end --- Set flight level. Setting this value will overrule all other logic. Aircraft will try to fly at this height regardless. -- @param #RAT self -- @param #number FL Fight Level in hundrets of feet. E.g. FL200 = 20000 ft ASL. +-- @return #RAT RAT self object. function RAT:SetFL(FL) self:F2(FL) FL=FL or self.FLcruise FL=math.max(FL,0) self.FLuser=FL*RAT.unit.FL2m + return self end --- Set max flight level. Setting this value will overrule all other logic. Aircraft will try to fly at less than this FL regardless. -- @param #RAT self -- @param #number FL Maximum Fight Level in hundrets of feet. +-- @return #RAT RAT self object. function RAT:SetFLmax(FL) self:F2(FL) self.FLmaxuser=FL*RAT.unit.FL2m + return self end --- Set max cruising altitude above sea level. -- @param #RAT self -- @param #number alt Altitude ASL in meters. +-- @return #RAT RAT self object. function RAT:SetMaxCruiseAltitude(alt) self:F2(alt) self.FLmaxuser=alt + return self end --- Set min flight level. Setting this value will overrule all other logic. Aircraft will try to fly at higher than this FL regardless. -- @param #RAT self -- @param #number FL Maximum Fight Level in hundrets of feet. +-- @return #RAT RAT self object. function RAT:SetFLmin(FL) self:F2(FL) self.FLminuser=FL*RAT.unit.FL2m + return self end --- Set min cruising altitude above sea level. -- @param #RAT self -- @param #number alt Altitude ASL in meters. +-- @return #RAT RAT self object. function RAT:SetMinCruiseAltitude(alt) self:F2(alt) self.FLminuser=alt + return self end --- Set flight level of cruising part. This is still be checked for consitancy with selected route and prone to radomization. -- Default is FL200 for planes and FL005 for helicopters. -- @param #RAT self -- @param #number FL Flight level in hundrets of feet. E.g. FL200 = 20000 ft ASL. +-- @return #RAT RAT self object. function RAT:SetFLcruise(FL) self:F2(FL) self.FLcruise=FL*RAT.unit.FL2m + return self end --- Set cruising altitude. This is still be checked for consitancy with selected route and prone to radomization. -- @param #RAT self -- @param #number alt Cruising altitude ASL in meters. +-- @return #RAT RAT self object. function RAT:SetCruiseAltitude(alt) self:F2(alt) self.FLcruise=alt + return self end --- Set onboard number prefix. Same as setting "TAIL #" in the mission editor. Note that if you dont use this function, the values defined in the template group of the ME are taken. -- @param #RAT self -- @param #string tailnumprefix String of the tail number prefix. If flight consists of more than one aircraft, two digits are appended automatically, i.e. 001, 002, ... -- @param #number zero (Optional) Starting value of the automatically appended numbering of aircraft within a flight. Default is 0. +-- @return #RAT RAT self object. function RAT:SetOnboardNum(tailnumprefix, zero) self:F2({tailnumprefix=tailnumprefix, zero=zero}) self.onboardnum=tailnumprefix if zero ~= nil then self.onboardnum0=zero end + return self end +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Private functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Initialize basic parameters of the aircraft based on its (template) group in the mission editor. @@ -1828,12 +2054,6 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live if not (departure and destination and waypoints) then return nil end - - -- Find parking spot in RAT parking DB. Category 4 should be airports and farps. Ships would be caterory 1. - local _spawnpos=_lastpos --- if self.useparkingdb and (takeoff==RAT.wp.cold or takeoff==RAT.wp.hot) and departure:GetCategory()==4 and _spawnpos==nil then --- _spawnpos=self:_FindParkingSpot(departure) --- end -- Set (another) livery. local livery @@ -1850,7 +2070,7 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live end -- Modify the spawn template to follow the flight plan. - local successful=self:_ModifySpawnTemplate(waypoints, livery, _spawnpos, departure, takeoff) + local successful=self:_ModifySpawnTemplate(waypoints, livery, _lastpos, departure, takeoff) if not successful then return nil end @@ -1935,11 +2155,6 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live -- Number of preformed spawn attempts for this group. self.ratcraft[self.SpawnIndex].nrespawn=nrespawn - - -- If we start at a parking position, we memorize the parking spot position for future use (DCS bug). - if self.useparkingdb and (takeoff==RAT.wp.cold or takeoff==RAT.wp.hot) and departure:GetCategory()==4 then - self:_AddParkingSpot(departure, group) - end -- Create submenu for this group. if self.f10menu then @@ -3399,25 +3614,26 @@ function RAT:_OnBirth(EventData) local _landing=self.ratcraft[i].landing local _livery=self.ratcraft[i].livery + -- Some is only useful for an actual airbase (not a zone). + local _airbase=AIRBASE:FindByName(_departure) + -- Check if aircraft group was accidentally spawned on the runway. -- This can happen due to no parking slots available and other DCS bugs. local onrunway=false - if _takeoff ~= RAT.wp.runway and self.checkonrunway then - for _,unit in pairs(SpawnGroup:GetUnits()) do - local _onrunway=self:_CheckOnRunway(unit, _departure) - if _onrunway then - onrunway=true - end - end - end + if _airbase then + -- Check that we did not want to spawn at a runway or in air. + if self.checkonrunway and _takeoff ~= RAT.wp.runway and _takeoff ~= RAT.wp.air then + onrunway=_airbase:CheckOnRunWay(SpawnGroup, self.onrunwayradius, false) + end + end -- Workaround if group was spawned on runway. if onrunway then -- Error message. - local text=string.format("ERROR: RAT group of %s was spawned on runway (DCS bug). Group #%d will be despawned immediately!", self.alias, i) + local text=string.format("ERROR: RAT group of %s was spawned on runway. Group #%d will be despawned immediately!", self.alias, i) MESSAGE:New(text,30):ToAllIf(self.Debug) - self:T(RAT.id..text) + self:E(RAT.id..text) if self.Debug then SpawnGroup:FlareRed() end @@ -3426,12 +3642,12 @@ function RAT:_OnBirth(EventData) self:_Despawn(SpawnGroup) -- Try to respawn the group if there is at least another airport or random airport selection is used. - if (self.Ndeparture_Airports>=2 or self.random_departure) and _nrespawn=2 or self.random_departure) and _nrespawn self.aircraft.box*2 - -- Or (if possible) even better to take our and the other object's size (plus 10% safety margin) - local size=self:_GetObjectSize(unit) - if size then - safe=_dist > (self.aircraft.box+size)*1.1 - end - self:T2(RAT.id..string.format("RAT aircraft size = %.1f m, other object size = %.1f m", self.aircraft.box, size or 0)) - if not safe then - occupied=true - end - self:T2(RAT.id..string.format("Unit %s to parking spot %d: distance = %.1f m (occupied = %s).", unit:GetName(), _i, _dist, tostring(safe))) - end - end - end - - if occupied then - self:T(RAT.id..string.format("Parking spot #%d occupied at %s.", _i, airport)) - else - parkingspot=spawnplace - self:T(RAT.id..string.format("Found free parking spot in DB at airport %s.", airport)) - break - end - - end - - return parkingspot - else - self:T2(RAT.id..string.format("No parking position in DB yet for %s.", airport)) - end - - self:T(RAT.id..string.format("No free parking position found in DB at airport %s.", airport)) - return nil -end --- Get aircraft dimensions length, width, height. -- @param #RAT self @@ -4535,9 +4632,9 @@ function RAT:_GetObjectSize(unit) if DCSunit then local DCSdesc=DCSunit:getDesc() -- dimensions - local length=DCSdesc.box.max.x - local height=DCSdesc.box.max.y - local width=DCSdesc.box.max.z + local length=DCSdesc.box.max.x+math.abs(DCSdesc.box.min.x) + local height=DCSdesc.box.max.y+math.abs(DCSdesc.box.max.y) + local width =DCSdesc.box.max.z+math.abs(DCSdesc.box.min.z) return math.max(length,width) end return nil @@ -4577,7 +4674,7 @@ function RAT:_CheckOnTop(group, distmin) -- Check for min distance. if _dist < distmin then - if not uniti:InAir() and not unitj:InAir() then + if (not uniti:InAir()) and (not unitj:InAir()) then -- Trigger immidiate destuction of unit. self:T(RAT.id..string.format("Unit %s is on top of unit %s. Distance %.2f m.", namei, namej,_dist)) return true @@ -4594,67 +4691,6 @@ function RAT:_CheckOnTop(group, distmin) return false end ---- Function to check whether an aircraft is on the runway. --- @param #RAT self --- @param Wrapper.Unit#UNIT unit The unit to be checked. --- @param #string airport The name of the airport we want to check. --- @return #boolean True if aircraft is on the runway and on the ground. -function RAT:_CheckOnRunway(unit, airport) - - -- We use the tabulated points in the ATC_GROUND classes to find out if the group is on the runway. - -- Note that land.SurfaceType.RUNWAY also is true for the parking areas etc. Hence, not useful. - -- This is useful to check if an aircraft was accidentally spawned on the runway due to missing parking spots. - --BASE:E(ATC_GROUND_CAUCASUS.Airbases[AIRBASE.Caucasus.Batumi].PointsRunways) - - -- Table holding the points around the runway. - local pointsrwy={} - - -- Loop over all airports on Caucaus map. - for id,name in pairs(AIRBASE.Caucasus) do - if name==airport then - --pointsrwy=ATC_GROUND_CAUCASUS.Airbases[AIRBASE.Caucasus.Batumi].PointsRunways - pointsrwy=ATC_GROUND_CAUCASUS.Airbases[name].PointsRunways - self:T2({name=name, points=pointsrwy}) - end - end - -- Loop over all airports on NTTR map. - for id,name in pairs(AIRBASE.Nevada) do - if name==airport then - pointsrwy=ATC_GROUND_NEVADA.Airbases[name].PointsRunways - self:T2({name=name, points=pointsrwy}) - end - end - -- Loop over all airports on Normandy map. - for id,name in pairs(AIRBASE.Normandy) do - if name==airport then - pointsrwy=ATC_GROUND_NORMANDY.Airbases[name].PointsRunways - self:T2({name=name, points=pointsrwy}) - end - end - - -- Assume we are not on the runway. - local onrunway=false - - -- Loop over all runways. Some airports have more than one. - for PointsRunwayID, PointsRunway in pairs(pointsrwy) do - -- Create zone around runway. - local runway = ZONE_POLYGON_BASE:New("Runway "..PointsRunwayID, PointsRunway) - - -- Check if unit is in on the runway. - if runway:IsVec3InZone(unit:GetVec3()) then - onrunway=true - end - end - - -- Check that aircraft is on ground. - onrunway=onrunway and unit:InAir()==false - - -- Debug - self:T(RAT.id..string.format("Check on runway of %s airport for unit %s = %s", airport, unit:GetName(),tostring(onrunway))) - - return onrunway -end - --- Calculate minimum distance between departure and destination for given minimum flight level and climb/decent rates. -- @param #RAT self @@ -4929,7 +4965,6 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - --- Modifies the template of the group to be spawned. -- In particular, the waypoints of the group's flight plan are copied into the spawn template. -- This allows to spawn at airports and also land at other airports, i.e. circumventing the DCS "landing bug". @@ -5012,6 +5047,13 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take if spawnonrunway then termtype=AIRBASE.TerminalType.Runway end + + -- Scan options. Might make that input somehow. + local scanradius=self.parkingscanradius + local scanunits=true + local scanstatics=true + local scanscenery=self.parkingscanscenery + local verysafe=self.parkingverysafe -- Get free parking spots depending on where we spawn. if spawnonship or spawnonfarp or spawnonrunway then @@ -5025,18 +5067,18 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take if termtype==nil then -- Try exclusive helo spots first. self:T(RAT.id..string.format("Helo group %s is spawned at %s using terminal type %d.", self.alias, departure:GetName(), AIRBASE.TerminalType.HelicopterOnly)) - spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly) + spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe) nfree=#spots if nfree<#SpawnTemplate.units then -- Not enough helo ports. Let's try also other terminal types. self:T(RAT.id..string.format("Helo group %s is spawned at %s using terminal type %d.", self.alias, departure:GetName(), AIRBASE.TerminalType.HelicopterOnly)) - spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterUsable) + spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterUsable, scanradius, scanunits, scanstatics, scanscenery, verysafe) nfree=#spots end else -- Terminal type specified explicitly. self:T(RAT.id..string.format("Helo group %s is at %s using terminal type %d.", self.alias, departure:GetName(), termtype)) - spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, termtype) + spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe) nfree=#spots end else @@ -5050,23 +5092,23 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take if bomber or transport then -- First we fill the potentially bigger spots. self:T(RAT.id..string.format("Transport/bomber group %s is spawned at %s using terminal type %d.", self.alias, departure:GetName(), AIRBASE.TerminalType.OpenBig)) - spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenBig) + spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenBig, scanradius, scanunits, scanstatics, scanscenery, verysafe) nfree=#spots if nfree<#SpawnTemplate.units then -- Now we try the smaller ones. self:T(RAT.id..string.format("Transport/bomber group %s is at %s using terminal type %d.", self.alias, departure:GetName(), AIRBASE.TerminalType.OpenMed)) - spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenMed) + spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.OpenMed, scanradius, scanunits, scanstatics, scanscenery, verysafe) nfree=#spots end else self:T(RAT.id..string.format("Fighter group %s is spawned at %s using terminal type %d.", self.alias, departure:GetName(), AIRBASE.TerminalType.FighterAircraft)) - spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.FighterAircraft) + spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.FighterAircraft, scanradius, scanunits, scanstatics, scanscenery, verysafe) nfree=#spots end else -- Terminal type explicitly given. self:T(RAT.id..string.format("Plane group %s is spawned at %s using terminal type %d.", self.alias, departure:GetName(), termtype)) - spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, termtype) + spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, termtype, scanradius, scanunits, scanstatics, scanscenery, verysafe) nfree=#spots end end @@ -5090,7 +5132,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take end else - if self.respawn_inair or self.uncontrolled then + if self.respawn_inair and not self.uncontrolled then self:E(RAT.id..string.format("WARNING: RAT group %s has no parking spots at %s ==> air start!", self.alias, departure:GetName())) -- Not enough parking spots at the airport ==> Spawn in air. @@ -5621,6 +5663,7 @@ end -- @param #RATMANAGER self -- @param #RAT ratobject RAT object to be managed. -- @param #number min Minimum number of groups for this RAT object. Default is 1. +-- @return #RATMANAGER RATMANAGER self object. function RATMANAGER:Add(ratobject,min) --Automatic respawning is disabled. @@ -5640,11 +5683,14 @@ function RATMANAGER:Add(ratobject,min) -- Call spawn to initialize RAT parameters. ratobject:Spawn(0) + + return self end --- Starts the RAT manager and spawns the initial random number RAT groups for each RAT object. -- @param #RATMANAGER self -- @param #number delay Time delay in seconds after which the RAT manager is started. Default is 5 seconds. +-- @return #RATMANAGER RATMANAGER self object. function RATMANAGER:Start(delay) -- Time delay. @@ -5661,10 +5707,13 @@ function RATMANAGER:Start(delay) -- Start scheduler. SCHEDULER:New(nil, self._Start, {self}, delay) + + return self end --- Instantly starts the RAT manager and spawns the initial random number RAT groups for each RAT object. -- @param #RATMANAGER self +-- @return #RATMANAGER RATMANAGER self object. function RATMANAGER:_Start() -- Ensure that ntot is at least sum of min RAT groups. @@ -5695,29 +5744,36 @@ function RATMANAGER:_Start() local text=string.format(RATMANAGER.id.."Starting RAT manager with scheduler ID %s.", self.managerid) self:E(text) + return self end --- Stops the RAT manager. -- @param #RATMANAGER self -- @param #number delay Delay in seconds before the manager is stopped. Default is 1 second. +-- @return #RATMANAGER RATMANAGER self object. function RATMANAGER:Stop(delay) delay=delay or 1 self:E(string.format(RATMANAGER.id.."Manager will be stopped in %d seconds.", delay)) SCHEDULER:New(nil, self._Stop, {self}, delay) + return self end --- Instantly stops the RAT manager by terminating its scheduler. -- @param #RATMANAGER self +-- @return #RATMANAGER RATMANAGER self object. function RATMANAGER:_Stop() self:E(string.format(RATMANAGER.id.."Stopping manager with scheduler ID %s.", self.managerid)) self.manager:Stop(self.managerid) + return self end --- Sets the time interval between checks of alive RAT groups. Default is 30 seconds. -- @param #RATMANAGER self -- @param #number dt Time interval in seconds. +-- @return #RATMANAGER RATMANAGER self object. function RATMANAGER:SetTcheck(dt) self.Tcheck=dt or 30 + return self end --- Manager function. Calculating the number of current groups and respawning new groups if necessary. diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 143dbf368..0b4663bb5 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -268,7 +268,7 @@ AIRBASE.PersianGulf = { -- * AIRBASE.TerminalType.OpenBig: Open air spawn points. Generally larger but does not guarantee large aircraft are capable of spawning there. -- * AIRBASE.TerminalType.OpenMedOrBig: Combines OpenMed and OpenBig spots. -- * AIRBASE.TerminalType.HelicopterUnsable: Combines HelicopterOnly, OpenMed and OpenBig. --- * AIRBASE.TerminalType.FighterAircraft: Combines Shelter. OpenMed and OpenBig spots. +-- * AIRBASE.TerminalType.FighterAircraft: Combines Shelter. OpenMed and OpenBig spots. So effectively all spots usable by fixed wing aircraft. -- @field TerminalType AIRBASE.TerminalType = { Runway=16, @@ -392,7 +392,7 @@ end --- Get number of parking spots at an airbase. Optionally, a specific terminal type can be requested. -- @param #AIRBASE self --- @param #number termtype Terminal type of which the number of spots is counted. Default all spots but spawn points on runway. +-- @param #AIRBASE.TerminalType termtype Terminal type of which the number of spots is counted. Default all spots but spawn points on runway. -- @return #number Number of parking spots at this airbase. function AIRBASE:GetParkingSpotsNumber(termtype) @@ -411,7 +411,7 @@ end --- Get number of free parking spots at an airbase. -- @param #AIRBASE self --- @param #number termtype Terminal type. +-- @param #AIRBASE.TerminalType termtype Terminal type. -- @param #boolean allowTOAC If true, spots are considered free even though TO_AC is true. Default is off which is saver to avoid spawning aircraft on top of each other. Option might be enabled for FARPS and ships. -- @return #number Number of free parking spots at this airbase. function AIRBASE:GetFreeParkingSpotsNumber(termtype, allowTOAC) @@ -434,7 +434,7 @@ end --- Get the coordinates of free parking spots at an airbase. -- @param #AIRBASE self --- @param #number termtype Terminal type. +-- @param #AIRBASE.TerminalType termtype Terminal type. -- @param #boolean allowTOAC If true, spots are considered free even though TO_AC is true. Default is off which is saver to avoid spawning aircraft on top of each other. Option might be enabled for FARPS and ships. -- @return #table Table of coordinates of the free parking spots. function AIRBASE:GetFreeParkingSpotsCoordinates(termtype, allowTOAC) @@ -458,7 +458,7 @@ end --- Get the coordinates of all parking spots at an airbase. Optionally only those of a specific terminal type. Spots on runways are excluded if not explicitly requested by terminal type. -- @param #AIRBASE self --- @param #number termtype (Optional) Terminal type. Default all. +-- @param #AIRBASE.TerminalType termtype (Optional) Terminal type. Default all. -- @return #table Table of coordinates of parking spots. function AIRBASE:GetParkingSpotsCoordinates(termtype) @@ -487,7 +487,7 @@ end --- Get a table containing the coordinates, terminal index and terminal type of free parking spots at an airbase. -- @param #AIRBASE self --- @param #number termtype Terminal type. +-- @param #AIRBASE.TerminalType termtype Terminal type. -- @return #table Table free parking spots. Table has the elements ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy". function AIRBASE:GetParkingSpotsTable(termtype) @@ -521,7 +521,7 @@ end --- Get a table containing the coordinates, terminal index and terminal type of free parking spots at an airbase. -- @param #AIRBASE self --- @param #number termtype Terminal type. +-- @param #AIRBASE.TerminalType termtype Terminal type. -- @param #boolean allowTOAC If true, spots are considered free even though TO_AC is true. Default is off which is saver to avoid spawning aircraft on top of each other. Option might be enabled for FARPS and ships. -- @return #table Table free parking spots. Table has the elements ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy". function AIRBASE:GetFreeParkingSpotsTable(termtype, allowTOAC) @@ -545,8 +545,14 @@ end --- Place markers of parking spots on the F10 map. -- @param #AIRBASE self --- @param #number termtype Terminal type for which marks should be placed. -function AIRBASE:MarkParkingSpots(termtype) +-- @param #AIRBASE.TerminalType termtype Terminal type for which marks should be placed. +-- @param #boolean mark If false, do not place markers but only give output to DCS.log file. Default true. +function AIRBASE:MarkParkingSpots(termtype, mark) + + -- Default is true. + if mark==nil then + mark=true + end -- Get parking data from getParking() wrapper function. local parkingdata=self:GetParkingSpotsTable(termtype) @@ -562,7 +568,9 @@ function AIRBASE:MarkParkingSpots(termtype) _spot.TerminalID, _spot.TerminalType,tostring(_spot.Free),tostring(_spot.TOAC),_spot.TerminalID0,_spot.DistToRwy) -- Create mark on the F10 map. - _spot.Coordinate:MarkToAll(_text) + if mark then + _spot.Coordinate:MarkToAll(_text) + end -- Info to DCS.log file. local _text=string.format("%s, Term Index=%3d, Term Type=%03d, Free=%5s, TOAC=%5s, Term ID0=%3d, Dist2Rwy=%.1f m", @@ -575,16 +583,17 @@ end -- The dimension of the spawned aircraft and of the potential obstacle are taken into account. Note that the routine can only return so many spots that are free. -- @param #AIRBASE self -- @param Wrapper.Group#GROUP group Aircraft group for which the parking spots are requested. --- @param #number terminaltype (Optional) Only search spots at a specific terminal type. Default is all types execpt on runway. --- @param #number scanradius (Optional) Radius in meters around parking spot to scan for obstacles. Default 30 m. +-- @param #AIRBASE.TerminalType terminaltype (Optional) Only search spots at a specific terminal type. Default is all types execpt on runway. +-- @param #number scanradius (Optional) Radius in meters around parking spot to scan for obstacles. Default 50 m. -- @param #boolean scanunits (Optional) Scan for units as obstacles. Default true. -- @param #boolean scanstatics (Optional) Scan for statics as obstacles. Default true. -- @param #boolean scanscenery (Optional) Scan for scenery as obstacles. Default false. Can cause problems with e.g. shelters. +-- @param #boolean verysafe (Optional) If true, wait until an aircraft has taken off until the parking spot is considered to be free. Defaul false. -- @return #table Table of coordinates and terminal IDs of free parking spots. Each table entry has the elements .Coordinate and .TerminalID. -function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, scanunits, scanstatics, scanscenery) +function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, scanunits, scanstatics, scanscenery, verysafe) -- Init default - scanradius=scanradius or 30 + scanradius=scanradius or 50 if scanunits==nil then scanunits=true end @@ -594,9 +603,9 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, if scanscenery==nil then scanscenery=false end - - -- Mark all found obstacles on F10 map. - local markobstacles=false + if verysafe==nil then + verysafe=false + end -- Get the size of an object. local function _GetObjectSize(unit,mooseobject) @@ -605,10 +614,12 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, end if unit and unit:isExist() then local DCSdesc=unit:getDesc() - local x=DCSdesc.box.max.x+math.abs(DCSdesc.box.min.x) - local y=DCSdesc.box.max.y+math.abs(DCSdesc.box.min.y) --height - local z=DCSdesc.box.max.z+math.abs(DCSdesc.box.min.z) - return math.max(x,z), x , y, z + if DCSdesc.box then + local x=DCSdesc.box.max.x+math.abs(DCSdesc.box.min.x) + local y=DCSdesc.box.max.y+math.abs(DCSdesc.box.min.y) --height + local z=DCSdesc.box.max.z+math.abs(DCSdesc.box.min.z) + return math.max(x,z), x , y, z + end end return 0,0,0,0 end @@ -626,8 +637,11 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, -- Get airport name. local airport=self:GetName() - -- Get free parking spot data table. - --local parkingdata=self:GetFreeParkingSpotsTable(terminaltype, true) + -- Get parking spot data table. This contains free and "non-free" spots. + -- Note that there are three major issues with the DCS getParking() function: + -- 1. A spot is considered as NOT free until an aircraft that is present has finally taken off. This might be a bit long especiall at smaller airports. + -- 2. A "free" spot does not take the aircraft size into accound. So if two big aircraft are spawned on spots next to each other, they might overlap and get destroyed. + -- 3. The routine return a free spot, if there a static objects placed on the spot. local parkingdata=self:GetParkingSpotsTable(terminaltype) -- Get the aircraft size, i.e. it's longest side of x,z. @@ -642,15 +656,29 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, local validspots={} local nvalid=0 + -- Test other stuff if no parking spot is available. + local _test=false + if _test then + return validspots + end + + -- Mark all found obstacles on F10 map for debugging. + local markobstacles=false + -- Loop over all known parking spots for _,parkingspot in pairs(parkingdata) do - - -- Check for requested terminal type if any. - if AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) then - -- Coordinate of the parking spot. - local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE - local _termid=parkingspot.TerminalID + -- Coordinate of the parking spot. + local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE + local _termid=parkingspot.TerminalID + + -- Very safe uses the DCS getParking() info to check if a spot is free. Unfortunately, the function returns free=false until the aircraft has actually taken-off. + if verysafe and parkingspot.Free==false then + + -- DCS getParking() routine returned that spot is not free. + self:T(string.format("%s: Parking spot id %d NOT free (or aircraft has not taken off yet).", airport, parkingspot.TerminalID)) + + else -- Scan a radius of 50 meters around the spot. local _,_,_,_units,_statics,_sceneries=_spot:ScanObjects(scanradius, scanunits, scanstatics, scanscenery) @@ -712,45 +740,52 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, --_spot:MarkToAll(string.format("Parking spot %d free=%s", parkingspot.TerminalID, tostring(not occupied))) if occupied then - self:T(string.format("%s: Parking spot id %d occupied.", airport, parkingspot.TerminalID)) + self:T(string.format("%s: Parking spot id %d occupied.", airport, _termid)) else - self:E(string.format("%s: Parking spot id %d free.", airport, parkingspot.TerminalID)) + self:E(string.format("%s: Parking spot id %d free.", airport, _termid)) if nvalid=nspots then return validspots end end + -- Retrun spots we found, even if there were not enough. return validspots end ---- Helper function that checks if a group is close to a spawn point on the runway. +--- Function that checks if at leat one unit of a group has been spawned close to a spawn point on the runway. -- @param #AIRBASE self -- @param Wrapper.Group#GROUP group Group to be checked. --- @param #number radius Radius around the spawn point to be checked. Default is 25 m. +-- @param #number radius Radius around the spawn point to be checked. Default is 50 m. -- @param #boolean despawn If true, the group is destroyed. -- @return #boolean True if group is within radius around spawn points on runway. function AIRBASE:CheckOnRunWay(group, radius, despawn) -- Default radius. - radius=radius or 25 + radius=radius or 50 - -- Debug. - self:T(string.format("%s, checking if group %s is on runway?",self:GetName(), group:GetName())) + -- We only check at real airbases (not FARPS or ships). + if self:GetDesc().category~=Airbase.Category.AIRDROME then + return false + end if group and group:IsAlive() then + -- Debug. + self:T(string.format("%s, checking if group %s is on runway?",self:GetName(), group:GetName())) + -- Get coordinates on runway. local runwaypoints=self:GetParkingSpotsCoordinates(AIRBASE.TerminalType.Runway) + -- Mark runway spawn points. --[[ for _i,_coord in pairs(runwaypoints) do _coord:MarkToAll(string.format("runway %d",_i)) @@ -762,8 +797,11 @@ function AIRBASE:CheckOnRunWay(group, radius, despawn) -- Loop over units. for _,_unit in pairs(units) do + local unit=_unit --Wrapper.Unit#UNIT - if unit and unit:IsAlive() then + + -- Check if unit is alive and not in air. + if unit and unit:IsAlive() and not unit:InAir() then self:T(string.format("%s, checking if unit %s is on runway?",self:GetName(), unit:GetName())) -- Loop over runway spawn points. @@ -772,9 +810,12 @@ function AIRBASE:CheckOnRunWay(group, radius, despawn) -- Distance between unit and spawn pos. local dist=unit:GetCoordinate():Get2DDistance(_coord) + -- Mark unit spawn points for debugging. + --unit:GetCoordinate():MarkToAll(string.format("unit %s distance to rwy %d = %d",unit:GetName(),_i, dist)) + -- Check if unit is withing radius. - if dist Date: Fri, 29 Jun 2018 05:05:31 +0200 Subject: [PATCH 194/420] Documentation --- Moose Development/Moose/Core/Point.lua | 116 ++++++++++++++++------- Moose Development/Moose/Tasking/Task.lua | 39 +++++--- 2 files changed, 109 insertions(+), 46 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index b98159460..191fbc050 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1,5 +1,11 @@ ---- **Core** -- **POINT\_VEC** classes define an **extensive API** to **manage 3D points** in the simulation space. +--- **Core** -- Defines an **extensive API** to **manage 3D points** in the DCS World 3D simulation space. -- +-- **Features:** +-- +-- * Provides a COORDINATE class, which allows to manage points in 3D space and perform various operations on it. +-- * Provides a POINT\_VEC2 class, which is derived from COORDINATE, and allows to manage points in 3D space, but from a Lat/Lon and Altitude perspective. +-- * Provides a POINT\_VEC3 class, which is derived from COORDINATE, and allows to manage points in 3D space, but from a X, Z and Y vector perspective. +-- -- === -- -- # Demo Missions @@ -38,29 +44,20 @@ do -- COORDINATE --- Defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space. -- - -- ## COORDINATE constructor + -- # 1) Create a COORDINATE object. -- - -- A new COORDINATE object can be created with: + -- A new COORDINATE object can be created with 3 various methods: -- - -- * @{#COORDINATE.New}(): a 3D point. - -- * @{#COORDINATE.NewFromVec2}(): a 2D point created from a @{DCS#Vec2}. - -- * @{#COORDINATE.NewFromVec3}(): a 3D point created from a @{DCS#Vec3}. - -- - -- ## Create waypoints for routes - -- - -- A COORDINATE can prepare waypoints for Ground and Air groups to be embedded into a Route. - -- - -- * @{#COORDINATE.WaypointAir}(): Build an air route point. - -- * @{#COORDINATE.WaypointGround}(): Build a ground route point. - -- - -- Route points can be used in the Route methods of the @{Wrapper.Group#GROUP} class. + -- * @{#COORDINATE.New}(): from a 3D point. + -- * @{#COORDINATE.NewFromVec2}(): from a @{DCS#Vec2} and possible altitude. + -- * @{#COORDINATE.NewFromVec3}(): from a @{DCS#Vec3}. -- -- - -- ## Smoke, flare, explode, illuminate + -- # 2) Smoke, flare, explode, illuminate at the coordinate. -- -- At the point a smoke, flare, explosion and illumination bomb can be triggered. Use the following methods: -- - -- ### Smoke + -- ## 2.1) Smoke -- -- * @{#COORDINATE.Smoke}(): To smoke the point in a certain color. -- * @{#COORDINATE.SmokeBlue}(): To smoke the point in blue. @@ -69,7 +66,7 @@ do -- COORDINATE -- * @{#COORDINATE.SmokeWhite}(): To smoke the point in white. -- * @{#COORDINATE.SmokeGreen}(): To smoke the point in green. -- - -- ### Flare + -- ## 2.2) Flare -- -- * @{#COORDINATE.Flare}(): To flare the point in a certain color. -- * @{#COORDINATE.FlareRed}(): To flare the point in red. @@ -77,18 +74,19 @@ do -- COORDINATE -- * @{#COORDINATE.FlareWhite}(): To flare the point in white. -- * @{#COORDINATE.FlareGreen}(): To flare the point in green. -- - -- ### Explode + -- ## 2.3) Explode -- -- * @{#COORDINATE.Explosion}(): To explode the point with a certain intensity. -- - -- ### Illuminate + -- ## 2.4) Illuminate -- -- * @{#COORDINATE.IlluminationBomb}(): To illuminate the point. -- -- - -- ## Markings + -- # 3) Create markings on the map. -- - -- Place markers (text boxes with clarifications for briefings, target locations or any other reference point) on the map for all players, coalitions or specific groups: + -- Place markers (text boxes with clarifications for briefings, target locations or any other reference point) + -- on the map for all players, coalitions or specific groups: -- -- * @{#COORDINATE.MarkToAll}(): Place a mark to all players. -- * @{#COORDINATE.MarkToCoalition}(): Place a mark to a coalition. @@ -96,47 +94,99 @@ do -- COORDINATE -- * @{#COORDINATE.MarkToCoalitionBlue}(): Place a mark to the blue coalition. -- * @{#COORDINATE.MarkToGroup}(): Place a mark to a group (needs to have a client in it or a CA group (CA group is bugged)). -- * @{#COORDINATE.RemoveMark}(): Removes a mark from the map. - -- - -- - -- ## 3D calculation methods + -- + -- # 4) Coordinate calculation methods. -- -- Various calculation methods exist to use or manipulate 3D space. Find below a short description of each method: -- - -- ### Distance + -- ## 4.1) Get the distance between 2 points. -- -- * @{#COORDINATE.Get3DDistance}(): Obtain the distance from the current 3D point to the provided 3D point in 3D space. -- * @{#COORDINATE.Get2DDistance}(): Obtain the distance from the current 3D point to the provided 3D point in 2D space. -- - -- ### Angle + -- ## 4.2) Get the angle. -- -- * @{#COORDINATE.GetAngleDegrees}(): Obtain the angle in degrees from the current 3D point with the provided 3D direction vector. -- * @{#COORDINATE.GetAngleRadians}(): Obtain the angle in radians from the current 3D point with the provided 3D direction vector. -- * @{#COORDINATE.GetDirectionVec3}(): Obtain the 3D direction vector from the current 3D point to the provided 3D point. -- - -- ### Translation + -- ## 4.3) Coordinate translation. -- -- * @{#COORDINATE.Translate}(): Translate the current 3D point towards an other 3D point using the given Distance and Angle. -- - -- ### Get the North correction of the current location + -- ## 4.4) Get the North correction of the current location. -- -- * @{#COORDINATE.GetNorthCorrection}(): Obtains the north correction at the current 3D point. -- - -- - -- ## Point Randomization + -- ## 4.5) Point Randomization -- -- Various methods exist to calculate random locations around a given 3D point. -- -- * @{#COORDINATE.GetRandomVec2InRadius}(): Provides a random 2D vector around the current 3D point, in the given inner to outer band. -- * @{#COORDINATE.GetRandomVec3InRadius}(): Provides a random 3D vector around the current 3D point, in the given inner to outer band. + -- + -- ## 4.6) LOS between coordinates. + -- + -- Calculate if the coordinate has Line of Sight (LOS) with the other given coordinate. + -- Mountains, trees and other objects can be positioned between the two 3D points, preventing visibilty in a straight continuous line. + -- The method @{#COORDINATE.IsLOS}() returns if the two coodinates have LOS. + -- + -- ## 4.7) Check the coordinate position. + -- + -- Various methods are available that allow to check if a coordinate is: + -- + -- * @{#COORDINATE.IsInRadius}(): in a give radius. + -- * @{#COORDINATE.IsInSphere}(): is in a given sphere. + -- * @{#COORDINATE.IsAtCoordinate2D}(): is in a given coordinate within a specific precision. + -- + -- + -- + -- # 5) Measure the simulation environment at the coordinate. + -- + -- ## 5.1) Weather specific. + -- + -- Within the DCS simulator, a coordinate has specific environmental properties, like wind, temperature, humidity etc. + -- + -- * @{#COORDINATE.GetWind}(): Retrieve the wind at the specific coordinate within the DCS simulator. + -- * @{#COORDINATE.GetTemperature}(): Retrieve the temperature at the specific height within the DCS simulator. + -- * @{#COORDINATE.GetPressure}(): Retrieve the pressure at the specific height within the DCS simulator. + -- + -- ## 5.2) Surface specific. + -- + -- Within the DCS simulator, the surface can have various objects placed at the coordinate, and the surface height will vary. + -- + -- * @{#COORDINATE.GetLandHeight}(): Retrieve the height of the surface (on the ground) within the DCS simulator. + -- * @{#COORDINATE.GetSurfaceType}(): Retrieve the surface type (on the ground) within the DCS simulator. + -- + -- # 6) Create waypoints for routes. + -- + -- A COORDINATE can prepare waypoints for Ground and Air groups to be embedded into a Route. + -- + -- * @{#COORDINATE.WaypointAir}(): Build an air route point. + -- * @{#COORDINATE.WaypointGround}(): Build a ground route point. + -- + -- Route points can be used in the Route methods of the @{Wrapper.Group#GROUP} class. + -- + -- ## 7) Manage the roads. + -- + -- Important for ground vehicle transportation and movement, the method @{#COORDINATE.GetClosestPointToRoad}() will calculate + -- the closest point on the nearest road. + -- + -- In order to use the most optimal road system to transport vehicles, the method @{#COORDINATE.GetPathOnRoad}() will calculate + -- the most optimal path following the road between two coordinates. + -- -- -- - -- ## Metric system + -- + -- + -- ## 8) Metric or imperial system -- -- * @{#COORDINATE.IsMetric}(): Returns if the 3D point is Metric or Nautical Miles. -- * @{#COORDINATE.SetMetric}(): Sets the 3D point to Metric or Nautical Miles. -- -- - -- ## Coorinate text generation + -- ## 9) Coordinate text generation + -- -- -- * @{#COORDINATE.ToStringBR}(): Generates a Bearing & Range text in the format of DDD for DI where DDD is degrees and DI is distance. -- * @{#COORDINATE.ToStringLL}(): Generates a Latutude & Longutude text. diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index 3835621a3..7b645c2ad 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -80,9 +80,9 @@ -- Depending on the task progress, a **scoring** can be allocated to award pilots of the achievements made. -- The scoring is fully flexible, and different levels of awarding can be provided depending on the task type and complexity. -- --- ## 1. Task Statuses +-- # 1) Task Statuses -- --- ### 1.1. Task status overview. +-- ## 1.1) Task status overview. -- -- A task has a state, reflecting the progress and completion of the task: -- @@ -93,12 +93,18 @@ -- - **Abort**: Expresses that the task is aborted by by the player using the abort menu. -- - **Cancelled**: Expresses that the task is cancelled by HQ or through a logical situation where a cancellation of the task is required. -- +-- -- A normal flow of task status would evolve from the **Planned** state, to the **Assigned** state ending either in a **Success** or a **Failed** state. +-- +-- Planned -> Assigned -> Success +-- -> Failed +-- -- The state completion is by default set to **Success**, if the goals of the task have been reached, but can be overruled by a goal method. -- --- Depending on the tactical situation, a task can be **Rejected** or **Cancelled** by the mission governer. +-- Depending on the tactical situation, a task can be **Cancelled** by the mission governer. -- It is actually the mission designer who has the flexibility to decide at which conditions a task would be set to **Success**, **Failed** or **Cancelled**. --- It all depends on the task goals, and the phase/evolution of the task conditions that would accomplish the goals. +-- This decision all depends on the task goals, and the phase/evolution of the task conditions that would accomplish the goals. +-- -- For example, if the task goal is to merely destroy a target, and the target is mid-mission destroyed by another event than the pilot destroying the target, -- the task goal could be set to **Failed**, or .. **Cancelled** ... -- However, it could very well be also acceptable that the task would be flagged as **Success**. @@ -106,7 +112,7 @@ -- The tasking mechanism governs beside the progress also a scoring mechanism, and in case of goal completion without any active pilot involved -- in the execution of the task, could result in a **Success** task completion status, but no score would be awared, as there were no players involved. -- --- ### 1.2. Task status events. +-- ## 1.2) Task status events. -- -- The task statuses can be set by using the following methods: -- @@ -119,12 +125,12 @@ -- The mentioned derived TASK_ classes are implementing the task status transitions out of the box. -- So no extra logic needs to be written. -- --- ## 2. Goal conditions for a task. +-- # 2) Goal conditions for a task. -- -- Every 30 seconds, a @{#Task.Goal} trigger method is fired. -- You as a mission designer, can capture the **Goal** event trigger to check your own task goal conditions and take action! -- --- ### 2.1. Goal event handler `OnAfterGoal()`. +-- ## 2.1) Goal event handler `OnAfterGoal()`. -- -- And this is a really great feature! Imagine a task which has **several conditions to check** before the task can move into **Success** state. -- You can do this with the OnAfterGoal method. @@ -141,23 +147,30 @@ -- end -- end -- --- So the @{#TASK.OnAfterGoal}() event handler would be called every 30 seconds automatically, and within this method, you can now check the conditions and take respective action. +-- So the @{#TASK.OnAfterGoal}() event handler would be called every 30 seconds automatically, +-- and within this method, you can now check the conditions and take respective action. -- --- ### 2.2. Goal event trigger `Goal()`. +-- ## 2.2) Goal event trigger `Goal()`. -- -- If you would need to check a goal at your own defined event timing, then just call the @{#TASK.Goal}() method within your logic. -- The @{#TASK.OnAfterGoal}() event handler would then directly be called and would execute the logic. --- Note that you can also delay the goal check by using the delayed event trigger syntax `:__Goal( Dalay )`. +-- Note that you can also delay the goal check by using the delayed event trigger syntax `:__Goal( Delay )`. -- -- --- ## 3) Add scoring when reaching a certain task status: +-- # 3) Add scoring when reaching a certain task status: -- -- Upon reaching a certain task status in a task, additional scoring can be given. If the Mission has a scoring system attached, the scores will be added to the mission scoring. -- Use the method @{#TASK.AddScore}() to add scores when a status is reached. -- --- ## 1.4) Task briefing: +-- # 4) Task briefing: +-- +-- A task briefing is a text that is shown to the player when he is assigned to the task. +-- The briefing is broadcasted by the command center owning the mission. +-- +-- The briefing is part of the parameters in the @{#TASK.New}() constructor, +-- but can separately be modified later in your mission using the +-- @{#TASK.SetBriefing}() method. -- --- A task briefing can be given that is shown to the player when he is assigned to the task. -- -- @field #TASK TASK -- From 5d87672657f0d297d572a9d36dae242188467bcd Mon Sep 17 00:00:00 2001 From: funkyfranky <> Date: Fri, 29 Jun 2018 17:24:50 +0200 Subject: [PATCH 195/420] Spawn fixes AIRBASE: * Added parameter of how many parking spots are required in find free parking spots routine. SPAWN: * Fixed grouping bug in SpawnAtAirbase. * Fixed spawn on runway bug. * Added user functions for livery and skill. RAT: * Fixed spawn on runway bug. --- Moose Development/Moose/Core/Spawn.lua | 170 ++++++++++++++++---- Moose Development/Moose/Functional/RAT.lua | 103 ++++++++---- Moose Development/Moose/Wrapper/Airbase.lua | 13 +- 3 files changed, 225 insertions(+), 61 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 67d1cf753..ebf4f65ea 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -295,7 +295,9 @@ function SPAWN:New( SpawnTemplatePrefix ) self.SpawnUnControlled = false self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. self.DelayOnOff = false -- No intial delay when spawning the first group. - self.Grouping = nil -- No grouping + self.Grouping = nil -- No grouping. + self.SpawnInitLivery = nil -- No special livery. + self.SpawnInitSkill = nil -- No special skill. self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. else @@ -341,7 +343,9 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix ) self.SpawnUnControlled = false self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. self.DelayOnOff = false -- No intial delay when spawning the first group. - self.Grouping = nil + self.Grouping = nil -- No grouping. + self.SpawnInitLivery = nil -- No special livery. + self.SpawnInitSkill = nil -- No special skill. self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. else @@ -390,7 +394,9 @@ function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPr self.SpawnUnControlled = false self.SpawnInitKeepUnitNames = false -- Overwrite unit names by default with group name. self.DelayOnOff = false -- No intial delay when spawning the first group. - self.Grouping = nil + self.Grouping = nil -- No grouping. + self.SpawnInitLivery = nil -- No special livery. + self.SpawnInitSkill = nil -- No special skill. self.SpawnGroups = {} -- Array containing the descriptions of each Group to be Spawned. else @@ -488,15 +494,23 @@ function SPAWN:InitHeading( HeadingMin, HeadingMax ) end +--- Sets the coalition of the spawned group. Note that it might be necessary to also set the country explicitly! +-- @param #SPAWN self +-- @param #DCS.coalition Coaliton Coaliton of the group as number of enumerator, i.e. 0=coaliton.side.NEUTRAL, 1=coaliton.side.RED, 2=coalition.side.BLUE. +-- @return #SPAWN self function SPAWN:InitCoalition( Coalition ) - self:F( ) + self:F({coalition=Coalition}) self.SpawnInitCoalition = Coalition return self end - +--- Sets the country of the spawn group. Note that the country determins the coalition of the group depending on which country is defined to be on which side for each specific mission! +-- See https://wiki.hoggitworld.com/view/DCS_enum_country for country enumerators. +-- @param #SPAWN self +-- @param #DCS.country Country Country id as number or enumerator, e.g. country.id.RUSSIA=0, county.id.USA=2 etc. +-- @return #SPAWN self function SPAWN:InitCountry( Country ) self:F( ) @@ -506,6 +520,10 @@ function SPAWN:InitCountry( Country ) end +--- Sets category ID of the group. +-- @param #SPAWN self +-- @param #number Category Category id. +-- @return #SPAWN self function SPAWN:InitCategory( Category ) self:F( ) @@ -514,6 +532,40 @@ function SPAWN:InitCategory( Category ) return self end +--- Sets livery of the group. +-- @param #SPAWN self +-- @param #string Livery Livery name. Note that this is not necessarily the same name as displayed in the mission edior. +-- @return #SPAWN self +function SPAWN:InitLivery( Livery ) + self:F({livery=Livery} ) + + self.SpawnInitLivery = Livery + + return self +end + +--- Sets skill of the group. +-- @param #SPAWN self +-- @param #string Skill Skill, possible values "Average", "Good", "High", "Excellent" or "Random". +-- @return #SPAWN self +function SPAWN:InitSkill( Skill ) + self:F({skill=Skill}) + if Skill:lower()=="average" then + self.SpawnInitSkill="Average" + elseif Skill:lower()=="good" then + self.SpawnInitSkill="Good" + elseif Skill:lower()=="excellent" then + self.SpawnInitSkill="Excellent" + elseif Skill:lower()=="random" then + self.SpawnInitSkill="Random" + else + self.SpawnInitSkill="High" + end + + return self +end + + --- Randomizes the defined route of the SpawnTemplatePrefix group in the ME. This is very useful to define extra variation of the behaviour of groups. -- @param #SPAWN self -- @param #number SpawnStartPoint is the waypoint where the randomization begins. @@ -1054,9 +1106,25 @@ function SPAWN:SpawnWithIndex( SpawnIndex ) if self.SpawnInitHeadingMin then for UnitID = 1, #SpawnTemplate.units do SpawnTemplate.units[UnitID].heading = self.SpawnInitHeadingMax and math.random( self.SpawnInitHeadingMin, self.SpawnInitHeadingMax ) or self.SpawnInitHeadingMin + SpawnTemplate.units[UnitID].psi = -SpawnTemplate.units[UnitID].heading end end + -- Set livery. + if self.SpawnInitLivery then + for UnitID = 1, #SpawnTemplate.units do + SpawnTemplate.units[UnitID].livery_id = self.SpawnInitLivery + end + end + + -- Set skill. + if self.SpawnInitSkill then + for UnitID = 1, #SpawnTemplate.units do + SpawnTemplate.units[UnitID].skill = self.SpawnInitSkill + end + end + + -- Set country, coaliton and categroy. SpawnTemplate.CategoryID = self.SpawnInitCategory or SpawnTemplate.CategoryID SpawnTemplate.CountryID = self.SpawnInitCountry or SpawnTemplate.CountryID SpawnTemplate.CoalitionID = self.SpawnInitCoalition or SpawnTemplate.CoalitionID @@ -1272,9 +1340,12 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT local isbomber=TemplateUnit:HasAttribute("Bombers") local istransport=TemplateUnit:HasAttribute("Transports") local isfighter=TemplateUnit:HasAttribute("Battleplanes") + + -- Number of units in the group. With grouping this can actually differ from the template group size! + local nunits=#SpawnTemplate.units -- First waypoint of the group. - local SpawnPoint = SpawnTemplate.route.points[1] + local SpawnPoint = SpawnTemplate.route.points[1] -- These are only for ships and FARPS. SpawnPoint.linkUnit = nil @@ -1350,25 +1421,25 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT if spawnonship or spawnonfarp or spawnonrunway then -- These places work procedural and have some kind of build in queue ==> Less effort. self:T(string.format("Group %s is spawned on farp/ship/runway %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName())) - nfree=SpawnAirbase:GetFreeParkingSpotsNumber(termtype, spawnonship or spawnonfarp or spawnonrunway) - spots=SpawnAirbase:GetFreeParkingSpotsTable(termtype, spawnonship or spawnonfarp or spawnonrunway) + nfree=SpawnAirbase:GetFreeParkingSpotsNumber(termtype, true) + spots=SpawnAirbase:GetFreeParkingSpotsTable(termtype, true) else if ishelo then if termtype==nil then -- Helo is spawned. Try exclusive helo spots first. self:T(string.format("Helo group %s is at %s using terminal type %d.", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), AIRBASE.TerminalType.HelicopterOnly)) - spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe) + spots=SpawnAirbase:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits) nfree=#spots - if nfree<#SpawnTemplate.units then + if nfree= #SpawnTemplate.units or (spawnonrunway and nfree>0) then - - for i=1,#SpawnTemplate.units do - table.insert(parkingspots, spots[i].Coordinate) - table.insert(parkingindex, spots[i].TerminalID) + -- Set this to true if not enough spots are available for emergency air start. + local _notenough=false + + -- Need to differentiate some cases again. + if spawnonship or spawnonfarp or spawnonrunway then + + -- On free spot required in these cases. + if nfree >=1 then + + -- All units get the same spot. DCS takes care of the rest. + for i=1,nunits do + table.insert(parkingspots, spots[1].Coordinate) + table.insert(parkingindex, spots[1].TerminalID) + end + -- This is actually used... + PointVec3=spots[1].Coordinate + + else + -- If there is absolutely not spot ==> air start! + _notenough=true end + + elseif spawnonairport then + + if nfree>=nunits then - else + for i=1,nunits do + table.insert(parkingspots, spots[i].Coordinate) + table.insert(parkingindex, spots[i].TerminalID) + end + + else + -- Not enough spots for the whole group ==> air start! + _notenough=true + end + end + + -- Not enough spots ==> Prepare airstart. + if _notenough then + if EmergencyAirSpawn and not self.SpawnUnControlled then self:E(string.format("WARNING: Group %s has no parking spots at %s ==> air start!", self.SpawnTemplatePrefix, SpawnAirbase:GetName())) @@ -1449,11 +1551,25 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT return nil end end - + + else + + -- Air start requested initially ==> Set altitude. + if TakeoffAltitude then + PointVec3.y=TakeoffAltitude + else + if ishelo then + PointVec3.y=PointVec3:GetLandHeight()+math.random(100,1000) + else + -- Randomize position so that multiple AC wont be spawned on top even in air. + PointVec3.y=PointVec3:GetLandHeight()+math.random(500,2500) + end + end + end -- Translate the position of the Group Template to the Vec3. - for UnitID = 1, #SpawnTemplate.units do + for UnitID = 1, nunits do self:T2('Before Translation SpawnTemplate.units['..UnitID..'].x = '..SpawnTemplate.units[UnitID].x..', SpawnTemplate.units['..UnitID..'].y = '..SpawnTemplate.units[UnitID].y) -- Template of the current unit. diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 081106680..142dd60cb 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -511,7 +511,7 @@ RAT.id="RAT | " --- RAT version. -- @list version RAT.version={ - version = "2.3.0", + version = "2.3.1", print = true, } @@ -5031,6 +5031,9 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take self.SpawnUnControlled=true end + -- Number of units in the group. With grouping this can actually differ from the template group size! + local nunits=#SpawnTemplate.units + -- Array with parking spots coordinates. local parkingspots={} local parkingindex={} @@ -5059,26 +5062,26 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take if spawnonship or spawnonfarp or spawnonrunway then -- These places work procedural and have some kind of build in queue ==> Less effort. self:T(RAT.id..string.format("Group %s is spawned on farp/ship/runway %s.", self.alias, departure:GetName())) - nfree=departure:GetFreeParkingSpotsNumber(termtype, spawnonship or spawnonfarp or spawnonrunway) - spots=departure:GetFreeParkingSpotsTable(termtype, spawnonship or spawnonfarp or spawnonrunway) + nfree=departure:GetFreeParkingSpotsNumber(termtype, true) + spots=departure:GetFreeParkingSpotsTable(termtype, true) else -- Helo is spawned. if self.category==RAT.cat.heli then if termtype==nil then -- Try exclusive helo spots first. self:T(RAT.id..string.format("Helo group %s is spawned at %s using terminal type %d.", self.alias, departure:GetName(), AIRBASE.TerminalType.HelicopterOnly)) - spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe) + spots=departure:FindFreeParkingSpotForAircraft(TemplateGroup, AIRBASE.TerminalType.HelicopterOnly, scanradius, scanunits, scanstatics, scanscenery, verysafe, nunits) nfree=#spots - if nfree<#SpawnTemplate.units then + if nfree= #SpawnTemplate.units or (spawnonrunway and nfree>0) then - - for i=1,#SpawnTemplate.units do - table.insert(parkingspots, spots[i].Coordinate) - table.insert(parkingindex, spots[i].TerminalID) + + -- Set this to true if not enough spots are available for emergency air start. + local _notenough=false + + -- Need to differentiate some cases again. + if spawnonship or spawnonfarp or spawnonrunway then + + -- On free spot required in these cases. + if nfree >=1 then + + -- All units get the same spot. DCS takes care of the rest. + for i=1,nunits do + table.insert(parkingspots, spots[1].Coordinate) + table.insert(parkingindex, spots[1].TerminalID) + end + -- This is actually used... + PointVec3=spots[1].Coordinate + + else + -- If there is absolutely not spot ==> air start! + _notenough=true end + + elseif spawnonairport then + + if nfree>=nunits then - else - if self.respawn_inair and not self.uncontrolled then - self:E(RAT.id..string.format("WARNING: RAT group %s has no parking spots at %s ==> air start!", self.alias, departure:GetName())) + for i=1,nunits do + table.insert(parkingspots, spots[i].Coordinate) + table.insert(parkingindex, spots[i].TerminalID) + end + + else + -- Not enough spots for the whole group ==> air start! + _notenough=true + end + end + + -- Not enough spots ==> Prepare airstart. + if _notenough then + + if self.respawn_inair and not self.SpawnUnControlled then + self:E(RAT.id..string.format("WARNING: Group %s has no parking spots at %s ==> air start!", self.SpawnTemplatePrefix, departure:GetName())) -- Not enough parking spots at the airport ==> Spawn in air. spawnonground=false @@ -5142,27 +5177,37 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take spawnonrunway=false -- Set waypoint type/action to turning point. - waypoints[1].type = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][1] -- type = Turning Point + waypoints[1].type = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][1] -- type = Turning Point waypoints[1].action = GROUPTEMPLATE.Takeoff[GROUP.Takeoff.Air][2] -- action = Turning Point - -- Adjust and randomize position and altitude of the spawn point. + -- Adjust altitude to be 500-1000 m above the airbase. PointVec3.x=PointVec3.x+math.random(-500,500) - PointVec3.z=PointVec3.z+math.random(-500,500) + PointVec3.z=PointVec3.z+math.random(-500,500) if self.category==RAT.cat.heli then PointVec3.y=PointVec3:GetLandHeight()+math.random(100,1000) else + -- Randomize position so that multiple AC wont be spawned on top even in air. PointVec3.y=PointVec3:GetLandHeight()+math.random(500,2500) end else - self:E(RAT.id..string.format("WARNING: RAT group %s has no parking spots at %s. Air start deactivated or uncontrolled AC!", self.alias, departure:GetName())) + self:E(RAT.id..string.format("WARNING: Group %s has no parking spots at %s ==> No emergency air start or uncontrolled spawning ==> No spawn!", self.SpawnTemplatePrefix, departure:GetName())) return nil - end + end end + + else + + -- Air start requested initially! + --PointVec3.y is already set from first waypoint here! + end - + + +--- new + -- Translate the position of the Group Template to the Vec3. - for UnitID = 1, #SpawnTemplate.units do + for UnitID = 1, nunits do -- Template of the current unit. local UnitTemplate = SpawnTemplate.units[UnitID] diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 0b4663bb5..635cbfe4a 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -589,8 +589,9 @@ end -- @param #boolean scanstatics (Optional) Scan for statics as obstacles. Default true. -- @param #boolean scanscenery (Optional) Scan for scenery as obstacles. Default false. Can cause problems with e.g. shelters. -- @param #boolean verysafe (Optional) If true, wait until an aircraft has taken off until the parking spot is considered to be free. Defaul false. +-- @param #number nspots (Optional) Number of freeparking spots requested. Default is the number of aircraft in the group. -- @return #table Table of coordinates and terminal IDs of free parking spots. Each table entry has the elements .Coordinate and .TerminalID. -function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, scanunits, scanstatics, scanscenery, verysafe) +function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nspots) -- Init default scanradius=scanradius or 50 @@ -646,11 +647,13 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, -- Get the aircraft size, i.e. it's longest side of x,z. local aircraft=group:GetUnit(1) - local nspots=group:GetSize() local _aircraftsize, ax,ay,az=_GetObjectSize(aircraft, true) + -- Number of spots we are looking for. Note that, e.g. grouping can require a number different from the group size! + local _nspots=nspots or group:GetSize() + -- Debug info. - self:E(string.format("Looking for %d parking spot(s) at %s for aircraft of size %.1f m (x=%.1f,y=%.1f,z=%.1f) at termial type %s.", nspots, airport, _aircraftsize, ax, ay, az, tostring(terminaltype))) + self:E(string.format("%s: Looking for %d parking spot(s) for aircraft of size %.1f m (x=%.1f,y=%.1f,z=%.1f) at termial type %s.", airport, _nspots, _aircraftsize, ax, ay, az, tostring(terminaltype))) -- Table of valid spots. local validspots={} @@ -743,7 +746,7 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, self:T(string.format("%s: Parking spot id %d occupied.", airport, _termid)) else self:E(string.format("%s: Parking spot id %d free.", airport, _termid)) - if nvalid=nspots then + if nvalid>=_nspots then return validspots end end From 7b1825aca539743dc79c583862ab123bdb89a9b8 Mon Sep 17 00:00:00 2001 From: funkyfranky <> Date: Sat, 30 Jun 2018 00:02:46 +0200 Subject: [PATCH 196/420] SPAWN set correct initial heading --- Moose Development/Moose/Core/Spawn.lua | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index ebf4f65ea..a9817109e 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1102,10 +1102,21 @@ function SPAWN:SpawnWithIndex( SpawnIndex ) end end + -- Get correct heading. + local function _Heading(course) + local h + if course<=180 then + h=math.rad(course) + else + h=-math.rad(360-course) + end + return h + end + -- If Heading is given, point all the units towards the given Heading. if self.SpawnInitHeadingMin then for UnitID = 1, #SpawnTemplate.units do - SpawnTemplate.units[UnitID].heading = self.SpawnInitHeadingMax and math.random( self.SpawnInitHeadingMin, self.SpawnInitHeadingMax ) or self.SpawnInitHeadingMin + SpawnTemplate.units[UnitID].heading = _Heading(self.SpawnInitHeadingMax and math.random( self.SpawnInitHeadingMin, self.SpawnInitHeadingMax ) or self.SpawnInitHeadingMin) SpawnTemplate.units[UnitID].psi = -SpawnTemplate.units[UnitID].heading end end From 3c2ff2d7a11bf738bcc6df980dba8eac7175c7fa Mon Sep 17 00:00:00 2001 From: Van De Velde Date: Sat, 30 Jun 2018 09:37:28 +0200 Subject: [PATCH 197/420] Updated documentation --- Moose Development/Moose/Tasking/Task.lua | 262 +++++++-- .../Moose/Tasking/Task_CARGO.lua | 508 +++++++++--------- .../Moose/Tasking/Task_Cargo_CSAR.lua | 150 +++++- .../Moose/Tasking/Task_Cargo_Transport.lua | 139 ++++- 4 files changed, 753 insertions(+), 306 deletions(-) diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index 7b645c2ad..84f8158d0 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -2,6 +2,7 @@ -- -- **Features:** -- +-- * A base class for other task classes filling in the details and making a concrete task process. -- * Manage the overall task execution, following-up the progression made by the pilots and actors. -- * Provide a mechanism to set a task status, depending on the progress made within the task. -- * Manage a task briefing. @@ -11,6 +12,200 @@ -- -- === -- +-- # 1) Tasking from a player perspective. +-- +-- Tasking can be controlled by using the "other" menu in the radio menu of the player group. +-- +-- ![Other Menu](../Tasking/Menu_Main.JPG) +-- +-- ## 1.1) Command Centers govern multiple Missions. +-- +-- Depending on the tactical situation, your coalition may have one (or multiple) command center(s). +-- These command centers govern one (or multiple) mission(s). +-- +-- For each command center, there will be a separate **Command Center Menu** that focuses on the missions governed by that command center. +-- +-- ![Command Center](../Tasking/Menu_CommandCenter.JPG) +-- +-- In the above example menu structure, there is one command center with the name **`[Lima]`**. +-- The command center has one @{Tasking.Mission}, named **`"Overlord"`** with **`High`** priority. +-- +-- ## 1.2) Missions govern multiple Tasks. +-- +-- A mission has a mission goal to be achieved by the players within the coalition. +-- The mission goal is actually dependent on the tactical situation of the overall battlefield and the conditions set to achieve the goal. +-- So a mission can be much more than just shoot stuff ... It can be a combination of different conditions or events to complete a mission goal. +-- +-- A mission can be in a specific state during the simulation run. For more information about these states, please check the @{Tasking.Mission} section. +-- +-- To achieve the mission goal, a mission administers @{Tasking.Task}s that are set to achieve the mission goal by the human players. +-- Each of these tasks can be **dynamically created** using a task dispatcher, or **coded** by the mission designer. +-- Each mission has a separate **Mission Menu**, that focuses on the administration of these tasks. +-- +-- On top, a mission has a mission briefing, can help to allocate specific points of interest on the map, and provides various reports. +-- +-- ![Mission](../Tasking/Menu_Mission.JPG) +-- +-- The above shows a mission menu in detail of **`"Overlord"`**. +-- +-- The two other menus are related to task assignment. Which will be detailed later. +-- +-- ### 1.2.1) Mission briefing. +-- +-- The task briefing will show a message containing a description of the mission goal, and other tactical information. +-- +-- ![Mission](../Tasking/Report_Briefing.JPG) +-- +-- ### 1.2.2) Mission Map Locations. +-- +-- Various points of interest as part of the mission can be indicated on the map using the *Mark Task Locations on Map* menu. +-- As a result, the map will contain various points of interest for the player (group). +-- +-- ![Mission](../Tasking/Report_Mark_Task_Location.JPG) +-- +-- ### 1.2.3) Mission Task Reports. +-- +-- Various reports can be generated on the status of each task governed within the mission. +-- +-- ![Mission](../Tasking/Report_Task_Summary.JPG) +-- +-- The Task Overview Report will show each task, with its task status and a short coordinate information. +-- +-- ![Mission](../Tasking/Report_Tasks_Planned.JPG) +-- +-- The other Task Menus will show for each task more details, for example here the planned tasks report. +-- Note that the order of the tasks are shortest distance first to the unit position seated by the player. +-- +-- ### 1.2.4) Mission Statistics. +-- +-- Various statistics can be displayed regarding the mission. +-- +-- ![Mission](../Tasking/Report_Statistics_Progress.JPG) +-- +-- A statistic report on the progress of the mission. Each task achievement will increase the %-tage to 100% as a goal to complete the task. +-- +-- ## 1.3) Join a Task. +-- +-- The mission menu contains a very important option, that is to join a task governed within the mission. +-- In order to join a task, select the **Join Planned Task** menu, and a new menu will be given. +-- +-- ![Mission](../Tasking/Menu_Join_Planned_Tasks.JPG) +-- +-- A mission governs multiple tasks, as explained earlier. Each task is of a certain task type. +-- This task type was introduced to have some sort of task classification system in place for the player. +-- A short acronym is shown that indicates the task type. The meaning of each acronym can be found in the task types explanation. +-- +-- ![Mission](../Tasking/Menu_Join_Tasks.JPG) +-- +-- When the player selects a task type, a list of the available tasks of that type are listed... +-- In this case the **`SEAD`** task type was selected and a list of available **`SEAD`** tasks can be selected. +-- +-- ![Mission](../Tasking/Menu_Join_Planned_Task.JPG) +-- +-- A new list of menu options are now displayed that allow to join the task selected, but also to obtain first some more information on the task. +-- +-- ### 1.3.1) Report Task Details. +-- +-- ![Mission](../Tasking/Report_Task_Detailed.JPG) +-- +-- When selected, a message is displayed that shows detailed information on the task, like the coordinate, enemy target information, threat level etc. +-- +-- ### 1.3.2) Mark Task Location on Map. +-- +-- ![Mission](../Tasking/Report_Task_Detailed.JPG) +-- +-- When selected, the target location on the map is indicated with specific information on the task. +-- +-- ### 1.3.3) Join Task. +-- +-- ![Mission](../Tasking/Report_Task_Detailed.JPG) +-- +-- By joining a task, the player will indicate that the task is assigned to him, and the task is started. +-- The Command Center will communicate several task details to the player and the coalition of the player. +-- +-- ## 1.4) Task Control and Actions. +-- +-- ![Mission](../Tasking/Menu_Main_Task.JPG) +-- +-- When a player has joined a task, a **Task Action Menu** is available to be used by the player. +-- +-- ![Mission](../Tasking/Menu_Task.JPG) +-- +-- The task action menu contains now menu items specific to the task, but also one generic menu item, which is to control the task. +-- This **Task Control Menu** allows to display again the task details and the task map location information. +-- But it also allows to abort a task! +-- +-- Depending on the task type, the task action menu can contain more menu items which are specific to the task. +-- For example, cargo transportation tasks will contain various additional menu items to select relevant cargo coordinates, +-- or to load/unload cargo. +-- +-- ## 1.5) Automatic task assignment. +-- +-- ![Command Center](../Tasking/Menu_CommandCenter.JPG) +-- +-- When we take back the command center menu, you see two addtional **Assign Task** menu items. +-- The menu **Assign Task On** will automatically allocate a task to the player. +-- After the selection of this menu, the menu will change into **Assign Task Off**, +-- and will need to be selected again by the player to switch of the automatic task assignment. +-- +-- The other option is to select **Assign Task**, which will assign a new random task to the player. +-- +-- When a task is automatically assigned to a player, the task needs to be confirmed as accepted within 30 seconds. +-- If this is not the case, the task will be cancelled automatically, and a new random task will be assigned to the player. +-- This will continue to happen until the player accepts the task or switches off the automatic task assignment process. +-- +-- The player can accept the task using the menu **Confirm Task Acceptance** ... +-- +-- ## 1.6) Task states. +-- +-- A task has a state, reflecting the progress or completion status of the task: +-- +-- - **Planned**: Expresses that the task is created, but not yet in execution and is not assigned yet to a pilot. +-- - **Assigned**: Expresses that the task is assigned to a group of pilots, and that the task is in execution mode. +-- - **Success**: Expresses the successful execution and finalization of the task. +-- - **Failed**: Expresses the failure of a task. +-- - **Abort**: Expresses that the task is aborted by by the player using the abort menu. +-- - **Cancelled**: Expresses that the task is cancelled by HQ or through a logical situation where a cancellation of the task is required. +-- +-- ### 1.6.1) Task progress. +-- +-- The task governor takes care of the **progress** and **completion** of the task **goal(s)**. +-- Tasks are executed by **human pilots** and actors within a DCS simulation. +-- Pilots can use a **menu system** to engage or abort a task, and provides means to +-- understand the **task briefing** and goals, and the relevant **task locations** on the map and +-- obtain **various reports** related to the task. +-- +-- ### 1.6.2) Task completion. +-- +-- As the task progresses, the **task status** will change over time, from Planned state to Completed state. +-- **Multiple pilots** can execute the same task, as such, the tasking system provides a **co-operative model** for joint task execution. +-- Depending on the task progress, a **scoring** can be allocated to award pilots of the achievements made. +-- The scoring is fully flexible, and different levels of awarding can be provided depending on the task type and complexity. +-- +-- A normal flow of task status would evolve from the **Planned** state, to the **Assigned** state ending either in a **Success** or a **Failed** state. +-- +-- Planned -> Assigned -> Success +-- -> Failed +-- -> Cancelled +-- +-- The state completion is by default set to **Success**, if the goals of the task have been reached, but can be overruled by a goal method. +-- +-- Depending on the tactical situation, a task can be **Cancelled** by the mission governer. +-- It is actually the mission designer who has the flexibility to decide at which conditions a task would be set to **Success**, **Failed** or **Cancelled**. +-- This decision all depends on the task goals, and the phase/evolution of the task conditions that would accomplish the goals. +-- +-- For example, if the task goal is to merely destroy a target, and the target is mid-mission destroyed by another event than the pilot destroying the target, +-- the task goal could be set to **Failed**, or .. **Cancelled** ... +-- However, it could very well be also acceptable that the task would be flagged as **Success**. +-- +-- The tasking mechanism governs beside the progress also a scoring mechanism, and in case of goal completion without any active pilot involved +-- in the execution of the task, could result in a **Success** task completion status, but no score would be awared, as there were no players involved. +-- +-- These different completion states are important for the mission designer to reflect scoring to a player. +-- A success could mean a positive score to be given, while a failure could mean a negative score or penalties to be awarded. +-- +-- === +-- -- ### Author: **FlightControl** -- -- ### Contributions: @@ -34,8 +229,11 @@ -- -- A task is governed by a @{Tasking.Mission} object. Tasks are of different types. -- The @{#TASK} object is used or derived by more detailed tasking classes that will implement the task execution mechanisms --- and goals. The following TASK_ classes are derived from @{#TASK}. +-- and goals. -- +-- # 1) Derived task classes. +-- +-- The following TASK_ classes are derived from @{#TASK}. -- -- TASK -- TASK_A2A @@ -50,69 +248,25 @@ -- TASK_CARGO_TRANSPORT -- TASK_CARGO_CSAR -- --- --- --- #### A2A Tasks +-- ## 1.1) A2A Tasks -- -- - @{Tasking.Task_A2A#TASK_A2A_ENGAGE} - Models an A2A engage task of a target group of airborne intruders mid-air. -- - @{Tasking.Task_A2A#TASK_A2A_INTERCEPT} - Models an A2A ground intercept task of a target group of airborne intruders mid-air. -- - @{Tasking.Task_A2A#TASK_A2A_SWEEP} - Models an A2A sweep task to clean an area of previously detected intruders mid-air. -- --- #### A2G Tasks +-- ## 1.2) A2G Tasks -- -- - @{Tasking.Task_A2G#TASK_A2G_SEAD} - Models an A2G Suppression or Extermination of Air Defenses task to clean an area of air to ground defense threats. -- - @{Tasking.Task_A2G#TASK_A2G_CAS} - Models an A2G Close Air Support task to provide air support to nearby friendlies near the front-line. -- - @{Tasking.Task_A2G#TASK_A2G_BAI} - Models an A2G Battlefield Air Interdiction task to provide air support to nearby friendlies near the front-line. -- --- #### Cargo Tasks +-- ## 1.3) Cargo Tasks -- -- - @{Tasking.Task_Cargo#TASK_CARGO_TRANSPORT} - Models the transportation of cargo to deployment zones. -- - @{Tasking.Task_Cargo#TASK_CARGO_CSAR} - Models the rescue of downed friendly pilots from behind enemy lines. -- --- The above task objects take care of the **progress** and **completion** of the task **goal(s)**. --- Tasks are executed by **human pilots** and actors within a DCS simulation. --- Pilots can use a **menu system** to engage or abort a task, and provides means to --- understand the **task briefing** and goals, and the relevant **task locations** on the map and --- obtain **various reports** related to the task. -- --- As the task progresses, the **task status** will change over time, from Planned state to Completed state. --- **Multiple pilots** can execute the same task, as such, the tasking system provides a **co-operative model** for joint task execution. --- Depending on the task progress, a **scoring** can be allocated to award pilots of the achievements made. --- The scoring is fully flexible, and different levels of awarding can be provided depending on the task type and complexity. --- --- # 1) Task Statuses --- --- ## 1.1) Task status overview. --- --- A task has a state, reflecting the progress and completion of the task: --- --- - **Planned**: Expresses that the task is created, but not yet in execution and is not assigned yet to a pilot. --- - **Assigned**: Expresses that the task is assigned to a group of pilots, and that the task is in execution mode. --- - **Success**: Expresses the successful execution and finalization of the task. --- - **Failed**: Expresses the failure of a task. --- - **Abort**: Expresses that the task is aborted by by the player using the abort menu. --- - **Cancelled**: Expresses that the task is cancelled by HQ or through a logical situation where a cancellation of the task is required. --- --- --- A normal flow of task status would evolve from the **Planned** state, to the **Assigned** state ending either in a **Success** or a **Failed** state. --- --- Planned -> Assigned -> Success --- -> Failed --- --- The state completion is by default set to **Success**, if the goals of the task have been reached, but can be overruled by a goal method. --- --- Depending on the tactical situation, a task can be **Cancelled** by the mission governer. --- It is actually the mission designer who has the flexibility to decide at which conditions a task would be set to **Success**, **Failed** or **Cancelled**. --- This decision all depends on the task goals, and the phase/evolution of the task conditions that would accomplish the goals. --- --- For example, if the task goal is to merely destroy a target, and the target is mid-mission destroyed by another event than the pilot destroying the target, --- the task goal could be set to **Failed**, or .. **Cancelled** ... --- However, it could very well be also acceptable that the task would be flagged as **Success**. --- --- The tasking mechanism governs beside the progress also a scoring mechanism, and in case of goal completion without any active pilot involved --- in the execution of the task, could result in a **Success** task completion status, but no score would be awared, as there were no players involved. --- --- ## 1.2) Task status events. +-- # 2) Task status events. -- -- The task statuses can be set by using the following methods: -- @@ -125,12 +279,12 @@ -- The mentioned derived TASK_ classes are implementing the task status transitions out of the box. -- So no extra logic needs to be written. -- --- # 2) Goal conditions for a task. +-- # 3) Goal conditions for a task. -- -- Every 30 seconds, a @{#Task.Goal} trigger method is fired. -- You as a mission designer, can capture the **Goal** event trigger to check your own task goal conditions and take action! -- --- ## 2.1) Goal event handler `OnAfterGoal()`. +-- ## 3.1) Goal event handler `OnAfterGoal()`. -- -- And this is a really great feature! Imagine a task which has **several conditions to check** before the task can move into **Success** state. -- You can do this with the OnAfterGoal method. @@ -150,19 +304,19 @@ -- So the @{#TASK.OnAfterGoal}() event handler would be called every 30 seconds automatically, -- and within this method, you can now check the conditions and take respective action. -- --- ## 2.2) Goal event trigger `Goal()`. +-- ## 3.2) Goal event trigger `Goal()`. -- -- If you would need to check a goal at your own defined event timing, then just call the @{#TASK.Goal}() method within your logic. -- The @{#TASK.OnAfterGoal}() event handler would then directly be called and would execute the logic. -- Note that you can also delay the goal check by using the delayed event trigger syntax `:__Goal( Delay )`. -- -- --- # 3) Add scoring when reaching a certain task status: +-- # 4) Score task completion. -- -- Upon reaching a certain task status in a task, additional scoring can be given. If the Mission has a scoring system attached, the scores will be added to the mission scoring. -- Use the method @{#TASK.AddScore}() to add scores when a status is reached. -- --- # 4) Task briefing: +-- # 5) Task briefing. -- -- A task briefing is a text that is shown to the player when he is assigned to the task. -- The briefing is broadcasted by the command center owning the mission. diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index b10e9e1ab..c73114fac 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -1,17 +1,243 @@ ---- **Tasking** -- Base class to model tasks for players to transport @{Cargo}. +--- **Tasking** -- Base class to model tasks for players to transport @{Cargo.Cargo}. -- -- === -- --- The Moose framework provides various CARGO classes that allow DCS phisical or logical objects to be transported or sling loaded by Carriers. --- The CARGO_ classes, as part of the moose core, are able to Board, Load, UnBoard and UnLoad cargo between Carrier units. +-- # 1) Tasking system. -- --- This collection of classes in this module define tasks for human players to handle these cargo objects. --- Cargo can be transported, picked-up, deployed and sling-loaded from and to other places. +-- If you are not yet aware what the MOOSE tasking system is about, read FIRST the explanation on tasking **@{Tasking.Task}**. -- --- The following classes are important to consider: +-- === -- --- * @{#TASK_CARGO_TRANSPORT}: Defines a task for a human player to transport a set of cargo between various zones. --- * @{#TASK_CARGO_CSAR}: Defines a task for a human player to Search and Rescue wounded pilots. +-- # 2) Context of cargo tasking. +-- +-- The Moose framework provides various CARGO classes that allow DCS physical or logical objects to be transported or sling loaded by Carriers. +-- The CARGO_ classes, as part of the MOOSE core, are able to Board, Load, UnBoard and UnLoad cargo between Carrier units. +-- +-- The TASK\_CARGO class is not meant to use within your missions as a mission designer. It is a base class, and other classes are derived from it. +-- +-- The following TASK_CARGO_ classes are important, as they implement the CONCRETE tasks: +-- +-- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT}: Defines a task for a human player to transport a set of cargo between various zones. +-- * @{Tasking.Task_Cargo_CSAR#TASK_CARGO_CSAR}: Defines a task for a human player to Search and Rescue wounded pilots. +-- +-- However! The menu system and basic usage of the TASK_CARGO classes is explained in the @{#TASK_CARGO} class description. +-- So please browse further below to understand how to use it from a player perspective! +-- +-- === +-- +-- # 3) Cargo tasking from a player perspective. +-- +-- A human player can join the battle field in a client airborne slot or a ground vehicle within the CA module (ALT-J). +-- The player needs to accept the task from the task overview list within the mission, using the menus. +-- +-- Once the task is assigned to the player and accepted by the player, the player will obtain +-- an extra **Cargo (Radio) Menu** that contains the CARGO objects that need to be transported. +-- +-- Each @{Cargo.Cargo} object has a certain state: +-- +-- * **UnLoaded**: The cargo is located within the battlefield. It may still need to be transported. +-- * **Loaded**: The cargo is loaded within a Carrier. This can be your air unit, or another air unit, or even a vehicle. +-- * **Boarding**: The cargo is running or moving towards your Carrier for loading. +-- * **UnBoarding**: The cargo is driving or jumping out of your Carrier and moves to a location in the Deployment Zone. +-- +-- Cargo must be transported towards different Deployment @{Core.Zone}s. +-- +-- The Cargo Menu system allows to execute **various actions** to transport the cargo. +-- In the menu, you'll find for each CARGO, that is part of the scope of the task, various actions that can be completed. +-- Depending on the location of your Carrier unit, the menu options will vary. +-- +-- ## 3.1) Joining a Cargo Transport Task +-- +-- If you are unfamiliar with the tasking menu mechanism, it is highly recommended to read through +-- chapter 1 of the @{Tasking} description from a player perspective. +-- +-- This chapter explains all the different menu items that are available to control the tasking as a player. +-- Using the menu structure, you can join tasks either manually or automatically, and various +-- menu options are available to obtain more information and various reports on the tasks and mission statistics. +-- +-- From this moment on, you can Pickup cargo from a pickup location and Deploy cargo in deployment zones, using the **Task Action Menu**. +-- +-- ## 3.2) Task Action Menu. +-- +-- When a player has joined a **`CARGO`** task (type), for that player only, +-- it's **Task Action Menu** will show an additional menu options. +-- The task action menu will have the name of the task you currently joined and **`@ player name`**. +-- +-- From within this menu, you will be able to route to a cargo location, deploy zone, and load/unload cargo. +-- +-- ## 3.3) Pickup cargo by Boarding, Loading and Sling Loading. +-- +-- There are three different ways how cargo can be picked up: +-- +-- - **Boarding**: Moveable cargo (like infantry or vehicles), can be boarded, that means, the cargo will move towards your carrier to board. +-- However, it can only execute the boarding actions if it is within the foreseen **Reporting Range**. +-- Therefore, it is important that you steer your Carrier within the Reporting Range around the cargo, +-- so that boarding actions can be executed on the cargo. The reporting range is set by the mission designer. +-- Fortunately, the cargo is reporting to you when it is within reporting range. +-- +-- - **Loading**: Stationary cargo (like crates), which are heavy, can only be loaded or sling loaded, meaning, +-- your carrier must be close enough to the cargo to be able to load the cargo within the carrier bays. +-- Moose provides you with an additional menu system to load stationary cargo into your carrier bays using the menu. +-- These menu options will become available, when the carrier is within loading range. +-- The Moose cargo will report to the carrier when the range is close enough. The load range is set by the mission designer. +-- +-- - **Sling Loading**: Stationary cargo (like crates), which are heavy, can only be loaded or sling loaded, meaning, +-- your carrier must be close enough to the cargo to be able to load the cargo within the carrier bays. +-- Sling loading cargo is done using the default DCS menu system. However, Moose cargo will report to the carrier that +-- it is within sling loading range. +-- +-- In order to be able to pickup cargo, you'll need to know where the cargo is located, right? +-- Fortunately, if your Carrier is not within the reporting range of the cargo, the HQ can help to route you to the locations of cargo. +-- Use the task action menu to receive HQ help for this. +-- +-- ![Task_Types](../Tasking/Task_Cargo_Actions.JPG) +-- +-- Depending on the location within the battlefield, the task action menu will contain **Route options** that can be selected +-- to start the HQ sending you routing messages. +-- +-- When selected, the HQ will send you routing messages. +-- +-- ![Task_Types](../Tasking/Task_Cargo_Routing_LL.JPG) +-- An example of routing in LL mode. +-- +-- ![Task_Types](../Tasking/Task_Cargo_Routing_BR.JPG) +-- An example of routing in BR mode. +-- +-- Possible coordinate formats are: Bearing Range (BR), Lattitude Longitude (LL) or Military Grid System (MGRS). +-- Note that for LL, there are two sub formats. +-- +-- The routing messages are formulated in the coordinate format that is currently active as configured in your settings profile. +-- ![Task_Types](../Tasking/Task_Cargo_Settings.JPG) +-- Use the **Settings Menu** to select the coordinate format that you would like to use for location determination. +-- +-- +-- ### 3.3.1) Pickup Cargo. +-- +-- In order to pickup cargo, use the **task action menu** to **route to a specific cargo**. +-- When a cargo route is selected, the HQ will send you routing messages indicating the location of the cargo. +-- +-- Upon arrival at the cargo, and when the cargo is within **reporting range**, the cargo will contact you and **further instructions will be given**. +-- +-- - When your Carrier is airborne, you will receive instructions to land your Carrier. +-- The action will not be completed until you've landed your Carrier. +-- +-- - For ground carriers, you can just drive to the optimal cargo board or load position. +-- +-- It takes a bit of skill to land a helicopter near a cargo to be loaded, but that is part of the game, isn't it? +-- Expecially when you are landing in a "hot" zone, so when cargo is under immediate threat of fire. +-- +-- ### 3.3.2) Board Cargo. +-- +-- If your Carrier is within the **Reporting Range of the cargo**, and the cargo is **moveable**, the **cargo can be boarded**! +-- +-- Select the task action menu and now a **Board or Load option** will be listed with the cargo name next to it! +-- Select the option from the action menu, and the cargo will start moving towards your carrier. +-- +-- The moveable cargo will run in formation to your carrier, and will board one by one, depending on the near range set by the mission designer. +-- The near range as added because carriers can be large or small, depending on the object size of the carrier. +-- Note that multiple units may need to board your Carrier, so it is required to await the full boarding process. +-- Once the cargo is fully boarded within your Carrier, you will be notified of this. +-- +-- Note that for airborne Carriers, it is required to land first before the Boarding process can be initiated. +-- If during boarding the Carrier gets airborne, the boarding process will be cancelled. +-- +-- ### 3.3.3) Load Cargo. +-- +-- If your Carrier is within the **Loading Range of the cargo**, and the cargo is **stationary**, the **cargo can be loaded**, but not boarded! +-- +-- Select the task action menu and now a **Load option** will be listed with the cargo name next to it! +-- Select the option from the action menu, and the cargo will loaded into your carrier. +-- Once the cargo is loaded within your Carrier, you will be notified of this. +-- +-- Note that for airborne Carriers, it is required to land first right near the cargo, before the loading process can be initiated. +-- As stated, this requires some pilot skills :-) +-- +-- ### 3.3.4) Sling Load Cargo (helicopters only). +-- +-- If your Carrier is within the **Loading Range of the cargo**, and the cargo is **stationary**, the **cargo can also be sling loaded**! +-- Note that this is only possible for helicopters. +-- +-- To sling load cargo, there is no task action menu required. Just follow the normal sling loading procedure and the cargo will report. +-- Use the normal DCS sling loading menu system to hook the cargo you the cable attached on your helicopter. +-- +-- Again note that you may land firstly right next to the cargo, before the loading process can be initiated. +-- As stated, this requires some pilot skills :-) +-- +-- +-- ## 3.4) Deploy cargo by Unboarding, Unloading and Sling Deploying. +-- +-- There are two different ways how cargo can be deployed: +-- +-- - **Unboarding**: Moveable cargo (like infantry or vehicles), can be unboarded, that means, +-- the cargo will step out of the carrier and will run to a group location. +-- Moose provides you with an additional menu system to unload stationary cargo from the carrier bays, +-- using the menu. These menu options will become available, when the carrier is within the deploy zone. +-- +-- - **Unloading**: Stationary cargo (like crates), which are heavy, can only be unloaded or sling loaded. +-- Moose provides you with an additional menu system to unload stationary cargo from the carrier bays, +-- using the menu. These menu options will become available, when the carrier is within the deploy zone. +-- +-- - **Sling Deploying**: Stationary cargo (like crates), which are heavy, can also be sling deployed. +-- Once the cargo is within the deploy zone, the cargo can be deployed from the sling onto the ground. +-- +-- In order to be able to deploy cargo, you'll need to know where the deploy zone is located, right? +-- Fortunately, the HQ can help to route you to the locations of deploy zone. +-- Use the task action menu to receive HQ help for this. +-- +-- ![Task_Types](../Tasking/Task_Cargo_Actions.JPG) +-- +-- Depending on the location within the battlefield, the task action menu will contain **Route options** that can be selected +-- to start the HQ sending you routing messages. Also, if the carrier cargo bays contain cargo, +-- then beside **Route options** there will also be **Deploy options** listed. +-- These **Deploy options** are meant to route you to the deploy zone locations. +-- +-- Possible routing coordinate formats are: Bearing Range (BR), Lattitude Longitude (LL) or Military Grid System (MGRS). +-- Note that for LL, there are two sub formats. +-- +-- The routing messages are formulated in the coordinate format that is currently active as configured in your settings profile. +-- ![Task_Types](../Tasking/Task_Cargo_Settings.JPG) +-- Use the **Settings Menu** to select the coordinate format that you would like to use for location determination. +-- +-- ## 3.4) Deploy Cargo. +-- +-- Various Deployment Zones can be foreseen in the scope of the Cargo transportation. Each deployment zone can be of varying @{Zone} type. +-- The Cargo menu provides with menu options to execute an action to steer your Carrier to a specific Zone. +-- +-- In order to deploy cargo, use the task action menu to select a cargo to route to. +-- When selected, the HQ will send you routing messages indicating the location of the deploy zone. +-- +-- Upon arrival at the deploy zone, the HQ will contact you and further instructions will be given. +-- +-- ### 3.4.1) Unboard Cargo. +-- +-- If your Carrier is within the **deploy zone**, and the cargo is **moveable**, the **cargo can be unboarded**! +-- +-- Select the task action menu and now an **Unboard option** will be listed with the cargo name next to it! +-- Select the option from the action menu, and the cargo will step out of your carrier and will move towards a grouping point. +-- +-- The moveable cargo will unboard one by one, so note that multiple units may need to unboard your Carrier, +-- so it is required to await the full completion of the unboarding process. +-- Once the cargo is fully unboarded from your Carrier, you will be notified of this. +-- +-- Note that for airborne Carriers, it is required to land first before the unboarding process can be initiated. +-- If during unboarding the Carrier gets airborne, the unboarding process will be cancelled. +-- +-- ### 3.4.2) Unload Cargo. +-- +-- If your Carrier is within the **deploy zone**, and the cargo is **stationary**, the **cargo can be unloaded**, but not unboarded! +-- +-- Select the task action menu and now an **Unload option** will be listed with the cargo name next to it! +-- Select the option from the action menu, and the cargo will unloaded from your carrier. +-- Once the cargo is unloaded fom your Carrier, you will be notified of this. +-- +-- Note that for airborne Carriers, it is required to land first at the deploy zone, before the unloading process can be initiated. +-- +-- ### 3.4.3) Sling Deploy Cargo (helicopters only). +-- +-- If your Carrier is within the **deploy zone**, and the cargo is **stationary**, the **cargo can also be sling deploying**! +-- Note that this is only possible for helicopters. +-- +-- To sling deploy cargo, there is no task action menu required. Just follow the normal sling deploying procedure. -- -- === -- @@ -29,268 +255,59 @@ do -- TASK_CARGO --- @type TASK_CARGO -- @extends Tasking.Task#TASK - --- Base class to model tasks for players to transport @{Cargo}. + --- Model tasks for players to transport Cargo. -- - -- ## 1. A flexible tasking system + -- This models the process of a flexible transporation tasking system of cargo. + -- + -- # 1) A flexible tasking system. -- -- The TASK_CARGO classes provide you with a flexible tasking sytem, -- that allows you to transport cargo of various types between various locations -- and various dedicated deployment zones. -- - -- The cargo in scope of the TASK_CARGO classes must be explicitly given, and is of type SET_CARGO. + -- The cargo in scope of the TASK\_CARGO classes must be explicitly given, and is of type SET\_CARGO. -- The SET_CARGO contains a collection of CARGO objects that must be handled by the players in the mission. -- + -- # 2) Cargo Tasking from a mission designer perspective. -- - -- ## 2. Task execution experience from the player perspective + -- A cargo task is governed by a @{Tasking.Mission} object. Tasks are of different types. + -- The @{#TASK} object is used or derived by more detailed tasking classes that will implement the task execution mechanisms + -- and goals. -- - -- A human player can join the battle field in a client airborne slot or a ground vehicle within the CA module (ALT-J). - -- The player needs to accept the task from the task overview list within the mission, using the menus. + -- ## 2.1) Derived cargo task classes. -- - -- Once the TASK_CARGO is assigned to the player and accepted by the player, the player will obtain - -- an extra **Cargo (Radio) Menu** that contains the CARGO objects that need to be transported. + -- The following TASK_CARGO classes are derived from @{#TASK}. -- - -- Each CARGO object has a certain state: + -- TASK + -- TASK_CARGO + -- TASK_CARGO_TRANSPORT + -- TASK_CARGO_CSAR -- - -- * **UnLoaded**: The CARGO is located within the battlefield. It may still need to be transported. - -- * **Loaded**: The CARGO is loaded within a Carrier. This can be your air unit, or another air unit, or even a vehicle. - -- * **Boarding**: The CARGO is running or moving towards your Carrier for loading. - -- * **UnBoarding**: The CARGO is driving or jumping out of your Carrier and moves to a location in the Deployment Zone. + -- ### 2.1.1) Cargo Tasks -- - -- Cargo must be transported towards different Deployment @{Zone}s. - -- - -- The Cargo Menu system allows to execute **various actions** to transport the cargo. - -- In the menu, you'll find for each CARGO, that is part of the scope of the task, various actions that can be completed. - -- Depending on the location of your Carrier unit, the menu options will vary. - -- - -- ### 2.1. Joining a Cargo Transport Task - -- - -- Select __Join Tasks__, and you'll see a **Transport** task category. Select __Transport__ and you'll see the different tasks - -- listed. - -- - -- ![Task Types](../Tasking/###) - -- - -- Select one of the tasks ... - -- - -- ![Task_Types](../Tasking/###) - -- - -- Select Join Task ... - -- - -- After the menu "Join Task" selection, you are assigned to the Task. - -- - -- - ![Task_Types](../Tasking/Task_Briefing.JPG). - -- A briefing message is shown. - -- - The notification message is shown to all players, indicating that the cargo task is now assigned. - -- - When no task as part of the mission was assigned, the mission is set to **ONGOING**. - -- - -- From this moment on, you can Pickup cargo from a pickup location and Deploy cargo in deployment zones, using the **Task Action Menu**. - -- - -- ### 2.2. Task Action Menu. - -- - -- When a player has joined a task, for that player only, it's carrier Menu will show an additional menu option. - -- It has the name of the task you currently joined and @ player name. - -- - -- ![Task_Types](../Tasking/Task_Briefing.JPG). - -- For example, this shows the task __Transport Liquids.002@ Transport#013__. - -- - -- We call this menu option the **Task Action Menu**. - -- Under this menu option, there will be other menu options available which are specific to the task you just selected. - -- Depending on the task type, these menu options will vary. - -- - -- ### 2.2. Cancel a joined Cargo Transport Task. - -- - -- One more thing, it is possible to cancel a task that you joined. - -- ![Task_Types](../Tasking/###) - -- - -- When this option is selected, the player is removed to be assigned as part of the task. - -- If the player was the last player that was assigned to the task, the task is set to "Hold". - -- - -- ### 2.3. Pickup cargo by Boarding, Loading and Sling Loading. - -- - -- There are three different ways how cargo can be picked up: - -- - -- - **Boarding**: Moveable cargo (like infantry or vehicles), can be boarded, that means, the cargo will move towards your carrier to board. - -- However, it can only execute the boarding actions if it is within the foreseen **Reporting Range**. - -- Therefore, it is important that you steer your Carrier within the Reporting Range around the cargo, - -- so that boarding actions can be executed on the cargo. The reporting range is set by the mission designer. - -- Fortunately, the cargo is reporting to you when it is within reporting range. - -- - -- - **Loading**: Stationary cargo (like crates), which are heavy, can only be loaded or sling loaded, meaning, - -- your carrier must be close enough to the cargo to be able to load the cargo within the carrier bays. - -- Moose provides you with an additional menu system to load stationary cargo into your carrier bays using the menu. - -- These menu options will become available, when the carrier is within loading range. - -- The Moose cargo will report to the carrier when the range is close enough. The load range is set by the mission designer. + -- - @{Tasking.Task_Cargo#TASK_CARGO_TRANSPORT} - Models the transportation of cargo to deployment zones. + -- - @{Tasking.Task_Cargo#TASK_CARGO_CSAR} - Models the rescue of downed friendly pilots from behind enemy lines. -- - -- - **Sling Loading**: Stationary cargo (like crates), which are heavy, can only be loaded or sling loaded, meaning, - -- your carrier must be close enough to the cargo to be able to load the cargo within the carrier bays. - -- Sling loading cargo is done using the default DCS menu system. However, Moose cargo will report to the carrier that - -- it is within sling loading range. - -- - -- In order to be able to pickup cargo, you'll need to know where the cargo is located, right? - -- Fortunately, if your Carrier is not within the reporting range of the cargo, the HQ can help to route you to the locations of cargo. - -- Use the task action menu to receive HQ help for this. - -- - -- ![Task_Types](../Tasking/Task_Cargo_Actions.JPG) - -- - -- Depending on the location within the battlefield, the task action menu will contain **Route options** that can be selected - -- to start the HQ sending you routing messages. - -- - -- When selected, the HQ will send you routing messages. - -- - -- ![Task_Types](../Tasking/Task_Cargo_Routing_LL.JPG) - -- An example of routing in LL mode. - -- - -- ![Task_Types](../Tasking/Task_Cargo_Routing_BR.JPG) - -- An example of routing in BR mode. - -- - -- Possible coordinate formats are: Bearing Range (BR), Lattitude Longitude (LL) or Military Grid System (MGRS). - -- Note that for LL, there are two sub formats. - -- - -- The routing messages are formulated in the coordinate format that is currently active as configured in your settings profile. - -- ![Task_Types](../Tasking/Task_Cargo_Settings.JPG) - -- Use the **Settings Menu** to select the coordinate format that you would like to use for location determination. - -- - -- - -- #### 2.3.1. Pickup Cargo. - -- - -- In order to pickup cargo, use the **task action menu** to **route to a specific cargo**. - -- When a cargo route is selected, the HQ will send you routing messages indicating the location of the cargo. - -- - -- Upon arrival at the cargo, and when the cargo is within **reporting range**, the cargo will contact you and **further instructions will be given**. - -- - -- - When your Carrier is airborne, you will receive instructions to land your Carrier. - -- The action will not be completed until you've landed your Carrier. - -- - -- - For ground carriers, you can just drive to the optimal cargo board or load position. - -- - -- It takes a bit of skill to land a helicopter near a cargo to be loaded, but that is part of the game, isn't it? - -- Expecially when you are landing in a "hot" zone, so when cargo is under immediate threat of fire. - -- - -- #### 2.3.2. Board Cargo. - -- - -- If your Carrier is within the **Reporting Range of the cargo**, and the cargo is **moveable**, the **cargo can be boarded**! - -- - -- Select the task action menu and now a **Board or Load option** will be listed with the cargo name next to it! - -- Select the option from the action menu, and the cargo will start moving towards your carrier. - -- - -- The moveable cargo will run in formation to your carrier, and will board one by one, depending on the near range set by the mission designer. - -- The near range as added because carriers can be large or small, depending on the object size of the carrier. - -- Note that multiple units may need to board your Carrier, so it is required to await the full boarding process. - -- Once the cargo is fully boarded within your Carrier, you will be notified of this. - -- - -- Note that for airborne Carriers, it is required to land first before the Boarding process can be initiated. - -- If during boarding the Carrier gets airborne, the boarding process will be cancelled. - -- - -- #### 2.3.3. Load Cargo. - -- - -- If your Carrier is within the **Loading Range of the cargo**, and the cargo is **stationary**, the **cargo can be loaded**, but not boarded! - -- - -- Select the task action menu and now a **Load option** will be listed with the cargo name next to it! - -- Select the option from the action menu, and the cargo will loaded into your carrier. - -- Once the cargo is loaded within your Carrier, you will be notified of this. - -- - -- Note that for airborne Carriers, it is required to land first right near the cargo, before the loading process can be initiated. - -- As stated, this requires some pilot skills :-) - -- - -- #### 2.3.4. Sling Load Cargo (helicopters only). - -- - -- If your Carrier is within the **Loading Range of the cargo**, and the cargo is **stationary**, the **cargo can also be sling loaded**! - -- Note that this is only possible for helicopters. - -- - -- To sling load cargo, there is no task action menu required. Just follow the normal sling loading procedure and the cargo will report. - -- Use the normal DCS sling loading menu system to hook the cargo you the cable attached on your helicopter. - -- - -- Again note that you may land firstly right next to the cargo, before the loading process can be initiated. - -- As stated, this requires some pilot skills :-) - -- - -- - -- ### 2.4. Deploy cargo by Unboarding, Unloading and Sling Deploying. - -- - -- There are two different ways how cargo can be deployed: - -- - -- - **Unboarding**: Moveable cargo (like infantry or vehicles), can be unboarded, that means, - -- the cargo will step out of the carrier and will run to a group location. - -- Moose provides you with an additional menu system to unload stationary cargo from the carrier bays, - -- using the menu. These menu options will become available, when the carrier is within the deploy zone. - -- - -- - **Unloading**: Stationary cargo (like crates), which are heavy, can only be unloaded or sling loaded. - -- Moose provides you with an additional menu system to unload stationary cargo from the carrier bays, - -- using the menu. These menu options will become available, when the carrier is within the deploy zone. - -- - -- - **Sling Deploying**: Stationary cargo (like crates), which are heavy, can also be sling deployed. - -- Once the cargo is within the deploy zone, the cargo can be deployed from the sling onto the ground. - -- - -- In order to be able to deploy cargo, you'll need to know where the deploy zone is located, right? - -- Fortunately, the HQ can help to route you to the locations of deploy zone. - -- Use the task action menu to receive HQ help for this. - -- - -- ![Task_Types](../Tasking/Task_Cargo_Actions.JPG) - -- - -- Depending on the location within the battlefield, the task action menu will contain **Route options** that can be selected - -- to start the HQ sending you routing messages. Also, if the carrier cargo bays contain cargo, - -- then beside **Route options** there will also be **Deploy options** listed. - -- These **Deploy options** are meant to route you to the deploy zone locations. - -- - -- Possible routing coordinate formats are: Bearing Range (BR), Lattitude Longitude (LL) or Military Grid System (MGRS). - -- Note that for LL, there are two sub formats. - -- - -- The routing messages are formulated in the coordinate format that is currently active as configured in your settings profile. - -- ![Task_Types](../Tasking/Task_Cargo_Settings.JPG) - -- Use the **Settings Menu** to select the coordinate format that you would like to use for location determination. - -- - -- ### 2.4. Deploy Cargo. - -- - -- Various Deployment Zones can be foreseen in the scope of the Cargo transportation. Each deployment zone can be of varying @{Zone} type. - -- The Cargo menu provides with menu options to execute an action to steer your Carrier to a specific Zone. - -- - -- In order to deploy cargo, use the task action menu to select a cargo to route to. - -- When selected, the HQ will send you routing messages indicating the location of the deploy zone. - -- - -- Upon arrival at the deploy zone, the HQ will contact you and further instructions will be given. - -- - -- #### 2.4.1. Unboard Cargo. - -- - -- If your Carrier is within the **deploy zone**, and the cargo is **moveable**, the **cargo can be unboarded**! - -- - -- Select the task action menu and now an **Unboard option** will be listed with the cargo name next to it! - -- Select the option from the action menu, and the cargo will step out of your carrier and will move towards a grouping point. - -- - -- The moveable cargo will unboard one by one, so note that multiple units may need to unboard your Carrier, - -- so it is required to await the full completion of the unboarding process. - -- Once the cargo is fully unboarded from your Carrier, you will be notified of this. - -- - -- Note that for airborne Carriers, it is required to land first before the unboarding process can be initiated. - -- If during unboarding the Carrier gets airborne, the unboarding process will be cancelled. - -- - -- #### 2.4.2. Unload Cargo. - -- - -- If your Carrier is within the **deploy zone**, and the cargo is **stationary**, the **cargo can be unloaded**, but not unboarded! - -- - -- Select the task action menu and now an **Unload option** will be listed with the cargo name next to it! - -- Select the option from the action menu, and the cargo will unloaded from your carrier. - -- Once the cargo is unloaded fom your Carrier, you will be notified of this. - -- - -- Note that for airborne Carriers, it is required to land first at the deploy zone, before the unloading process can be initiated. - -- - -- #### 2.4.3. Sling Deploy Cargo (helicopters only). - -- - -- If your Carrier is within the **deploy zone**, and the cargo is **stationary**, the **cargo can also be sling deploying**! - -- Note that this is only possible for helicopters. - -- - -- To sling deploy cargo, there is no task action menu required. Just follow the normal sling deploying procedure. - -- - -- ## Handle TASK_CARGO Events ... + -- ## 2.2) Handle TASK_CARGO Events ... -- -- The TASK_CARGO classes define @{Cargo} transport tasks, -- based on the tasking capabilities defined in @{Tasking.Task#TASK}. -- - -- ### Specific TASK_CARGO Events + -- ### 2.2.1) Boarding events. -- -- Specific Cargo event can be captured, that allow to trigger specific actions! -- -- * **Boarded**: Triggered when the Cargo has been Boarded into your Carrier. -- * **UnBoarded**: Triggered when the cargo has been Unboarded from your Carrier and has arrived at the Deployment Zone. -- - -- ### Standard TASK_CARGO Events + -- ### 2.2.2) Loading events. + -- + -- Specific Cargo event can be captured, that allow to trigger specific actions! + -- + -- * **Loaded**: Triggered when the Cargo has been Loaded into your Carrier. + -- * **UnLoaded**: Triggered when the cargo has been Unloaded from your Carrier and has arrived at the Deployment Zone. + -- + -- ### 2.2.2) Standard TASK_CARGO Events -- -- The TASK_CARGO is implemented using a @{Core.Fsm#FSM_TASK}, and has the following standard statuses: -- @@ -303,7 +320,6 @@ do -- TASK_CARGO -- === -- -- @field #TASK_CARGO - -- TASK_CARGO = { ClassName = "TASK_CARGO", } diff --git a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua index 616f8a89b..1710a2866 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua @@ -1,4 +1,63 @@ ---- **Tasking** -- Models tasks for players to execute CSAR @{Cargo} downed pilots. +--- **Tasking** -- Orchestrates the task for players to execute CSAR for downed pilots @{Cargo.Cargo}. +-- +-- **Specific features:** +-- +-- * Creates a task to retrieve a pilot @{Cargo.Cargo} from behind enemy lines. +-- * Derived from the TASK_CARGO class, which is derived from the TASK class. +-- * Orchestrate the task flow, so go from Planned to Assigned to Success, Failed or Cancelled. +-- * Co-operation tasking, so a player joins a group of players executing the same task. +-- +-- +-- **A complete task menu system to allow players to:** +-- +-- * Join the task, abort the task. +-- * Mark the task location on the map. +-- * Provide details of the target. +-- * Route to the cargo. +-- * Route to the deploy zones. +-- * Load/Unload cargo. +-- * Board/Unboard cargo. +-- * Slingload cargo. +-- * Display the task briefing. +-- +-- +-- **A complete mission menu system to allow players to:** +-- +-- * Join a task, abort the task. +-- * Display task reports. +-- * Display mission statistics. +-- * Mark the task locations on the map. +-- * Provide details of the targets. +-- * Display the mission briefing. +-- * Provide status updates as retrieved from the command center. +-- * Automatically assign a random task as part of a mission. +-- * Manually assign a specific task as part of a mission. +-- +-- +-- **A settings system, using the settings menu:** +-- +-- * Tweak the duration of the display of messages. +-- * Switch between metric and imperial measurement system. +-- * Switch between coordinate formats used in messages: BR, BRA, LL DMS, LL DDM, MGRS. +-- * Different settings modes for A2G and A2A operations. +-- * Various other options. +-- +-- === +-- +-- Please read through the @{Tasking.Task_Cargo} process to understand the mechanisms of tasking and cargo tasking and handling. +-- +-- The cargo will be a downed pilot, which is located somwhere on the battlefield. Use the menus system and facilities to +-- join the CSAR task, and retrieve the pilot from behind enemy lines. The menu system is generic, there is nothing +-- specific on a CSAR task that requires further explanation, than reading the generic TASK_CARGO explanations. +-- +-- Enjoy! +-- FC +-- +-- === +-- +-- ### Author: **FlightControl** +-- +-- ### Contributions: -- -- === -- @@ -8,9 +67,94 @@ do -- TASK_CARGO_CSAR - --- The TASK_CARGO_CSAR class - -- @type TASK_CARGO_CSAR + --- @type TASK_CARGO_CSAR -- @extends Tasking.Task_Cargo#TASK_CARGO + + --- Orchestrates the task for players to execute CSAR for downed pilots. + -- + -- CSAR tasks are suited to govern the process of return downed pilots behind enemy lines back to safetly. + -- Typically, this task is executed by helicopter pilots, but it can also be executed by ground forces! + -- + -- === + -- + -- A CSAR task can be created manually, but actually, it is better to **GENERATE** these tasks using the + -- @{Tasking.Task_Cargo_Dispatcher} module. + -- + -- Using the dispatcher, CSAR tasks will be created **automatically** when a pilot ejects from a damaged AI aircraft. + -- When this happens, the pilot actually will survive, but needs to be retrieved from behind enemy lines. + -- + -- # 1) Create a CSAR task manually (code it). + -- + -- Although it is recommended to use the dispatcher, you can create a CSAR task yourself as a mission designer. + -- It is easy, as it works just like any other task setup. + -- + -- ## 1.1) Create a command center. + -- + -- First you need to create a command center using the @{Tasking.CommandCenter#COMMANDCENTER.New}() constructor. + -- + -- local CommandCenter = COMMANDCENTER + -- :New( HQ, "Lima" ) -- Create the CommandCenter. + -- + -- ## 1.2) Create a mission. + -- + -- Tasks work in a mission, which groups these tasks to achieve a joint mission goal. + -- A command center can govern multiple missions. + -- Create a new mission, using the @{Tasking.Mission#MISSION.New}() constructor. + -- + -- -- Declare the Mission for the Command Center. + -- local Mission = MISSION + -- :New( CommandCenter, + -- "Overlord", + -- "High", + -- "Retrieve the downed pilots.", + -- coalition.side.RED + -- ) + -- + -- ## 1.3) Create the CSAR cargo task. + -- + -- So, now that we have a command center and a mission, we now create the CSAR task. + -- We create the CSAR task using the @{#TASK_CARGO_CSAR.New}() constructor. + -- + -- Because a CSAR task will not generate the cargo itself, you'll need to create it first. + -- The cargo in this case will be the downed pilot! + -- + -- -- Here we define the "cargo set", which is a collection of cargo objects. + -- -- The cargo set will be the input for the cargo transportation task. + -- -- So a transportation object is handling a cargo set, which is automatically refreshed when new cargo is added/deleted. + -- local CargoSet = SET_CARGO:New():FilterTypes( "Pilots" ):FilterStart() + -- + -- -- Now we add cargo into the battle scene. + -- local PilotGroup = GROUP:FindByName( "Pilot" ) + -- + -- -- CARGO_GROUP can be used to setup cargo with a GROUP object underneath. + -- -- We name this group Engineers. + -- -- Note that the name of the cargo is "Engineers". + -- -- The cargoset "CargoSet" will embed all defined cargo of type "Pilots" (prefix) into its set. + -- local CargoGroup = CARGO_GROUP:New( PilotGroup, "Pilots", "Downed Pilot", 500 ) + -- + -- What is also needed, is to have a set of @{Core.Group}s defined that contains the clients of the players. + -- + -- -- Allocate the Transport, which are the helicopter to retrieve the pilot, that can be manned by players. + -- local GroupSet = SET_GROUP:New():FilterPrefixes( "Transport" ):FilterStart() + -- + -- Now that we have a CargoSet and a GroupSet, we can now create the CSARTask manually. + -- + -- -- Declare the CSAR task. + -- local CSARTask = TASK_CARGO_CSAR + -- :New( Mission, + -- GroupSet, + -- "CSAR Pilot", + -- CargoSet, + -- "Fly behind enemy lines, and retrieve the downed pilot." + -- ) + -- + -- So you can see, setting up a CSAR task manually is a lot of work. + -- It is better you use the cargo dispatcher to generate CSAR tasks and it will work as it is intended. + -- By doing this, CSAR tasking will become a dynamic experience. + -- + -- === + -- + -- @field #TASK_CARGO_CSAR TASK_CARGO_CSAR = { ClassName = "TASK_CARGO_CSAR", } diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua index 88bcb0ecb..57b5e04ec 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua @@ -1,4 +1,53 @@ ---- **Tasking** -- The TASK_CARGO models tasks for players to transport @{Cargo}. +--- **Tasking** -- Models tasks for players to transport @{Cargo.Cargo}. +-- +-- **Specific features:** +-- +-- * Creates a task to transport @{Cargo.Cargo} to and between deployment zones. +-- * Derived from the TASK_CARGO class, which is derived from the TASK class. +-- * Orchestrate the task flow, so go from Planned to Assigned to Success, Failed or Cancelled. +-- * Co-operation tasking, so a player joins a group of players executing the same task. +-- +-- +-- **A complete task menu system to allow players to:** +-- +-- * Join the task, abort the task. +-- * Mark the task location on the map. +-- * Provide details of the target. +-- * Route to the cargo. +-- * Route to the deploy zones. +-- * Load/Unload cargo. +-- * Board/Unboard cargo. +-- * Slingload cargo. +-- * Display the task briefing. +-- +-- +-- **A complete mission menu system to allow players to:** +-- +-- * Join a task, abort the task. +-- * Display task reports. +-- * Display mission statistics. +-- * Mark the task locations on the map. +-- * Provide details of the targets. +-- * Display the mission briefing. +-- * Provide status updates as retrieved from the command center. +-- * Automatically assign a random task as part of a mission. +-- * Manually assign a specific task as part of a mission. +-- +-- +-- **A settings system, using the settings menu:** +-- +-- * Tweak the duration of the display of messages. +-- * Switch between metric and imperial measurement system. +-- * Switch between coordinate formats used in messages: BR, BRA, LL DMS, LL DDM, MGRS. +-- * Different settings modes for A2G and A2A operations. +-- * Various other options. +-- +-- === +-- +-- Please read through the @{Tasking.Task_Cargo} process to understand the mechanisms of tasking and cargo tasking and handling. +-- +-- Enjoy! +-- FC -- -- === -- @@ -8,9 +57,93 @@ do -- TASK_CARGO_TRANSPORT - --- The TASK_CARGO_TRANSPORT class - -- @type TASK_CARGO_TRANSPORT + --- @type TASK_CARGO_TRANSPORT -- @extends Tasking.Task_CARGO#TASK_CARGO + + --- Orchestrates the task for players to transport cargo to or between deployment zones. + -- + -- Transport tasks are suited to govern the process of transporting cargo to specific deployment zones. + -- Typically, this task is executed by helicopter pilots, but it can also be executed by ground forces! + -- + -- === + -- + -- A transport task can be created manually, but actually, it is better to **GENERATE** these tasks using the + -- @{Tasking.Task_Cargo_Dispatcher} module. + -- + -- Using the dispatcher, transport tasks can be created much more easy. + -- + -- # 1) Create a transport task manually (code it). + -- + -- Although it is recommended to use the dispatcher, you can create a transport task yourself as a mission designer. + -- It is easy, as it works just like any other task setup. + -- + -- ## 1.1) Create a command center. + -- + -- First you need to create a command center using the @{Tasking.CommandCenter#COMMANDCENTER.New}() constructor. + -- + -- local CommandCenter = COMMANDCENTER + -- :New( HQ, "Lima" ) -- Create the CommandCenter. + -- + -- ## 1.2) Create a mission. + -- + -- Tasks work in a mission, which groups these tasks to achieve a joint mission goal. + -- A command center can govern multiple missions. + -- Create a new mission, using the @{Tasking.Mission#MISSION.New}() constructor. + -- + -- -- Declare the Mission for the Command Center. + -- local Mission = MISSION + -- :New( CommandCenter, + -- "Overlord", + -- "High", + -- "Transport the cargo to the deploy zones.", + -- coalition.side.RED + -- ) + -- + -- ## 1.3) Create the transport cargo task. + -- + -- So, now that we have a command center and a mission, we now create the transport task. + -- We create the transport task using the @{#TASK_CARGO_TRANSPORT.New}() constructor. + -- + -- Because a transport task will not generate the cargo itself, you'll need to create it first. + -- The cargo in this case will be the downed pilot! + -- + -- -- Here we define the "cargo set", which is a collection of cargo objects. + -- -- The cargo set will be the input for the cargo transportation task. + -- -- So a transportation object is handling a cargo set, which is automatically refreshed when new cargo is added/deleted. + -- local CargoSet = SET_CARGO:New():FilterTypes( "Cargo" ):FilterStart() + -- + -- -- Now we add cargo into the battle scene. + -- local PilotGroup = GROUP:FindByName( "Engineers" ) + -- + -- -- CARGO_GROUP can be used to setup cargo with a GROUP object underneath. + -- -- We name this group Engineers. + -- -- Note that the name of the cargo is "Engineers". + -- -- The cargoset "CargoSet" will embed all defined cargo of type "Pilots" (prefix) into its set. + -- local CargoGroup = CARGO_GROUP:New( PilotGroup, "Cargo", "Engineer Team 1", 500 ) + -- + -- What is also needed, is to have a set of @{Core.Group}s defined that contains the clients of the players. + -- + -- -- Allocate the Transport, which are the helicopter to retrieve the pilot, that can be manned by players. + -- local GroupSet = SET_GROUP:New():FilterPrefixes( "Transport" ):FilterStart() + -- + -- Now that we have a CargoSet and a GroupSet, we can now create the TransportTask manually. + -- + -- -- Declare the transport task. + -- local TransportTask = TASK_CARGO_TRANSPORT + -- :New( Mission, + -- GroupSet, + -- "Transport Engineers", + -- CargoSet, + -- "Fly behind enemy lines, and retrieve the downed pilot." + -- ) + -- + -- So you can see, setting up a transport task manually is a lot of work. + -- It is better you use the cargo dispatcher to create transport tasks and it will work as it is intended. + -- By doing this, cargo transport tasking will become a dynamic experience. + -- + -- === + -- + -- @field #TASK_CARGO_TRANSPORT TASK_CARGO_TRANSPORT = { ClassName = "TASK_CARGO_TRANSPORT", } From 8bee670bc991c61c74efa16e00fb49c586e09648 Mon Sep 17 00:00:00 2001 From: Van De Velde Date: Sun, 1 Jul 2018 08:54:46 +0200 Subject: [PATCH 198/420] Added first boarding cargo manual --- .../Moose/Tasking/Task_CARGO.lua | 94 ++++++++++++++----- .../Moose/Tasking/Task_Cargo_CSAR.lua | 2 +- .../Moose/Tasking/Task_Cargo_Transport.lua | 2 +- 3 files changed, 72 insertions(+), 26 deletions(-) diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index c73114fac..c2ffac9b3 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -1,10 +1,10 @@ ---- **Tasking** -- Base class to model tasks for players to transport @{Cargo.Cargo}. +--- **Tasking** -- Base class to model tasks for players to transport cargo. -- -- === -- -- # 1) Tasking system. -- --- If you are not yet aware what the MOOSE tasking system is about, read FIRST the explanation on tasking **@{Tasking.Task}**. +-- #### If you are not yet aware what the MOOSE tasking system is about, read FIRST the explanation on tasking **@{Tasking.Task}**. -- -- === -- @@ -48,20 +48,13 @@ -- -- ## 3.1) Joining a Cargo Transport Task -- --- If you are unfamiliar with the tasking menu mechanism, it is highly recommended to read through --- chapter 1 of the @{Tasking} description from a player perspective. --- --- This chapter explains all the different menu items that are available to control the tasking as a player. --- Using the menu structure, you can join tasks either manually or automatically, and various --- menu options are available to obtain more information and various reports on the tasks and mission statistics. --- --- From this moment on, you can Pickup cargo from a pickup location and Deploy cargo in deployment zones, using the **Task Action Menu**. +-- Once you've joined a task, using the **Join Planned Task Menu**, +-- you can Pickup cargo from a pickup location and Deploy cargo in deployment zones, using the **Task Action Menu**. -- -- ## 3.2) Task Action Menu. -- -- When a player has joined a **`CARGO`** task (type), for that player only, -- it's **Task Action Menu** will show an additional menu options. --- The task action menu will have the name of the task you currently joined and **`@ player name`**. -- -- From within this menu, you will be able to route to a cargo location, deploy zone, and load/unload cargo. -- @@ -87,29 +80,65 @@ -- it is within sling loading range. -- -- In order to be able to pickup cargo, you'll need to know where the cargo is located, right? --- Fortunately, if your Carrier is not within the reporting range of the cargo, the HQ can help to route you to the locations of cargo. +-- +-- Fortunately, if your Carrier is not within the reporting range of the cargo, +-- **the HQ can help to route you to the locations of cargo**. +-- +-- ![Task_Types](../Tasking/Task_Cargo_Main_Menu.JPG) +-- -- Use the task action menu to receive HQ help for this. -- --- ![Task_Types](../Tasking/Task_Cargo_Actions.JPG) +-- ![Task_Types](../Tasking/Task_Cargo_Action_Menu.JPG) -- -- Depending on the location within the battlefield, the task action menu will contain **Route options** that can be selected -- to start the HQ sending you routing messages. +-- The **route options will vary**, depending on the position of your carrier, and the location of the cargo and the deploy zones. +-- Note that the route options will **only be created** for cargo that is **in scope of your cargo transportation task**, +-- so there may be other cargo objects within the DCS simulation, but if those belong to other cargo transportations tasks, +-- then no routing options will be shown for these cargo. +-- This is done to ensure that **different teams** have a **defined scope** for defined cargo, and that **multiple teams** can join +-- **multiple tasks**, transporting cargo **simultaneously** in a **cooperation**. +-- +-- In this example, there is a menu option to **Route to pickup cargo...". +-- Use this menu to route towards cargo locations for pickup into your carrier. +-- +-- ![Task_Types](../Tasking/Task_Cargo_Types_Menu.JPG) +-- +-- When you select this menu, you'll see a new menu listing the different cargo types that are out there in the dcs simulator. +-- These cargo types are symbolic names that are assigned by the mission designer, like oil, liquid, engineers, food, workers etc. +-- MOOSE has introduced this concept to allow mission designers to make different cargo types for different purposes. +-- Only the creativity of the mission designer limits now the things that can be done with cargo ... +-- Okay, let's continue ..., and let's select Oil ... -- -- When selected, the HQ will send you routing messages. -- --- ![Task_Types](../Tasking/Task_Cargo_Routing_LL.JPG) --- An example of routing in LL mode. --- -- ![Task_Types](../Tasking/Task_Cargo_Routing_BR.JPG) +-- -- An example of routing in BR mode. -- --- Possible coordinate formats are: Bearing Range (BR), Lattitude Longitude (LL) or Military Grid System (MGRS). --- Note that for LL, there are two sub formats. +-- Note that the coordinate display format in the message can be switched between LL DMS, LL DDM, MGRS and BR. -- --- The routing messages are formulated in the coordinate format that is currently active as configured in your settings profile. --- ![Task_Types](../Tasking/Task_Cargo_Settings.JPG) --- Use the **Settings Menu** to select the coordinate format that you would like to use for location determination. +-- ![Task_Types](../Tasking/Main_Settings.JPG) +-- +-- Use the @{Core.Settings} menu to change your display format preferences. -- +-- ![Task_Types](../Tasking/Settings_A2G_Coordinate.JPG) +-- +-- There you can change the display format to another format that suits your need. +-- Because cargo transportation is Air 2 Ground oriented, you need to select the A2G coordinate format display options. +-- Note that the main settings menu contains much more +-- options to control your display formats, like switch to metric and imperial, or change the duration of the display messages. +-- +-- ![Task_Types](../Tasking/Task_Cargo_Routing_LL.JPG) +-- +-- Here I changed the routing display format to LL DMS. +-- +-- One important thing to know, is that the routing messages will flash at regular time intervals. +-- When using BR coordinate display format, the **distance and angle will change accordingly** from your carrier position and the location of the cargo. +-- +-- Another important note is the routing towards deploy zones. +-- These routing options will only be shown, when your carrier bays have cargo loaded. +-- So, only when there is something to be deployed from your carrier, the deploy options will be shown. -- -- ### 3.3.1) Pickup Cargo. -- @@ -126,16 +155,33 @@ -- It takes a bit of skill to land a helicopter near a cargo to be loaded, but that is part of the game, isn't it? -- Expecially when you are landing in a "hot" zone, so when cargo is under immediate threat of fire. -- --- ### 3.3.2) Board Cargo. +-- ### 3.3.2) Board Cargo (infantry). +-- +-- ![Task_Types](../Tasking/Boarding_Ready.JPG) -- -- If your Carrier is within the **Reporting Range of the cargo**, and the cargo is **moveable**, the **cargo can be boarded**! +-- This type of cargo will be most of the time be infantry. -- --- Select the task action menu and now a **Board or Load option** will be listed with the cargo name next to it! --- Select the option from the action menu, and the cargo will start moving towards your carrier. +-- ![Boarding](../Tasking/Boarding_Menu.JPG) +-- +-- A board menu has appeared, because your carrier is in boarding range of the cargo (infantry). +-- +-- ![Boarding](../Tasking/Boarding_Started.JPG) +-- +-- Select the option from the action menu, then select the cargo to be boarded, and the cargo will start moving towards your carrier. +-- Note that a message is displayed by the infantry cargo that boarding has started. +-- +-- ![Boarding](../Tasking/Boarding_Ongoing.JPG) -- -- The moveable cargo will run in formation to your carrier, and will board one by one, depending on the near range set by the mission designer. -- The near range as added because carriers can be large or small, depending on the object size of the carrier. +-- +-- ![Boarding](../Tasking/Boarding_Almost_Done.JPG) +-- -- Note that multiple units may need to board your Carrier, so it is required to await the full boarding process. +-- +-- ![Boarding](../Tasking/Boarding_Done.JPG) +-- -- Once the cargo is fully boarded within your Carrier, you will be notified of this. -- -- Note that for airborne Carriers, it is required to land first before the Boarding process can be initiated. diff --git a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua index 1710a2866..9d74b0743 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua @@ -1,4 +1,4 @@ ---- **Tasking** -- Orchestrates the task for players to execute CSAR for downed pilots @{Cargo.Cargo}. +--- **Tasking** -- Orchestrates the task for players to execute CSAR for downed pilots. -- -- **Specific features:** -- diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua index 57b5e04ec..621be319d 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua @@ -1,4 +1,4 @@ ---- **Tasking** -- Models tasks for players to transport @{Cargo.Cargo}. +--- **Tasking** -- Models tasks for players to transport cargo. -- -- **Specific features:** -- From 8fb6fc8c6d4e90572996069a4b2f995fb4bb3c59 Mon Sep 17 00:00:00 2001 From: Van De Velde Date: Sun, 1 Jul 2018 09:07:31 +0200 Subject: [PATCH 199/420] Small change --- Moose Development/Moose/Tasking/Task_CARGO.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index c2ffac9b3..c78d6a69d 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -168,6 +168,7 @@ -- -- ![Boarding](../Tasking/Boarding_Started.JPG) -- +-- -- Select the option from the action menu, then select the cargo to be boarded, and the cargo will start moving towards your carrier. -- Note that a message is displayed by the infantry cargo that boarding has started. -- From 2faf7631cbf88d34c5d5055bdcf23db8c5a5be17 Mon Sep 17 00:00:00 2001 From: funkyfranky <> Date: Sun, 1 Jul 2018 21:22:38 +0200 Subject: [PATCH 200/420] RATMANAGER RATMANAGER: - Added interval between spawns. RAT: - changed some default parameter values --- Moose Development/Moose/Functional/RAT.lua | 93 +++++++++++++--------- 1 file changed, 54 insertions(+), 39 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 142dd60cb..fafa78686 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -385,12 +385,12 @@ RAT={ onboardnum=nil, -- Tail number. onboardnum0=1, -- (Optional) Starting value of the automatically appended numbering of aircraft within a flight. Default is one. checkonrunway=true, -- Check whether aircraft have been spawned on the runway. - onrunwayradius=75, -- Distance from a runway spawn point until a unit is considered to have accidentally been spawned on a runway. + onrunwayradius=75, -- Distance from a runway spawn point until a unit is considered to have accidentally been spawned on a runway. onrunwaymaxretry=3, -- Number of respawn retries (on ground) at other airports if a group gets accidentally spawned on the runway. checkontop=false, -- Check whether aircraft have been spawned on top of another unit. ontopradius=2, -- Radius in meters until which a unit is considered to be on top of another. termtype=nil, -- Terminal type. - parkingscanradius=50, -- Scan radius. + parkingscanradius=40, -- Scan radius. parkingscanscenery=false, -- Scan parking spots for scenery obstacles. parkingverysafe=false, -- Very safe option. } @@ -3605,7 +3605,7 @@ function RAT:_OnBirth(EventData) end self:_SetStatus(SpawnGroup, status) - -- Get some info ablout this flight. + -- Get some info ablout this flight. local i=self:GetSpawnIndexFromGroup(SpawnGroup) local _departure=self.ratcraft[i].departure:GetName() local _destination=self.ratcraft[i].destination:GetName() @@ -3674,7 +3674,7 @@ function RAT:_OnBirth(EventData) end if ontop then - local text=string.format("ERROR: RAT group of %s was spawned on top of another unit. Group #%d will be despawned immediately!", self.alias, i) + local text=string.format("ERROR: Group of %s was spawned on top of another unit. Group #%d will be despawned immediately!", self.alias, i) MESSAGE:New(text,30):ToAllIf(self.Debug) self:T(RAT.id..text) if self.Debug then @@ -4283,16 +4283,13 @@ function RAT:_Waypoint(index, description, Type, Coord, Speed, Altitude, Airport if AirbaseCategory == Airbase.Category.SHIP then RoutePoint.linkUnit = AirbaseID RoutePoint.helipadId = AirbaseID - --self:T(RAT.id.."WP: Ship id = "..AirbaseID) elseif AirbaseCategory == Airbase.Category.HELIPAD then RoutePoint.linkUnit = AirbaseID RoutePoint.helipadId = AirbaseID - --self:T(RAT.id.."WP: Helipad id = "..AirbaseID) elseif AirbaseCategory == Airbase.Category.AIRDROME then RoutePoint.airdromeId = AirbaseID - --self:T(RAT.id.."WP: Airdrome id = "..AirbaseID) else - --self:E(RAT.id.."Unknown Airport categoryin _Waypoint()!") + self:T(RAT.id.."Unknown Airport category in _Waypoint()!") end end -- properties @@ -5074,7 +5071,7 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take nfree=#spots if nfree No emergency air start or uncontrolled spawning ==> No spawn!", self.SpawnTemplatePrefix, departure:GetName())) @@ -5625,7 +5622,8 @@ end -- @field #table min Minimum number of RAT groups alive. -- @field #number nrat Number of RAT objects. -- @field #number ntot Total number of active RAT groups. --- @field #number Tcheck Time interval between checking of alive groups. +-- @field #number Tcheck Time interval in seconds between checking of alive groups. +-- @field #number dTspawn Time interval in seconds between spawns of groups. -- @field Core.Scheduler#SCHEDULER manager Scheduler managing the RAT objects. -- @field #number managerid Managing scheduler id. -- @extends Core.Base#BASE @@ -5676,7 +5674,8 @@ RATMANAGER={ min={}, nrat=0, ntot=nil, - Tcheck=30, + Tcheck=60, + dTspawn=1.0, manager=nil, managerid=nil, } @@ -5772,21 +5771,31 @@ function RATMANAGER:_Start() local N=self:_RollDice(self.nrat, self.ntot, self.min, self.alive) -- Loop over all RAT objects and spawn groups. + local time=0.0 for i=1,self.nrat do for j=1,N[i] do - self.rat[i]:_SpawnWithRoute() - end - -- Start activation scheduler for uncontrolled aircraft. - if self.rat[i].uncontrolled and self.rat[i].activate_uncontrolled then - SCHEDULER:New(nil, self.rat[i]._ActivateUncontrolled, {self.rat[i]}, self.rat[i].activate_delay, self.rat[i].activate_delta, self.rat[i].activate_frand) + time=time+self.dTspawn + SCHEDULER:New(nil, RAT._SpawnWithRoute, {self.rat[i]}, time) end end + -- Start activation scheduler for uncontrolled aircraft. + for i=1,self.nrat do + if self.rat[i].uncontrolled and self.rat[i].activate_uncontrolled then + -- Start activating stuff but not before the latest spawn has happend. + local Tactivate=math.max(time+1, self.rat[i].activate_delay) + SCHEDULER:New(self.rat[i], self.rat[i]._ActivateUncontrolled, {self.rat[i]}, Tactivate, self.rat[i].activate_delta, self.rat[i].activate_frand) + end + end + + -- Start the manager. But not earlier than the latest spawn has happened! + local TstartManager=math.max(time+1, self.Tcheck) + -- Start manager scheduler. - self.manager, self.managerid = SCHEDULER:New(nil, self._Manage, {self}, 5, self.Tcheck) --Core.Scheduler#SCHEDULER + self.manager, self.managerid = SCHEDULER:New(self, self._Manage, {self}, TstartManager, self.Tcheck) --Core.Scheduler#SCHEDULER -- Info - local text=string.format(RATMANAGER.id.."Starting RAT manager with scheduler ID %s.", self.managerid) + local text=string.format(RATMANAGER.id.."Starting RAT manager with scheduler ID %s in %d seconds. Repeat interval %d seconds.", self.managerid, TstartManager, self.Tcheck) self:E(text) return self @@ -5812,15 +5821,25 @@ function RATMANAGER:_Stop() return self end ---- Sets the time interval between checks of alive RAT groups. Default is 30 seconds. +--- Sets the time interval between checks of alive RAT groups. Default is 60 seconds. -- @param #RATMANAGER self -- @param #number dt Time interval in seconds. -- @return #RATMANAGER RATMANAGER self object. function RATMANAGER:SetTcheck(dt) - self.Tcheck=dt or 30 + self.Tcheck=dt or 60 return self end +--- Sets the time interval between spawning of groups. +-- @param #RATMANAGER self +-- @param #number dt Time interval in seconds. Default is 1 second. +-- @return #RATMANAGER RATMANAGER self object. +function RATMANAGER:SetTspawn(dt) + self.dTspawn=dt or 1.0 + return self +end + + --- Manager function. Calculating the number of current groups and respawning new groups if necessary. -- @param #RATMANAGER self function RATMANAGER:_Manage() @@ -5829,18 +5848,18 @@ function RATMANAGER:_Manage() local ntot=self:_Count() -- Debug info. - if self.Debug then - local text=string.format("Number of alive groups %d. New groups to be spawned %d.", ntot, self.ntot-ntot) - self:T(RATMANAGER.id..text) - end + local text=string.format("Number of alive groups %d. New groups to be spawned %d.", ntot, self.ntot-ntot) + self:T(RATMANAGER.id..text) -- Get number of necessary spawns. local N=self:_RollDice(self.nrat, self.ntot, self.min, self.alive) -- Loop over all RAT objects and spawn new groups if necessary. + local time=0.0 for i=1,self.nrat do for j=1,N[i] do - self.rat[i]:_SpawnWithRoute() + time=time+self.dTspawn + SCHEDULER:New(nil, RAT._SpawnWithRoute, {self.rat[i]}, time) end end end @@ -5873,10 +5892,8 @@ function RATMANAGER:_Count() ntotal=ntotal+n -- Debug output. - if self.Debug then - local text=string.format("Number of alive groups of %s = %d", self.name[i], n) - self:T(RATMANAGER.id..text) - end + local text=string.format("Number of alive groups of %s = %d", self.name[i], n) + self:T(RATMANAGER.id..text) end -- Return grand total. @@ -5969,14 +5986,12 @@ function RATMANAGER:_RollDice(nrat,ntot,min,alive) table.insert(done,j) -- Debug info - if self.Debug then - local text=RATMANAGER.id.."\n" - for i=1,nrat do - text=text..string.format("%s: i=%d, alive=%d, min=%d, mini=%d, maxi=%d, add=%d\n", self.name[i], i, alive[i], min[i], mini[i], maxi[i], N[i]) - end - text=text..string.format("Total # of groups to add = %d", sum(N, done)) - self:T2(text) + local text=RATMANAGER.id.."\n" + for i=1,nrat do + text=text..string.format("%s: i=%d, alive=%d, min=%d, mini=%d, maxi=%d, add=%d\n", self.name[i], i, alive[i], min[i], mini[i], maxi[i], N[i]) end + text=text..string.format("Total # of groups to add = %d", sum(N, done)) + self:T(text) -- Return number of groups to be spawned. return N From fdb1db6f853cd3fe6ea0e114f23417140e36f1ef Mon Sep 17 00:00:00 2001 From: funkyfranky <> Date: Wed, 4 Jul 2018 22:38:17 +0200 Subject: [PATCH 201/420] RAT v2.3.2 AIRBASE: - FindFreeParkikng: Added check that units of a group dont overlap with previous members of the same group. RAT: - Added check for all units of a group that did not move within a certain time. --- Moose Development/Moose/Functional/RAT.lua | 47 ++++++++++++++++++--- Moose Development/Moose/Wrapper/Airbase.lua | 30 ++++++++----- 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index fafa78686..b4274f0d9 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -511,7 +511,7 @@ RAT.id="RAT | " --- RAT version. -- @list version RAT.version={ - version = "2.3.1", + version = "2.3.2", print = true, } @@ -2124,10 +2124,16 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live if group:InAir() then self.ratcraft[self.SpawnIndex]["Tground"]=nil self.ratcraft[self.SpawnIndex]["Pground"]=nil + self.ratcraft[self.SpawnIndex]["Uground"]=nil self.ratcraft[self.SpawnIndex]["Tlastcheck"]=nil else self.ratcraft[self.SpawnIndex]["Tground"]=timer.getTime() self.ratcraft[self.SpawnIndex]["Pground"]=group:GetCoordinate() + self.ratcraft[self.SpawnIndex]["Uground"]={} + for _,_unit in pairs(group:GetUnits()) do + local _unitname=_unit:GetName() + self.ratcraft[self.SpawnIndex]["Uground"][_unitname]=_unit:GetCoordinate() + end self.ratcraft[self.SpawnIndex]["Tlastcheck"]=timer.getTime() end -- Initial and current position. For calculating the travelled distance. @@ -3361,6 +3367,7 @@ function RAT:Status(message, forID) -- Aircraft is airborne. ratcraft["Tground"]=nil ratcraft["Pground"]=nil + ratcraft["Uground"]=nil ratcraft["Tlastcheck"]=nil else --Aircraft is on ground. @@ -3377,16 +3384,39 @@ function RAT:Status(message, forID) -- If more than Tinactive seconds passed since last check ==> check how much we moved meanwhile. if dTlast > self.Tinactive then - -- If aircraft did not move more than 50 m since last check, we call it stationary and despawn it. - -- Aircraft which are spawned uncontrolled or starting their engines are not counted. + --[[ if Dg<50 and active and status~=RAT.status.EventBirth then - --if Dg<50 and active then stationary=true end + ]] - -- Set the current time to know when the next check is necessary. + -- Loop over all units. + for _,_unit in pairs(group:GetUnits()) do + + if _unit and _unit:IsAlive() then + + -- Unit name, coord and distance since last check. + local unitname=_unit:GetName() + local unitcoord=_unit:GetCoordinate() + local Ug=unitcoord:Get2DDistance(ratcraft.Uground[unitname]) + + -- Debug info + self:T2(RAT.id..string.format("Unit %s travelled distance on ground %.1f m since %d seconds.", unitname, Ug, dTlast)) + + -- If aircraft did not move more than 50 m since last check, we call it stationary and despawn it. + -- Aircraft which are spawned uncontrolled or starting their engines are not counted. + if Ug<50 and active and status~=RAT.status.EventBirth then + stationary=true + end + + -- Update coords. + ratcraft["Uground"][unitname]=unitcoord + end + end + + -- Set the current time to know when the next check is necessary. ratcraft["Tlastcheck"]=Tnow - ratcraft["Pground"]=coords + ratcraft["Pground"]=coords end else @@ -3394,6 +3424,11 @@ function RAT:Status(message, forID) ratcraft["Tground"]=Tnow ratcraft["Tlastcheck"]=Tnow ratcraft["Pground"]=coords + ratcraft["Uground"]={} + for _,_unit in pairs(group:GetUnits()) do + local unitname=_unit:GetName() + ratcraft.Uground[unitname]=_unit:GetCoordinate() + end end end diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 635cbfe4a..a7db3796d 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -626,9 +626,9 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, end -- Function calculating the overlap of two (square) objects. - local function _overlap(mooseobject, dcsobject, dist) - local l1=_GetObjectSize(mooseobject, true) - local l2=_GetObjectSize(dcsobject) + local function _overlap(object1, mooseobject1, object2, mooseobject2, dist) + local l1=_GetObjectSize(object1, mooseobject1) + local l2=_GetObjectSize(object2, mooseobject2) local safedist=(l1/2+l2/2)*1.1 local safe = (dist > safedist) self:T3(string.format("l1=%.1f l2=%.1f s=%.1f d=%.1f ==> safe=%s", l1,l2,safedist,dist,tostring(safe))) @@ -676,10 +676,10 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, local _termid=parkingspot.TerminalID -- Very safe uses the DCS getParking() info to check if a spot is free. Unfortunately, the function returns free=false until the aircraft has actually taken-off. - if verysafe and parkingspot.Free==false then + if verysafe and (parkingspot.Free==false or parkingspot.TOAC==true) then -- DCS getParking() routine returned that spot is not free. - self:T(string.format("%s: Parking spot id %d NOT free (or aircraft has not taken off yet).", airport, parkingspot.TerminalID)) + self:E(string.format("%s: Parking spot id %d NOT free (or aircraft has not taken off yet). Free=%s, TOAC=%s.", airport, parkingspot.TerminalID, tostring(parkingspot.Free), tostring(parkingspot.TOAC))) else @@ -695,7 +695,7 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, local _vec3=unit:getPoint() local _coord=COORDINATE:NewFromVec3(_vec3) local _dist=_coord:Get2DDistance(_spot) - local _safe=_overlap(aircraft, unit, _dist) + local _safe=_overlap(aircraft, true, unit, false,_dist) if markobstacles then local l,x,y,z=_GetObjectSize(unit) @@ -712,7 +712,7 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, local _vec3=static:getPoint() local _coord=COORDINATE:NewFromVec3(_vec3) local _dist=_coord:Get2DDistance(_spot) - local _safe=_overlap(aircraft, static, _dist) + local _safe=_overlap(aircraft, true, static, false,_dist) if markobstacles then local l,x,y,z=_GetObjectSize(static) @@ -728,8 +728,8 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, for _,scenery in pairs(_sceneries) do local _vec3=scenery:getPoint() local _coord=COORDINATE:NewFromVec3(_vec3) - local _dist=_coord:Get2DDistance(_spot) - local _safe=_overlap(aircraft, scenery, _dist) + local _dist=_coord:Get2DDistance(_spot) + local _safe=_overlap(aircraft, true, scenery, false,_dist) if markobstacles then local l,x,y,z=_GetObjectSize(scenery) @@ -741,6 +741,15 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, end end + -- Now check the already given spots so that we do not put a large aircraft next to one we already assigned a nearby spot. + for _,_takenspot in pairs(validspots) do + local _dist=_takenspot.Coordinate:Get2DDistance(_spot) + local _safe=_overlap(aircraft, true, aircraft, true,_dist) + if not _safe then + occupied=true + end + end + --_spot:MarkToAll(string.format("Parking spot %d free=%s", parkingspot.TerminalID, tostring(not occupied))) if occupied then self:T(string.format("%s: Parking spot id %d occupied.", airport, _termid)) @@ -752,12 +761,13 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, nvalid=nvalid+1 end - end + end -- loop over units -- We found enough spots. if nvalid>=_nspots then return validspots end + end -- Retrun spots we found, even if there were not enough. From 727aea604fc770f585e19ce3fb702b5722e4fbf3 Mon Sep 17 00:00:00 2001 From: Van De Velde Date: Thu, 5 Jul 2018 08:48:05 +0200 Subject: [PATCH 202/420] Correctly interprete the _ in the documentation! Generate new documentation with markdown _ change avoiding wrong italics in the documentation. This is an important change improving the documentation quality! --- Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 6bce0816e..0feea4e8e 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -68,6 +68,7 @@ -- -- If no home zone is specified, the carriers will wait near the deploy zone for a new pickup command. -- +-- -- === -- -- @field #AI_CARGO_DISPATCHER From 9e13ac3f68e72599bf9ca574988bbd98054f83e4 Mon Sep 17 00:00:00 2001 From: Van De Velde Date: Thu, 5 Jul 2018 19:25:07 +0200 Subject: [PATCH 203/420] First version of documentation of cargo. --- .../Moose/AI/AI_Cargo_Dispatcher.lua | 1 - Moose Development/Moose/Cargo/Cargo.lua | 185 ++++++++++++++++++ 2 files changed, 185 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 0feea4e8e..6bce0816e 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -68,7 +68,6 @@ -- -- If no home zone is specified, the carriers will wait near the deploy zone for a new pickup command. -- --- -- === -- -- @field #AI_CARGO_DISPATCHER diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 8add3c374..42d309c6a 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -2,6 +2,191 @@ -- -- === -- +-- # 1) MOOSE Cargo System. +-- +-- #### Those who have used the mission editor, know that the DCS mission editor provides cargo facilities. +-- However, these are merely static objects. Wouldn't it be nice if cargo could bring a new dynamism into your +-- simulations? Where various objects of various types could be treated also as cargo? +-- +-- This is what MOOSE brings to you, a complete new cargo object model that used the cargo capabilities of +-- DCS world, but enhances it. +-- +-- MOOSE Cargo introduces also a new concept, called a "carrier". These can be: +-- +-- - Helicopters +-- - Planes +-- - Ground Vehicles +-- - Ships +-- +-- With the MOOSE Cargo system, you can: +-- +-- - Take full control of the cargo as objects within your script (see below). +-- - Board/Unboard infantry into carriers. Also other objects can be boarded, like mortars. +-- - Load/Unload dcs world cargo objects into carriers. +-- - Load/Unload other static objects into carriers (like tires etc). +-- - Slingload cargo objects. +-- - Board units one by one... +-- +-- # 2) MOOSE Cargo Objects. +-- +-- In order to make use of the MOOSE cargo system, you need to **declare** the DCS objects as MOOSE cargo objects! +-- +-- This sounds complicated, but it is actually quite simple. +-- +-- See here an example: +-- +-- local EngineerCargoGroup = CARGO_GROUP:New( GROUP:FindByName( "Engineers" ), "Workmaterials", "Engineers", 250 ) +-- +-- The above code declares a MOOSE cargo object called `EngineerCargoGroup`. +-- It actually just refers to an infantry group created within the sim called `"Engineers"`. +-- The infantry group now becomes controlled by the MOOSE cargo object `EngineerCargoGroup`. +-- A MOOSE cargo object also has properties, like the type of cargo, the logical name, and the reporting range. +-- +-- There are 4 types of MOOSE cargo objects possible, each represented by its own class: +-- +-- - @{Cargo.CargoGroup#CARGO_GROUP}: A MOOSE cargo that is represented by a DCS world GROUP object. +-- - @{Cargo.CargoCrate#CARGO_CRATE}: A MOOSE cargo that is represented by a DCS world cargo object (static object). +-- - @{Cargo.CargoUnit#CARGO_UNIT}: A MOOSE cargo that is represented by a DCS world unit object or static object. +-- - @{Cargo.CargoSlingload#CARGO_SLINGLOAD}: A MOOSE cargo that is represented by a DCS world cargo object (static object), that can be slingloaded. +-- +-- Note that a CARGO crate is not meant to be slingloaded (it can, but it is not **meant** to be handled like that. +-- Instead, a CARGO_CRATE is able to load itself into the bays of a carrier. +-- +-- Each of these MOOSE cargo objects behave in its own way, and have methods to be handled. +-- +-- local InfantryGroup = GROUP:FindByName( "Infantry" ) +-- local InfantryCargo = CARGO_GROUP:New( InfantryGroup, "Engineers", "Infantry Engineers", 2000 ) +-- local CargoCarrier = UNIT:FindByName( "Carrier" ) +-- -- This call will make the Cargo run to the CargoCarrier. +-- -- Upon arrival at the CargoCarrier, the Cargo will be Loaded into the Carrier. +-- -- This process is now fully automated. +-- InfantryCargo:Board( CargoCarrier, 25 ) +-- +-- The above would create a MOOSE cargo object called `InfantryCargo`, and using that object, +-- you can board the cargo into the carrier `CargoCarrier`. +-- Simple, isn't it? Told you, and this is only the beginning. +-- +-- The boarding, unboarding, loading, unloading of cargo is however something that is not meant to be coded manualy by mission designers. +-- It would be too low-level and not end-user friendly to deal with cargo handling complexity. +-- Things can become really complex if you want to make cargo being handled and behave in multiple scenarios. +-- +-- # 3) Cargo Handling Classes, the main engines for mission designers! +-- +-- For this reason, the MOOSE Cargo System is heavily used by 3 important **cargo handling class hierarchies** within MOOSE, +-- that make cargo come "alive" within your mission in a full automatic manner! +-- +-- ## 3.1) AI Cargo handlers. +-- +-- - @{AI.AI_Cargo_APC} will create for you the capatility to make an APC group handle cargo. +-- - @{AI.AI_Cargo_Helicopter} will create for you the capatility to make a Helicopter group handle cargo. +-- +-- +-- ## 3.2) AI Cargo transportation dispatchers. +-- +-- There are also dispatchers that make AI work together to transport cargo automatically!!! +-- +-- - @{AI.AI_Cargo_Dispatcher_APC} derived classes will create for your dynamic cargo handlers controlled by AI ground vehicle groups (APCs) to transport cargo between sites. +-- - @{AI.AI_Cargo_Dispatcher_Helicopters} derived classes will create for your dynamic cargo handlers controlled by AI helicpter groups to transport cargo between sites. +-- +-- ## 3.3) Cargo transportation tasking. +-- +-- And there is cargo transportation tasking for human players. +-- +-- - @{Tasking.Task_CARGO} derived classes will create for you cargo transportation tasks, that allow human players to interact with MOOSE cargo objects to complete tasks. +-- +-- Please refer to the documentation reflected within these modules to understand the detailed capabilties. +-- +-- # 4) Cargo SETs. +-- +-- To make life a bit more easy, MOOSE cargo objects can be grouped into a @{Core.Set#SET_CARGO}. +-- This is a collection of MOOSE cargo objects. +-- +-- This would work as follows: +-- +-- -- Define the cargo set. +-- local CargoSetWorkmaterials = SET_CARGO:New():FilterTypes( "Workmaterials" ):FilterStart() +-- +-- -- Now add cargo the cargo set. +-- local EngineerCargoGroup = CARGO_GROUP:New( GROUP:FindByName( "Engineers" ), "Workmaterials", "Engineers", 250 ) +-- local ConcreteCargo = CARGO_SLINGLOAD:New( STATIC:FindByName( "Concrete" ), "Workmaterials", "Concrete", 150, 50 ) +-- local CrateCargo = CARGO_CRATE:New( STATIC:FindByName( "Crate" ), "Workmaterials", "Crate", 150, 50 ) +-- local EnginesCargo = CARGO_CRATE:New( STATIC:FindByName( "Engines" ), "Workmaterials", "Engines", 150, 50 ) +-- local MetalCargo = CARGO_CRATE:New( STATIC:FindByName( "Metal" ), "Workmaterials", "Metal", 150, 50 ) +-- +-- This is a very powerful concept! +-- Instead of having to deal with multiple MOOSE cargo objects yourself, the cargo set capability will group cargo objects into one set. +-- The key is the **cargo type** name given at each cargo declaration! +-- In the above example, the cargo type name is `"Workmaterials"`. Each cargo object declared is given that type name. (the 2nd parameter). +-- What happens now is that the cargo set `CargoSetWorkmaterials` will be added with each cargo object **dynamically** when the cargo object is created. +-- In other words, the cargo set `CargoSetWorkmaterials` will incorporate any `"Workmaterials"` dynamically into its set. +-- +-- The cargo sets are extremely important for the AI cargo transportation dispatchers and the cargo transporation tasking. +-- +-- # 5) Declare MOOSE cargo within the mission editor!!! +-- +-- But I am not finished! There is something more, that is even more great! +-- Imagine the mission designers having to code all these lines every time it wants to embed cargo within a mission. +-- +-- -- Now add cargo the cargo set. +-- local EngineerCargoGroup = CARGO_GROUP:New( GROUP:FindByName( "Engineers" ), "Workmaterials", "Engineers", 250 ) +-- local ConcreteCargo = CARGO_SLINGLOAD:New( STATIC:FindByName( "Concrete" ), "Workmaterials", "Concrete", 150, 50 ) +-- local CrateCargo = CARGO_CRATE:New( STATIC:FindByName( "Crate" ), "Workmaterials", "Crate", 150, 50 ) +-- local EnginesCargo = CARGO_CRATE:New( STATIC:FindByName( "Engines" ), "Workmaterials", "Engines", 150, 50 ) +-- local MetalCargo = CARGO_CRATE:New( STATIC:FindByName( "Metal" ), "Workmaterials", "Metal", 150, 50 ) +-- +-- This would be extremely tiring and a huge overload. +-- However, the MOOSE framework allows to declare MOOSE cargo objects within the mission editor!!! +-- +-- So, at mission startup, MOOSE will search for objects following a special naming convention, and will create for you dynamically +-- cargo objects at mission start!!! +-- These cargo objects can then be automatically incorporated within cargo set(s)!!! +-- In other words, your mission would be reduced to about a few lines of code, providing you with a full dynamic cargo handling mission! +-- +-- What I talk about is this: +-- +-- HQ = GROUP:FindByName( "HQ", "Bravo" ) +-- +-- CommandCenter = COMMANDCENTER +-- :New( HQ, "Lima" ) +-- +-- Mission = MISSION +-- :New( CommandCenter, "Operation Cargo Fun", "Tactical", "Transport Cargo", coalition.side.RED ) +-- +-- TransportGroups = SET_GROUP:New():FilterCoalitions( "blue" ):FilterPrefixes( "Transport" ):FilterStart() +-- +-- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, TransportGroups ) +-- +-- +-- -- This is the most important now. You setup a new SET_CARGO filtering the relevant type. +-- -- The actual cargo objects are now created by MOOSE in the background. +-- -- Each cargo is setup in the Mission Editor using the ~CARGO tag in the group name. +-- -- This allows a truly dynamic setup. +-- local CargoSetWorkmaterials = SET_CARGO:New():FilterTypes( "Workmaterials" ):FilterStart() +-- +-- local WorkplaceTask = TaskDispatcher:AddTransportTask( "Build a Workplace", CargoSetWorkmaterials, "Transport the workers, engineers and the equipment near the Workplace." ) +-- TaskDispatcher:SetTransportDeployZone( WorkplaceTask, ZONE:New( "Workplace" ) ) +-- +-- Helos = { SPAWN:New( "Helicopters 1" ), SPAWN:New( "Helicopters 2" ), SPAWN:New( "Helicopters 3" ), SPAWN:New( "Helicopters 4" ), SPAWN:New( "Helicopters 5" ) } +-- +-- EnemyHelos = { SPAWN:New( "Enemy Helicopters 1" ), SPAWN:New( "Enemy Helicopters 2" ), SPAWN:New( "Enemy Helicopters 3" ) } +-- +-- function WorkplaceTask:OnAfterCargoDeployed( From, Event, To, TaskUnit, Cargo, DeployZone ) +-- Helos[ math.random(1,#Helos) ]:Spawn() +-- EnemyHelos[ math.random(1,#EnemyHelos) ]:Spawn() +-- +-- end +-- +-- Here the `CargoSetWorkmaterials` is provided as a parameter to the cargo task dispatcher object WorkplaceTask`. +-- And there is NO cargo object actually declared within the script! However, if you would open the mission, there would be hundreds of cargo objects... +-- +-- HOW? => Through a naming convention introduced. Name infantry groups in a special manner, and it can behave as MOOSE cargo! +-- +-- 5.1) Name MOOSE cargo objects within the mission editor! +-- +-- +-- +-- === +-- -- ### Author: **FlightControl** -- ### Contributions: -- From fda061d8c89696b847c920574c5a3853e514580a Mon Sep 17 00:00:00 2001 From: funkyfranky <> Date: Tue, 10 Jul 2018 23:47:12 +0200 Subject: [PATCH 204/420] Improved GroundOnRoad functions --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 6 +- Moose Development/Moose/Core/Point.lua | 52 +++++++++++---- .../Moose/Wrapper/Controllable.lua | 63 ++++++++++++++----- 3 files changed, 89 insertions(+), 32 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index a2d086683..7f4f34a3e 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -604,7 +604,7 @@ function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate ) if Coordinate then self.RoutePickup = true - local Waypoints = APC:TaskGroundOnRoad( Coordinate, APC:GetSpeedMax()*0.5, "Line abreast" ) + local Waypoints = APC:TaskGroundOnRoad( Coordinate, APC:GetSpeedMax()*0.5, "Line abreast", true ) local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Pickup", self ) @@ -635,7 +635,7 @@ function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate ) self.RouteDeploy = true - local Waypoints = APC:TaskGroundOnRoad( Coordinate, APC:GetSpeedMax()*0.5, "Line abreast" ) + local Waypoints = APC:TaskGroundOnRoad( Coordinate, APC:GetSpeedMax()*0.5, "Line abreast", true ) local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Deploy", self ) @@ -661,7 +661,7 @@ function AI_CARGO_APC:onafterHome( APC, From, Event, To, Coordinate ) self.RouteHome = true - local Waypoints = APC:TaskGroundOnRoad( Coordinate, APC:GetSpeedMax()*0.5, "Line abreast" ) + local Waypoints = APC:TaskGroundOnRoad( Coordinate, APC:GetSpeedMax()*0.5, "Line abreast", true ) self:F({Waypoints = Waypoints}) local Waypoint = Waypoints[#Waypoints] diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 97bc09215..3b88ab74b 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1184,29 +1184,55 @@ do -- COORDINATE end --- Returns a table of coordinates to a destination using only roads. - -- The first point is the closest point on road of the given coordinate. The last point is the closest point on road of the ToCoord. Hence, the coordinate itself and the final ToCoord are not necessarily included in the path. + -- The first point is the closest point on road of the given coordinate. + -- By default, the last point is the closest point on road of the ToCoord. Hence, the coordinate itself and the final ToCoord are not necessarily included in the path. -- @param #COORDINATE self -- @param #COORDINATE ToCoord Coordinate of destination. - -- @return #table Table of coordinates on road. If no path on road can be found, nil is returned. - function COORDINATE:GetPathOnRoad(ToCoord) + -- @param #boolean IncludeEndpoints (Optional) Include the coordinate itself and the ToCoordinate in the path. + -- @return #table Table of coordinates on road. If no path on road can be found, nil is returned or just the endpoints. + -- @return #number The length of the total path. + function COORDINATE:GetPathOnRoad(ToCoord, IncludeEndpoints) -- DCS API function returning a table of vec2. local path = land.findPathOnRoads("roads", self.x, self.z, ToCoord.x, ToCoord.z) + -- Array holding the path coordinates. local Path={} + local Way=0 - if path then - --Path[#Path+1]=self - for i, v in ipairs(path) do - Path[#Path+1]=COORDINATE:NewFromVec2(v) - end - --Path[#Path+1]=ToCoord - else - -- There are cases where no path on road can be found. - return nil + -- Include currrent position. + if IncludeEndpoints then + Path[1]=self end - return Path + -- Check that DCS routine actually returned a path. There are situations where this is not the case. + if path then + + -- Include all points on road. + for _,_vec2 in ipairs(path) do + Path[#Path+1]=COORDINATE:NewFromVec2(_vec2) + end + + else + self:E("Path is nil. No valid path on road could be found.") + end + + -- Include end point, which might not be on road. + if IncludeEndpoints then + Path[#Path+1]=ToCoord + end + + -- Sum up distances. + if #Path>=2 then + for i=1,#Path-1 do + Way=Way+Path[i+1]:Get2DDistance(Path[i]) + end + else + -- There are cases where no path on road can be found. + return nil,nil + end + + return Path, Way end --- Gets the surface type at the coordinate. diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 3199393b4..eef142020 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1990,8 +1990,9 @@ do -- Route methods -- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to. -- @param #number Speed (Optional) Speed in km/h. The default speed is 20 km/h. -- @param #string OffRoadFormation (Optional) The formation at initial and final waypoint. Default is "Off Road". + -- @param #boolean Shortcut (Optional) If true, controllable will take the direct route if the path on road is 10x longer. -- @return Task - function CONTROLLABLE:TaskGroundOnRoad( ToCoordinate, Speed, OffRoadFormation ) + function CONTROLLABLE:TaskGroundOnRoad( ToCoordinate, Speed, OffRoadFormation, Shortcut ) self:F2({ToCoordinate=ToCoordinate, Speed=Speed, OffRoadFormation=OffRoadFormation}) -- Defaults. @@ -2001,26 +2002,56 @@ do -- Route methods -- Current coordinate. local FromCoordinate = self:GetCoordinate() - -- First point on road. - local FromOnRoad = FromCoordinate:GetClosestPointToRoad() + -- Get path and path length on road including the end points (From and To). + local PathOnRoad, LengthOnRoad=FromCoordinate:GetPathOnRoad(ToCoordinate, true) + + -- Calculate the direct distance between the initial and final points. + local LengthDirect=FromCoordinate:Get2DDistance(ToCoordinate) + + env.info(string.format("FF length on road = %.1f", LengthOnRoad/1000)) + env.info(string.format("FF length directly = %.1f", LengthDirect/1000)) + env.info(string.format("FF length fraction = %.1f", LengthOnRoad/LengthDirect)) - -- Last Point on road. - local ToOnRoad = ToCoordinate:GetClosestPointToRoad() - -- Route, ground waypoints along road. local route={} - -- Create waypoints. - table.insert(route, FromCoordinate:WaypointGround(Speed, OffRoadFormation)) - table.insert(route, FromOnRoad:WaypointGround(Speed, "On Road")) - table.insert(route, ToOnRoad:WaypointGround(Speed, "On Road")) - - -- Add the final coordinate because the final might not be on the road. - local dist=ToCoordinate:Get2DDistance(ToOnRoad) - if dist>10 then - table.insert(route, ToCoordinate:WaypointGround(Speed, OffRoadFormation)) - end + -- Length on road is 10 times longer than direct route. + local LongRoad=LengthOnRoad and (LengthOnRoad > LengthDirect*10) + -- Check if a valid path on road could be found. + if PathOnRoad then + + -- Check whether the road is very long compared to direct path. + if LongRoad and Shortcut then + env.info(string.format("FF longroad and shortcut")) + -- Road is long ==> we take the short cut. + table.insert(route, FromCoordinate:WaypointGround(Speed, OffRoadFormation)) + table.insert(route, ToCoordinate:WaypointGround(Speed, OffRoadFormation)) + + else + env.info(string.format("FF longroad and shortcut else")) + -- Create waypoints. + table.insert(route, FromCoordinate:WaypointGround(Speed, OffRoadFormation)) + table.insert(route, PathOnRoad[2]:WaypointGround(Speed, "On Road")) + table.insert(route, PathOnRoad[#PathOnRoad-1]:WaypointGround(Speed, "On Road")) + + -- Add the final coordinate because the final might not be on the road. + local dist=ToCoordinate:Get2DDistance(PathOnRoad[#PathOnRoad-1]) + if dist>10 then + env.info(string.format("FF longroad and shortcut else dist>10")) + table.insert(route, ToCoordinate:WaypointGround(Speed, OffRoadFormation)) + end + + end + + else + + -- No path on road could be found (can happen!) ==> Route group directly from A to B. + table.insert(route, FromCoordinate:WaypointGround(Speed, OffRoadFormation)) + table.insert(route, ToCoordinate:WaypointGround(Speed, OffRoadFormation)) + + end + return route end From 258e3d29213f850078742fe2cf776fafc2662da0 Mon Sep 17 00:00:00 2001 From: funkyfranky <> Date: Wed, 11 Jul 2018 23:27:44 +0200 Subject: [PATCH 205/420] CARGO_GROUP - Fixed issue that units are spawned on top of each other on unboarding. --- Moose Development/Moose/Cargo/CargoGroup.lua | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index de8b08e6d..69787e721 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -364,9 +364,11 @@ do -- CARGO_GROUP -- @param #string From -- @param #string To function CARGO_GROUP:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) - --self:F( {From, Event, To, ToPointVec2, NearRadius } ) + self:F( {From, Event, To, ToPointVec2, NearRadius } ) NearRadius = NearRadius or 25 + + env.info("FF onenterunboarding") local Timer = 1 @@ -381,7 +383,14 @@ do -- CARGO_GROUP --- @param Cargo.Cargo#CARGO Cargo function( Cargo, NearRadius ) if not Cargo:IsDestroyed() then - Cargo:__UnBoard( Timer, ToPointVec2, NearRadius ) + env.info("FF blubblub") + local ToVec=nil + if ToPointVec2==nil then + ToVec=self.CargoCarrier:GetPointVec2():GetRandomPointVec2InRadius(2*NearRadius, NearRadius) + else + ToVec=ToPointVec2 + end + Cargo:__UnBoard( Timer, ToVec, NearRadius ) Timer = Timer + 3 end end, { NearRadius } From 72343bce2913885f6dfa5b8e1fced782b2e445a5 Mon Sep 17 00:00:00 2001 From: funkyfranky <> Date: Thu, 12 Jul 2018 23:23:00 +0200 Subject: [PATCH 206/420] Improved TaskOnRoad logic - direct route if path length on route is less than 5% --- Moose Development/Moose/Core/Point.lua | 1 + .../Moose/Wrapper/Controllable.lua | 23 +++++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 3b88ab74b..e3c478b14 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1211,6 +1211,7 @@ do -- COORDINATE -- Include all points on road. for _,_vec2 in ipairs(path) do Path[#Path+1]=COORDINATE:NewFromVec2(_vec2) + --COORDINATE:NewFromVec2(_vec2):SmokeGreen() end else diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index eef142020..8db384482 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1990,7 +1990,7 @@ do -- Route methods -- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to. -- @param #number Speed (Optional) Speed in km/h. The default speed is 20 km/h. -- @param #string OffRoadFormation (Optional) The formation at initial and final waypoint. Default is "Off Road". - -- @param #boolean Shortcut (Optional) If true, controllable will take the direct route if the path on road is 10x longer. + -- @param #boolean Shortcut (Optional) If true, controllable will take the direct route if the path on road is 10x longer or path on road is less than 5% of total path. -- @return Task function CONTROLLABLE:TaskGroundOnRoad( ToCoordinate, Speed, OffRoadFormation, Shortcut ) self:F2({ToCoordinate=ToCoordinate, Speed=Speed, OffRoadFormation=OffRoadFormation}) @@ -2005,18 +2005,27 @@ do -- Route methods -- Get path and path length on road including the end points (From and To). local PathOnRoad, LengthOnRoad=FromCoordinate:GetPathOnRoad(ToCoordinate, true) + -- Get the length only(!) on the road. + local _,LengthRoad=FromCoordinate:GetPathOnRoad(ToCoordinate, false) + + -- Off road part of the rout: Total=OffRoad+OnRoad. + local LengthOffRoad=LengthOnRoad-LengthRoad + -- Calculate the direct distance between the initial and final points. local LengthDirect=FromCoordinate:Get2DDistance(ToCoordinate) - env.info(string.format("FF length on road = %.1f", LengthOnRoad/1000)) - env.info(string.format("FF length directly = %.1f", LengthDirect/1000)) - env.info(string.format("FF length fraction = %.1f", LengthOnRoad/LengthDirect)) + env.info(string.format("FF length on road = %.1f km", LengthOnRoad/1000)) + env.info(string.format("FF length directly = %.1f km", LengthDirect/1000)) + env.info(string.format("FF length fraction = %.1f km", LengthOnRoad/LengthDirect)) + env.info(string.format("FF length only road = %.1f km", LengthRoad/1000)) + env.info(string.format("FF length off road = %.1f km", LengthOffRoad/1000)) + env.info(string.format("FF percent on road = %.1f", LengthRoad/LengthOnRoad*100)) -- Route, ground waypoints along road. local route={} - - -- Length on road is 10 times longer than direct route. - local LongRoad=LengthOnRoad and (LengthOnRoad > LengthDirect*10) + + -- Length on road is 10 times longer than direct route or path on road is very short (<5% of total path). + local LongRoad=LengthOnRoad and ((LengthOnRoad > LengthDirect*10) or (LengthRoad/LengthOnRoad*100<5)) -- Check if a valid path on road could be found. if PathOnRoad then From c60bb29303b8e361d9a0849e027736c329c9ec0e Mon Sep 17 00:00:00 2001 From: funkyfranky <> Date: Tue, 17 Jul 2018 22:19:25 +0200 Subject: [PATCH 207/420] Minor fixes Task_A2G: fixt GetAlt() AI_Formation: fixed comma in equation --- Moose Development/Moose/AI/AI_Formation.lua | 2 +- Moose Development/Moose/Tasking/Task_A2G.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Formation.lua b/Moose Development/Moose/AI/AI_Formation.lua index af5805bb4..8be01bd23 100644 --- a/Moose Development/Moose/AI/AI_Formation.lua +++ b/Moose Development/Moose/AI/AI_Formation.lua @@ -968,7 +968,7 @@ function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1 local Alpha_R = ( Alpha_T < 0 ) and Alpha_T + 2 * math.pi or Alpha_T local Position = math.cos( Alpha_R ) local GD = ( ( GDv.x )^2 + ( GDv.z )^2 ) ^ 0.5 - local Distance = GD * Position + - CS * 0,5 + local Distance = GD * Position + - CS * 0.5 -- Calculate the group direction vector local GV = { x = GV2.x - CV2.x, y = GV2.y - CV2.y, z = GV2.z - CV2.z } diff --git a/Moose Development/Moose/Tasking/Task_A2G.lua b/Moose Development/Moose/Tasking/Task_A2G.lua index 637f1fdc4..634234654 100644 --- a/Moose Development/Moose/Tasking/Task_A2G.lua +++ b/Moose Development/Moose/Tasking/Task_A2G.lua @@ -148,7 +148,7 @@ do -- TASK_A2G local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT if TargetUnit then local Coordinate = TargetUnit:GetPointVec3() - self:T( { TargetCoordinate = Coordinate, Coordinate:GetX(), Coordinate:GetAlt(), Coordinate:GetZ() } ) + self:T( { TargetCoordinate = Coordinate, Coordinate:GetX(), Coordinate:GetY(), Coordinate:GetZ() } ) Task:SetTargetCoordinate( Coordinate, TaskUnit ) end self:__RouteToTargetPoint( 0.1 ) From 6aa30f91e6ff5b7b8f29471c23bbfdadd1b01931 Mon Sep 17 00:00:00 2001 From: funkyfranky <> Date: Wed, 18 Jul 2018 17:41:21 +0200 Subject: [PATCH 208/420] Debug output --- Moose Development/Moose/Cargo/CargoGroup.lua | 3 --- .../Moose/Wrapper/Controllable.lua | 18 +++++++++--------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 69787e721..3fa26e9ab 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -367,8 +367,6 @@ do -- CARGO_GROUP self:F( {From, Event, To, ToPointVec2, NearRadius } ) NearRadius = NearRadius or 25 - - env.info("FF onenterunboarding") local Timer = 1 @@ -383,7 +381,6 @@ do -- CARGO_GROUP --- @param Cargo.Cargo#CARGO Cargo function( Cargo, NearRadius ) if not Cargo:IsDestroyed() then - env.info("FF blubblub") local ToVec=nil if ToPointVec2==nil then ToVec=self.CargoCarrier:GetPointVec2():GetRandomPointVec2InRadius(2*NearRadius, NearRadius) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 8db384482..df8767019 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -2014,12 +2014,13 @@ do -- Route methods -- Calculate the direct distance between the initial and final points. local LengthDirect=FromCoordinate:Get2DDistance(ToCoordinate) - env.info(string.format("FF length on road = %.1f km", LengthOnRoad/1000)) - env.info(string.format("FF length directly = %.1f km", LengthDirect/1000)) - env.info(string.format("FF length fraction = %.1f km", LengthOnRoad/LengthDirect)) - env.info(string.format("FF length only road = %.1f km", LengthRoad/1000)) - env.info(string.format("FF length off road = %.1f km", LengthOffRoad/1000)) - env.info(string.format("FF percent on road = %.1f", LengthRoad/LengthOnRoad*100)) + -- Debug info. + self:T(string.format("Length on road = %.3f km", LengthOnRoad/1000)) + self:T(string.format("Length directly = %.3f km", LengthDirect/1000)) + self:T(string.format("Length fraction = %.3f km", LengthOnRoad/LengthDirect)) + self:T(string.format("Length only road = %.3f km", LengthRoad/1000)) + self:T(string.format("Length off road = %.3f km", LengthOffRoad/1000)) + self:T(string.format("Percent on road = %.1f", LengthRoad/LengthOnRoad*100)) -- Route, ground waypoints along road. local route={} @@ -2032,13 +2033,13 @@ do -- Route methods -- Check whether the road is very long compared to direct path. if LongRoad and Shortcut then - env.info(string.format("FF longroad and shortcut")) + -- Road is long ==> we take the short cut. table.insert(route, FromCoordinate:WaypointGround(Speed, OffRoadFormation)) table.insert(route, ToCoordinate:WaypointGround(Speed, OffRoadFormation)) else - env.info(string.format("FF longroad and shortcut else")) + -- Create waypoints. table.insert(route, FromCoordinate:WaypointGround(Speed, OffRoadFormation)) table.insert(route, PathOnRoad[2]:WaypointGround(Speed, "On Road")) @@ -2047,7 +2048,6 @@ do -- Route methods -- Add the final coordinate because the final might not be on the road. local dist=ToCoordinate:Get2DDistance(PathOnRoad[#PathOnRoad-1]) if dist>10 then - env.info(string.format("FF longroad and shortcut else dist>10")) table.insert(route, ToCoordinate:WaypointGround(Speed, OffRoadFormation)) end From 267401a1f3665d43eb2e047ba9334e667196ac8f Mon Sep 17 00:00:00 2001 From: funkyfranky <> Date: Thu, 19 Jul 2018 23:52:07 +0200 Subject: [PATCH 209/420] Added Shiraz and Kerman Airports to enumerators --- Moose Development/Moose/Wrapper/Airbase.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index a7db3796d..9c32fb42c 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -236,6 +236,8 @@ AIRBASE.Normandy = { -- * AIRBASE.PersianGulf.Khasab -- * AIRBASE.PersianGulf.Al_Minhad_AB -- * AIRBASE.PersianGulf.Sharjah_Intl +-- * AIRBASE.PersianGulf.Shiraz_International_Airport +-- * AIRBASE.PersianGulf.Kerman_Airport -- @field PersianGulf AIRBASE.PersianGulf = { ["Fujairah_Intl"] = "Fujairah Intl", @@ -255,6 +257,8 @@ AIRBASE.PersianGulf = { ["Khasab"] = "Khasab", ["Al_Minhad_AB"] = "Al Minhad AB", ["Sharjah_Intl"] = "Sharjah Intl", + ["Shiraz_International_Airport"] = "Shiraz International Airport", + ["Kerman_Airport"] = "Kerman Airport", } --- Terminal Types of parking spots. See also https://wiki.hoggitworld.com/view/DCS_func_getParking From c7aa79937809d1ad0eb3c3f7d57ad7457de8a078 Mon Sep 17 00:00:00 2001 From: funkyfranky <> Date: Sat, 21 Jul 2018 00:26:46 +0200 Subject: [PATCH 210/420] Fixes ARTY: * Rearming group will not always use 20 km/h = 11 mph. SET_GROUP(+DESIGNATE): * Fixed bug in SET_GROUP:FindNearestGroupFromPointVec2( PointVec2 ) function. This caused DESIGNATE class to crash! --- Moose Development/Moose/Core/Set.lua | 9 +++++---- Moose Development/Moose/Functional/Artillery.lua | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 78607d83d..0b9052242 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -859,15 +859,16 @@ end function SET_GROUP:FindNearestGroupFromPointVec2( PointVec2 ) self:F2( PointVec2 ) - local NearestGroup = nil + local NearestGroup = nil --Wrapper.Group#GROUP local ClosestDistance = nil for ObjectID, ObjectData in pairs( self.Set ) do if NearestGroup == nil then - NearestGroup = ObjectData - ClosestDistance = PointVec2:DistanceFromVec2( ObjectData:GetVec2() ) + NearestGroup = ObjectData + NearestGroup:GetVec2() + ClosestDistance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() ) else - local Distance = PointVec2:DistanceFromVec2( ObjectData:GetVec2() ) + local Distance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() ) if Distance < ClosestDistance then NearestGroup = ObjectData ClosestDistance = Distance diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 126ef78b9..880b8d645 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -1632,7 +1632,7 @@ function ARTY:onafterStart(Controllable, From, Event, To) self.RearmingPlaceCoord=nil self.relocateafterfire=false self.autorelocate=false - self.RearmingGroupSpeed=20 + --self.RearmingGroupSpeed=20 end -- Check that default speed is below max speed. From 53c0599075a4a86fa103ad0d51e2281fb86e6b46 Mon Sep 17 00:00:00 2001 From: funkyfranky <> Date: Sat, 21 Jul 2018 00:30:02 +0200 Subject: [PATCH 211/420] Removed GetVec2 --- Moose Development/Moose/Core/Set.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 0b9052242..5453f9844 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -865,7 +865,6 @@ function SET_GROUP:FindNearestGroupFromPointVec2( PointVec2 ) for ObjectID, ObjectData in pairs( self.Set ) do if NearestGroup == nil then NearestGroup = ObjectData - NearestGroup:GetVec2() ClosestDistance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() ) else local Distance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() ) From 6991550d1bf4d181d77a6f6ccf30d84e88f7c4af Mon Sep 17 00:00:00 2001 From: funkyfranky <> Date: Sat, 21 Jul 2018 16:32:25 +0200 Subject: [PATCH 212/420] AI_CARGO_DISPATCHER APCs and helos will now obey speeds set by SetPickupSpeed() and SetDeploySpeed(). --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 25 ++++++++---- .../Moose/AI/AI_Cargo_Airplane.lua | 1 - .../AI/AI_Cargo_Dispatcher_Helicopter.lua | 2 +- .../Moose/AI/AI_Cargo_Helicopter.lua | 39 +++++++++++-------- 4 files changed, 40 insertions(+), 27 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 7f4f34a3e..2c76f2786 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -596,15 +596,18 @@ end -- @param From -- @param Event -- @param To --- @param Core.Point#COORDINATE Coordinate -function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate ) +-- @param Core.Point#COORDINATE Coordinate of the pickup point. +-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. +function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed ) if APC and APC:IsAlive() then if Coordinate then self.RoutePickup = true - local Waypoints = APC:TaskGroundOnRoad( Coordinate, APC:GetSpeedMax()*0.5, "Line abreast", true ) + local _speed=Speed or APC:GetSpeedMax()*0.5 + + local Waypoints = APC:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true ) local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Pickup", self ) @@ -629,13 +632,16 @@ end -- @param Event -- @param To -- @param Core.Point#COORDINATE Coordinate -function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate ) +-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. +function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed ) if APC and APC:IsAlive() then self.RouteDeploy = true + + local _speed=Speed or APC:GetSpeedMax()*0.5 - local Waypoints = APC:TaskGroundOnRoad( Coordinate, APC:GetSpeedMax()*0.5, "Line abreast", true ) + local Waypoints = APC:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true ) local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Deploy", self ) @@ -655,13 +661,16 @@ end -- @param Event -- @param To -- @param Core.Point#COORDINATE Coordinate -function AI_CARGO_APC:onafterHome( APC, From, Event, To, Coordinate ) +-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. +function AI_CARGO_APC:onafterHome( APC, From, Event, To, Coordinate, Speed ) if APC and APC:IsAlive() ~= nil then self.RouteHome = true - - local Waypoints = APC:TaskGroundOnRoad( Coordinate, APC:GetSpeedMax()*0.5, "Line abreast", true ) + + local _speed=Speed or APC:GetSpeedMax()*0.5 + + local Waypoints = APC:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true ) self:F({Waypoints = Waypoints}) local Waypoint = Waypoints[#Waypoints] diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 343823d2f..bbbe19ed0 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -25,7 +25,6 @@ AI_CARGO_AIRPLANE = { -- @param #AI_CARGO_AIRPLANE self -- @param Wrapper.Group#GROUP Airplane -- @param Core.Set#SET_CARGO CargoSet --- @param #number CombatRadius -- @return #AI_CARGO_AIRPLANE function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index eabc91dc4..eaf9666d2 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -90,7 +90,7 @@ AI_CARGO_DISPATCHER_HELICOPTER = { -- @param #AI_CARGO_DISPATCHER_HELICOPTER self -- @param Core.Set#SET_GROUP SetHelicopter The collection of Helicopter @{Wrapper.Group}s. -- @param Core.Set#SET_CARGO SetCargo The collection of @{Cargo} derived objects. --- @param Core.Set#SET_ZONE SetDeployZone The collection of deploy @{Zone}s, which are used to where the cargo will be deployed by the Helicopters. +-- @param Core.Set#SET_ZONE SetDeployZones The collection of deploy @{Zone}s, which are used to where the cargo will be deployed by the Helicopters. -- @return #AI_CARGO_DISPATCHER_HELICOPTER -- @usage -- diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index e8b7d24ce..139af0e16 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -29,7 +29,6 @@ AI_CARGO_QUEUE = {} -- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -- @param Core.Set#SET_CARGO CargoSet --- @param #number CombatRadius -- @return #AI_CARGO_HELICOPTER function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) @@ -530,15 +529,17 @@ end -- @param Event -- @param To -- @param Core.Point#COORDINATE Coordinate --- @param #number Speed -function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordinate ) +-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. +function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordinate, Speed ) if Helicopter and Helicopter:IsAlive() ~= nil then Helicopter:Activate() self.RoutePickup = true - Coordinate.y = math.random( 50, 200 ) + Coordinate.y = math.random( 50, 200 ) + + local _speed=Speed or Helicopter:GetSpeedMax()*0.5 local Route = {} @@ -551,7 +552,7 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, - 150, + _speed, true ) @@ -560,7 +561,7 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, - 150, + _speed, true ) @@ -596,8 +597,8 @@ end -- @param Event -- @param To -- @param Core.Point#COORDINATE Coordinate --- @param #number Speed -function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordinate ) +-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. +function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordinate, Speed ) if Helicopter and Helicopter:IsAlive() ~= nil then @@ -608,7 +609,9 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin --- Calculate the target route point. - Coordinate.y = math.random( 50, 200 ) + Coordinate.y = math.random( 50, 200 ) + + local _speed=Speed or Helicopter:GetSpeedMax()*0.5 --- Create a route point of type air. local CoordinateFrom = Helicopter:GetCoordinate() @@ -616,7 +619,7 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, - 150, + _speed, true ) Route[#Route+1] = WaypointFrom @@ -628,7 +631,7 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, - 150, + _speed, true ) @@ -641,7 +644,7 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin local Tasks = {} Tasks[#Tasks+1] = Helicopter:TaskFunction( "AI_CARGO_HELICOPTER._Deploy", self, Coordinate ) - Tasks[#Tasks+1] = Helicopter:TaskOrbitCircle( math.random( 30, 100 ), 150, CoordinateTo:GetRandomCoordinateInRadius( 800, 500 ) ) + Tasks[#Tasks+1] = Helicopter:TaskOrbitCircle( math.random( 30, 100 ), _speed, CoordinateTo:GetRandomCoordinateInRadius( 800, 500 ) ) --Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() ) Route[#Route].task = Helicopter:TaskCombo( Tasks ) @@ -662,8 +665,8 @@ end -- @param Event -- @param To -- @param Core.Point#COORDINATE Coordinate --- @param #number Speed -function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinate ) +-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. +function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinate, Speed ) if Helicopter and Helicopter:IsAlive() ~= nil then @@ -673,7 +676,9 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat --- Calculate the target route point. - Coordinate.y = math.random( 50, 200 ) + Coordinate.y = math.random( 50, 200 ) + + local _speed=Speed or Helicopter:GetSpeedMax()*0.5 --- Create a route point of type air. local CoordinateFrom = Helicopter:GetCoordinate() @@ -681,7 +686,7 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, - 150, + _speed, true ) Route[#Route+1] = WaypointFrom @@ -692,7 +697,7 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, - 150, + _speed, true ) From dc39107daaad1fe31dfc8452fd2070de905d27a2 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 23 Jul 2018 15:54:09 +0200 Subject: [PATCH 213/420] JTAC --- Moose Development/Moose/Functional/JTAC.lua | 163 ++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 Moose Development/Moose/Functional/JTAC.lua diff --git a/Moose Development/Moose/Functional/JTAC.lua b/Moose Development/Moose/Functional/JTAC.lua new file mode 100644 index 000000000..65a5f3d99 --- /dev/null +++ b/Moose Development/Moose/Functional/JTAC.lua @@ -0,0 +1,163 @@ +--- **Functional** - (R2.4) JTAC +-- +-- === +-- +-- JTAC mimic +-- +-- ## Features: +-- +-- * Feature 1 +-- * Feature 2 +-- +-- ==== +-- +-- # Demo Missions +-- +-- ### [MOOSE - ALL Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS) +-- +-- ==== +-- +-- # YouTube Channel +-- +-- ### [MOOSE YouTube Channel](https://www.youtube.com/channel/UCjrA9j5LQoWsG4SpS8i79Qg) +-- +-- === +-- +-- ### Author: **[funkyfranky](https://forums.eagle.ru/member.php?u=115026)** +-- +-- ### Contributions: [FlightControl](https://forums.eagle.ru/member.php?u=89536) +-- +-- ==== +-- @module Functional.Jtac +-- @image JTAC.JPG + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- JTAC class +-- @type JTAC +-- @field #string ClassName Name of the class. + +--- Easy assignment of JTAC. +-- +-- A new ARTY object can be created with the @{#ARTY.New}(*group*) contructor. +-- The parameter *group* has to be a MOOSE Group object and defines ARTY group. +-- +-- The ARTY FSM process can be started by the @{#ARTY.Start}() command. +-- +-- ## The ARTY Process +-- +-- ![Process](..\Presentations\ARTY\ARTY_Process.png) +-- +-- +-- +-- @field #ARTY +JTAC={ + ClassName="JTAC", + Debug=false, +} + +--- Some ID to identify who we are in output of the DCS.log file. +-- @field #string id +JTAC.id="JTAC | " + +--- Arty script version. +-- @field #string version +JTAC.version="0.0.1" + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- TODO list: +-- TODO: a lot. + + +--- Creates a new JTAC object. +-- @param #JTAC self +-- @param Wrapper.Group#GROUP group The GROUP object for which artillery tasks should be assigned. +-- @param alias (Optional) Alias name the group will be calling itself when sending messages. Default is the group name. +-- @return #ARTY ARTY object or nil if group does not exist or is not a ground or naval group. +function JTAC:New(group, alias) + BASE:F2(group) + + -- Inherits from FSM_CONTROLLABLE + local self=BASE:Inherit(self, FSM_CONTROLLABLE:New()) -- #JTAC + + -- Check that group is present. + if group then + self:T(JTAC.id..string.format("JTAC script version %s. Added group %s.", JTAC.version, group:GetName())) + else + self:E(JTAC.id.."ERROR: Requested JTAC group does not exist! (Has to be a MOOSE group.)") + return nil + end + + -- Set the controllable for the FSM. + self:SetControllable(group) + + --------------- + -- Transitions: + --------------- + + -- Entry. + self:AddTransition("*", "Start", "Ready") + self:AddTransition("Ready", "LaserOn", "Lasing") + self:AddTransition("Lasing", "LaserOff", "Ready") + +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- FSM Start Event +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- After "Start" event. +-- @param #JTAC self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function JTAC:onafterStart(Controllable, From, Event, To) + --self:_EventFromTo("onafterStart", Event, From, To) + + -- Debug output. + local text=string.format("Started JTAC version %s for group %s.", JTAC.version, Controllable:GetName()) + self:E(JTAC.id..text) + MESSAGE:New(text, 5):ToAllIf(self.Debug) + +end + +--- After "LaserOn" event. +-- @param #JTAC self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function JTAC:onafterLaserOn(Controllable, From, Event, To) + --self:_EventFromTo("onafterStart", Event, From, To) + + -- Debug output. + local text=string.format("Started JTAC version %s for group %s.", JTAC.version, Controllable:GetName()) + self:E(JTAC.id..text) + MESSAGE:New(text, 5):ToAllIf(self.Debug) + +end + + +--- After "LaserOff" event. +-- @param #JTAC self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function JTAC:onafterLaserOff(Controllable, From, Event, To) + --self:_EventFromTo("onafterStart", Event, From, To) + + -- Debug output. + local text=string.format("Started JTAC version %s for group %s.", JTAC.version, Controllable:GetName()) + self:E(JTAC.id..text) + MESSAGE:New(text, 5):ToAllIf(self.Debug) + +end + + + + + + + From 3526203ccb4400e7fb20076fbe6afae6b99d5d1c Mon Sep 17 00:00:00 2001 From: funkyfranky <> Date: Mon, 23 Jul 2018 22:24:59 +0200 Subject: [PATCH 214/420] DESIGNATE Class Fix DESIGNATE - GetRootMenu() function seems obsolete and caused a crash in DESIGNATE class. Needs to be replaced by GetMenu() Function. This bug only appeared when a mission is given in DESIGNATE:New(...) --- Moose Development/Moose/Core/Menu.lua | 13 +++++++++---- Moose Development/Moose/Core/Set.lua | 1 + Moose Development/Moose/Functional/Designate.lua | 5 +++-- Moose Development/Moose/Tasking/CommandCenter.lua | 1 + Moose Development/Moose/Tasking/Mission.lua | 6 ++++-- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Core/Menu.lua b/Moose Development/Moose/Core/Menu.lua index fd677b5c5..3e4b784a8 100644 --- a/Moose Development/Moose/Core/Menu.lua +++ b/Moose Development/Moose/Core/Menu.lua @@ -89,11 +89,14 @@ function MENU_INDEX:PrepareCoalition( CoalitionSide ) self.Coalition[CoalitionSide].Menus = self.Coalition[CoalitionSide].Menus or {} end - +--- +-- @param Wrapper.Group#GROUP Group function MENU_INDEX:PrepareGroup( Group ) + if Group and Group:IsAlive() then local GroupName = Group:GetName() self.Group[GroupName] = self.Group[GroupName] or {} self.Group[GroupName].Menus = self.Group[GroupName].Menus or {} + end end @@ -133,9 +136,11 @@ end function MENU_INDEX:HasGroupMenu( Group, Path ) - - local MenuGroupName = Group:GetName() - return self.Group[MenuGroupName].Menus[Path] + if Group and Group:IsAlive() then + local MenuGroupName = Group:GetName() + return self.Group[MenuGroupName].Menus[Path] + end + return nil end function MENU_INDEX:SetGroupMenu( Group, Path, Menu ) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 5453f9844..e81beb007 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -798,6 +798,7 @@ function SET_GROUP:GetAliveSet() -- Clean the Set before returning with only the alive Groups. for GroupName, GroupObject in pairs( self.Set ) do + local GroupObject=GroupObject --Wrapper.Group#GROUP if GroupObject then if GroupObject:IsAlive() then AliveSet:Add( GroupName, GroupObject ) diff --git a/Moose Development/Moose/Functional/Designate.lua b/Moose Development/Moose/Functional/Designate.lua index f23961e12..25d554466 100644 --- a/Moose Development/Moose/Functional/Designate.lua +++ b/Moose Development/Moose/Functional/Designate.lua @@ -175,7 +175,7 @@ do -- DESIGNATE -- Smoke will fire for 5 minutes. -- Each available recce within range will smoke a target. -- Smoking can be requested while lasing targets. - -- Smoke will appear “around†the targets, because of accuracy limitations. + -- Smoke will appear "around" the targets, because of accuracy limitations. -- -- -- Have FUN! @@ -952,7 +952,8 @@ do -- DESIGNATE local MissionMenu = nil if self.Mission then - MissionMenu = self.Mission:GetRootMenu( AttackGroup ) + --MissionMenu = self.Mission:GetRootMenu( AttackGroup ) + MissionMenu = self.Mission:GetMenu( AttackGroup ) end local MenuTime = timer.getTime() diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index 4b3e8a17c..ce0ab6ce5 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -426,6 +426,7 @@ end --- Gets the commandcenter menu structure governed by the HQ command center. -- @param #COMMANDCENTER self +-- @param Wrapper.Group#Group TaskGroup Task Group. -- @return Core.Menu#MENU_COALITION function COMMANDCENTER:GetMenu( TaskGroup ) diff --git a/Moose Development/Moose/Tasking/Mission.lua b/Moose Development/Moose/Tasking/Mission.lua index 6bbca053e..877754efa 100644 --- a/Moose Development/Moose/Tasking/Mission.lua +++ b/Moose Development/Moose/Tasking/Mission.lua @@ -611,13 +611,14 @@ function MISSION:RemoveTaskMenu( Task ) end ---- Gets the root mission menu for the TaskGroup. +--- Gets the root mission menu for the TaskGroup. Obsolete?! Originally no reference to TaskGroup parameter! -- @param #MISSION self +-- @param Wrapper.Group#GROUP TaskGroup Task group. -- @return Core.Menu#MENU_COALITION self function MISSION:GetRootMenu( TaskGroup ) -- R2.2 local CommandCenter = self:GetCommandCenter() - local CommandCenterMenu = CommandCenter:GetMenu() + local CommandCenterMenu = CommandCenter:GetMenu( TaskGroup ) local MissionName = self:GetText() --local MissionMenu = CommandCenterMenu:GetMenu( MissionName ) @@ -629,6 +630,7 @@ end --- Gets the mission menu for the TaskGroup. -- @param #MISSION self +-- @param Wrapper.Group#GROUP TaskGroup Task group. -- @return Core.Menu#MENU_COALITION self function MISSION:GetMenu( TaskGroup ) -- R2.1 -- Changed Menu Structure From a09cd6e667764bf5a42b864c44a61bc8a2bf06b7 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Tue, 24 Jul 2018 15:59:42 +0200 Subject: [PATCH 215/420] JTAC --- Moose Development/Moose/Functional/JTAC.lua | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Functional/JTAC.lua b/Moose Development/Moose/Functional/JTAC.lua index 65a5f3d99..5d8b4050e 100644 --- a/Moose Development/Moose/Functional/JTAC.lua +++ b/Moose Development/Moose/Functional/JTAC.lua @@ -49,7 +49,7 @@ -- -- -- --- @field #ARTY +-- @field #JTAC JTAC={ ClassName="JTAC", Debug=false, @@ -68,12 +68,13 @@ JTAC.version="0.0.1" -- TODO list: -- TODO: a lot. +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Creates a new JTAC object. -- @param #JTAC self -- @param Wrapper.Group#GROUP group The GROUP object for which artillery tasks should be assigned. -- @param alias (Optional) Alias name the group will be calling itself when sending messages. Default is the group name. --- @return #ARTY ARTY object or nil if group does not exist or is not a ground or naval group. +-- @return #JTAC JTAC object or nil if group does not exist or is not a ground or naval group. function JTAC:New(group, alias) BASE:F2(group) @@ -98,6 +99,7 @@ function JTAC:New(group, alias) -- Entry. self:AddTransition("*", "Start", "Ready") self:AddTransition("Ready", "LaserOn", "Lasing") + self:AddTransition("Lasing", "Lasing", "Lasing") self:AddTransition("Lasing", "LaserOff", "Ready") end @@ -128,14 +130,18 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -function JTAC:onafterLaserOn(Controllable, From, Event, To) +-- @param Wrapper.Unit#UNIT Target Target that should be lased. +function JTAC:onafterLaserOn(Controllable, From, Event, To, Target) --self:_EventFromTo("onafterStart", Event, From, To) -- Debug output. - local text=string.format("Started JTAC version %s for group %s.", JTAC.version, Controllable:GetName()) + local text=string.format("JTAC %s lasing target %s.", Controllable:GetName(), Target:GetName()) self:E(JTAC.id..text) MESSAGE:New(text, 5):ToAllIf(self.Debug) + -- Start lasing. + Controllable:LaseUnit(Target, self.LaserCode, self.Duration) + end @@ -149,10 +155,13 @@ function JTAC:onafterLaserOff(Controllable, From, Event, To) --self:_EventFromTo("onafterStart", Event, From, To) -- Debug output. - local text=string.format("Started JTAC version %s for group %s.", JTAC.version, Controllable:GetName()) + local text=string.format("JTAC %s stoped lasing.", Controllable:GetName()) self:E(JTAC.id..text) MESSAGE:New(text, 5):ToAllIf(self.Debug) + -- Turn of laser. + Controllable:LaseOff() + end From 4dbcaf120f4eb93464f9702a8ae5d336f7d78411 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Fri, 27 Jul 2018 12:13:14 +0200 Subject: [PATCH 216/420] - Fixed a bug with the deploy zones, (they werent listed anymore for deployment!), when using the task dispatcher. - Added documentation for task cargo! Pictures and a decent explanation. --- .../Moose/Tasking/Task_CARGO.lua | 159 +++++++++++++----- .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 19 ++- 2 files changed, 131 insertions(+), 47 deletions(-) diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index c78d6a69d..fd1d60ef7 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -99,7 +99,7 @@ -- This is done to ensure that **different teams** have a **defined scope** for defined cargo, and that **multiple teams** can join -- **multiple tasks**, transporting cargo **simultaneously** in a **cooperation**. -- --- In this example, there is a menu option to **Route to pickup cargo...". +-- In this example, there is a menu option to **Route to pickup cargo...**. -- Use this menu to route towards cargo locations for pickup into your carrier. -- -- ![Task_Types](../Tasking/Task_Cargo_Types_Menu.JPG) @@ -157,46 +157,73 @@ -- -- ### 3.3.2) Board Cargo (infantry). -- --- ![Task_Types](../Tasking/Boarding_Ready.JPG) +-- ![](../Tasking/Boarding_Ready.png) -- -- If your Carrier is within the **Reporting Range of the cargo**, and the cargo is **moveable**, the **cargo can be boarded**! -- This type of cargo will be most of the time be infantry. -- --- ![Boarding](../Tasking/Boarding_Menu.JPG) +-- ![](../Tasking/Boarding_Menu.png) -- --- A board menu has appeared, because your carrier is in boarding range of the cargo (infantry). +-- A **Board cargo...** sub menu has appeared, because your carrier is in boarding range of the cargo (infantry). +-- Select the **Board cargo...** menu. -- --- ![Boarding](../Tasking/Boarding_Started.JPG) +-- ![](../Tasking/Boarding_Menu_Engineers.png) -- +-- Any cargo that can be boarded (thus movable cargo), within boarding range of the carrier, will be listed here! +-- In this example, the cargo **Engineers** can be boarded, by selecting the menu option. -- --- Select the option from the action menu, then select the cargo to be boarded, and the cargo will start moving towards your carrier. --- Note that a message is displayed by the infantry cargo that boarding has started. +-- ![](../Tasking/Boarding_Started.png) -- --- ![Boarding](../Tasking/Boarding_Ongoing.JPG) +-- After the menu option to board the cargo has been selected, the boarding process is started. +-- A message from the cargo is communicated to the pilot, that boarding is started. +-- +-- ![](../Tasking/Boarding_Ongoing.png) +-- +-- **The pilot must wait at the exact position until all cargo has been boarded!** -- -- The moveable cargo will run in formation to your carrier, and will board one by one, depending on the near range set by the mission designer. -- The near range as added because carriers can be large or small, depending on the object size of the carrier. -- --- ![Boarding](../Tasking/Boarding_Almost_Done.JPG) +-- ![](../Tasking/Boarding_In_Progress.png) +-- +-- ![](../Tasking/Boarding_Almost_Done.png) -- -- Note that multiple units may need to board your Carrier, so it is required to await the full boarding process. -- --- ![Boarding](../Tasking/Boarding_Done.JPG) +-- ![](../Tasking/Boarding_Done.png) -- -- Once the cargo is fully boarded within your Carrier, you will be notified of this. -- --- Note that for airborne Carriers, it is required to land first before the Boarding process can be initiated. --- If during boarding the Carrier gets airborne, the boarding process will be cancelled. +-- **Remarks:** +-- +-- * For airborne Carriers, it is required to land first before the Boarding process can be initiated. +-- If during boarding the Carrier gets airborne, the boarding process will be cancelled. +-- * The carrier must remain stationary when the boarding sequence has started until further notified. -- -- ### 3.3.3) Load Cargo. -- --- If your Carrier is within the **Loading Range of the cargo**, and the cargo is **stationary**, the **cargo can be loaded**, but not boarded! +-- Cargo can be loaded into vehicles or helicopters or airplanes, as long as the carrier is sufficiently near to the cargo object. +-- +-- ![](../Tasking/Loading_Ready.png) +-- +-- If your Carrier is within the **Loading Range of the cargo**, thus, sufficiently near to the cargo, and the cargo is **stationary**, the **cargo can be loaded**, but not boarded! +-- +-- ![](../Tasking/Loading_Menu.png) +-- +-- Select the task action menu and now a **Load cargo...** sub menu will be listed. +-- Select the **Load cargo...** sub menu, and a further detailed menu will be shown. +-- +-- ![](../Tasking/Loading_Menu_Crate.png) +-- +-- For each non-moveable cargo object (crates etc), **within loading range of the carrier**, the cargo will be listed and can be loaded into the carrier! +-- +-- ![](../Tasking/Loading_Cargo_Loaded.png) -- --- Select the task action menu and now a **Load option** will be listed with the cargo name next to it! --- Select the option from the action menu, and the cargo will loaded into your carrier. -- Once the cargo is loaded within your Carrier, you will be notified of this. -- --- Note that for airborne Carriers, it is required to land first right near the cargo, before the loading process can be initiated. +-- **Remarks:** +-- +-- * For airborne Carriers, it is required to **land first right near the cargo**, before the loading process can be initiated. -- As stated, this requires some pilot skills :-) -- -- ### 3.3.4) Sling Load Cargo (helicopters only). @@ -213,6 +240,8 @@ -- -- ## 3.4) Deploy cargo by Unboarding, Unloading and Sling Deploying. -- +-- #### **Deploying the relevant cargo within deploy zones, will make you achieve cargo transportation tasks!!!** +-- -- There are two different ways how cargo can be deployed: -- -- - **Unboarding**: Moveable cargo (like infantry or vehicles), can be unboarded, that means, @@ -231,53 +260,103 @@ -- Fortunately, the HQ can help to route you to the locations of deploy zone. -- Use the task action menu to receive HQ help for this. -- --- ![Task_Types](../Tasking/Task_Cargo_Actions.JPG) +-- ![](../Tasking/Routing_Deploy_Zone_Menu.png) -- -- Depending on the location within the battlefield, the task action menu will contain **Route options** that can be selected -- to start the HQ sending you routing messages. Also, if the carrier cargo bays contain cargo, -- then beside **Route options** there will also be **Deploy options** listed. -- These **Deploy options** are meant to route you to the deploy zone locations. -- +-- ![](../Tasking/Routing_Deploy_Zone_Menu_Workplace.png) +-- +-- Depending on the task that you have selected, the deploy zones will be listed. +-- **There may be multiple deploy zones within the mission, but only the deploy zones relevant for your task will be available in the menu!** +-- +-- ![](../Tasking/Routing_Deploy_Zone_Message.png) +-- +-- When a routing option is selected, you are sent routing messages in a selected coordinate format. -- Possible routing coordinate formats are: Bearing Range (BR), Lattitude Longitude (LL) or Military Grid System (MGRS). --- Note that for LL, there are two sub formats. +-- Note that for LL, there are two sub formats. (See pickup). +-- +-- ![](../Tasking/Routing_Deploy_Zone_Arrived.png) +-- +-- When you are within the range of the deploy zone (can be also a polygon!), a message is communicated by HQ that you have arrived within the zone! -- -- The routing messages are formulated in the coordinate format that is currently active as configured in your settings profile. -- ![Task_Types](../Tasking/Task_Cargo_Settings.JPG) -- Use the **Settings Menu** to select the coordinate format that you would like to use for location determination. -- --- ## 3.4) Deploy Cargo. --- --- Various Deployment Zones can be foreseen in the scope of the Cargo transportation. Each deployment zone can be of varying @{Zone} type. --- The Cargo menu provides with menu options to execute an action to steer your Carrier to a specific Zone. --- --- In order to deploy cargo, use the task action menu to select a cargo to route to. --- When selected, the HQ will send you routing messages indicating the location of the deploy zone. --- --- Upon arrival at the deploy zone, the HQ will contact you and further instructions will be given. --- -- ### 3.4.1) Unboard Cargo. -- --- If your Carrier is within the **deploy zone**, and the cargo is **moveable**, the **cargo can be unboarded**! +-- If your carrier contains cargo, and the cargo is **moveable**, the **cargo can be unboarded**! +-- You can only unload cargo if there is cargo within your cargo bays within the carrier. -- --- Select the task action menu and now an **Unboard option** will be listed with the cargo name next to it! --- Select the option from the action menu, and the cargo will step out of your carrier and will move towards a grouping point. +-- ![](../Tasking/Unboarding_Menu.png) +-- +-- Select the task action menu and now an **Unboard cargo...** sub menu will be listed! +-- Again, this option will only be listed if there is a non moveable cargo within your cargo bays. +-- +-- ![](../Tasking/Unboarding_Menu_Engineers.png) +-- +-- Now you will see a menu option to unload the non-moveable cargo. +-- In this example, you can unload the **Engineers** that was loaded within your carrier cargo bays. +-- Depending on the cargo loaded within your cargo bays, you will see other options here! +-- Select the relevant menu option from the cargo unload menu, and the cargo will unloaded from your carrier. +-- +-- ![](../Tasking/Unboarding_Started.png) +-- +-- **The cargo will step out of your carrier and will move towards a grouping point.** +-- When the unboarding process has started, you will be notified by a message to your carrier. +-- +-- ![](../Tasking/Unboarding_In_Progress.png) -- -- The moveable cargo will unboard one by one, so note that multiple units may need to unboard your Carrier, -- so it is required to await the full completion of the unboarding process. --- Once the cargo is fully unboarded from your Carrier, you will be notified of this. -- --- Note that for airborne Carriers, it is required to land first before the unboarding process can be initiated. --- If during unboarding the Carrier gets airborne, the unboarding process will be cancelled. +-- ![](../Tasking/Unboarding_Done.png) +-- +-- Once the cargo is fully unboarded from your carrier, you will be notified of this. +-- +-- **Remarks:** +-- +-- * For airborne carriers, it is required to land first before the unboarding process can be initiated. +-- If during unboarding the Carrier gets airborne, the unboarding process will be cancelled. +-- * Once the moveable cargo is unboarded, they will start moving towards a specified gathering point. +-- * The moveable cargo will send a message to your carrier with unboarding status updates. +-- +-- **Deploying a cargo within a deployment zone, may complete a deployment task! So ensure that you deploy the right cargo at the right deployment zone!** -- -- ### 3.4.2) Unload Cargo. -- --- If your Carrier is within the **deploy zone**, and the cargo is **stationary**, the **cargo can be unloaded**, but not unboarded! +-- If your carrier contains cargo, and the cargo is **stationary**, the **cargo can be unloaded**, but not unboarded! +-- You can only unload cargo if there is cargo within your cargo bays within the carrier. -- --- Select the task action menu and now an **Unload option** will be listed with the cargo name next to it! --- Select the option from the action menu, and the cargo will unloaded from your carrier. --- Once the cargo is unloaded fom your Carrier, you will be notified of this. +-- ![](../Tasking/Unloading_Menu.png) +-- +-- Select the task action menu and now an **Unload cargo...** sub menu will be listed! +-- Again, this option will only be listed if there is a non moveable cargo within your cargo bays. +-- +-- ![](../Tasking/Unloading_Menu_Crate.png) +-- +-- Now you will see a menu option to unload the non-moveable cargo. +-- In this example, you can unload the **Crate** that was loaded within your carrier cargo bays. +-- Depending on the cargo loaded within your cargo bays, you will see other options here! +-- Select the relevant menu option from the cargo unload menu, and the cargo will unloaded from your carrier. +-- +-- ![](../Tasking/Unloading_Done.png) +-- +-- Once the cargo is unloaded fom your Carrier, you may be notified of this, when there is a truck near to the cargo. +-- If there is no truck near to the unload area, no message will be sent to your carrier! +-- +-- **Remarks:** +-- +-- * For airborne Carriers, it is required to land first, before the unloading process can be initiated. +-- * A truck must be near the unload area to get messages to your carrier of the unload event! +-- * Unloading is only for non-moveable cargo. +-- * The non-moveable cargo must be within your cargo bays, or no unload option will be available. +-- +-- **Deploying a cargo within a deployment zone, may complete a deployment task! So ensure that you deploy the right cargo at the right deployment zone!** -- --- Note that for airborne Carriers, it is required to land first at the deploy zone, before the unloading process can be initiated. -- -- ### 3.4.3) Sling Deploy Cargo (helicopters only). -- @@ -286,6 +365,8 @@ -- -- To sling deploy cargo, there is no task action menu required. Just follow the normal sling deploying procedure. -- +-- **Deploying a cargo within a deployment zone, may complete a deployment task! So ensure that you deploy the right cargo at the right deployment zone!** +-- -- === -- -- ### Author: **FlightControl** diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index b232c7138..5da5638cc 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -485,6 +485,8 @@ do -- TASK_CARGO_DISPATCHER else error( "Task does not exist" ) end + + self:ManageTasks() return self end @@ -503,6 +505,8 @@ do -- TASK_CARGO_DISPATCHER else error( "Task does not exist" ) end + + self:ManageTasks() return self end @@ -584,12 +588,6 @@ do -- TASK_CARGO_DISPATCHER Transport.Task = TASK_CARGO_TRANSPORT:New( Mission, self.SetGroup, TransportName, Transport.SetCargo, Transport.Briefing ) Mission:AddTask( Transport.Task ) TaskReport:Add( TransportName ) - if Transport.DeployZones then - Transport.Task:SetDeployZones( Transport.DeployZones or {} ) - else - Transport.Task:SetDeployZones( self.DefaultDeployZones or {} ) - end - function Transport.Task.OnEnterSuccess( Task, From, Event, To ) self:Success( Task ) end @@ -605,9 +603,14 @@ do -- TASK_CARGO_DISPATCHER function Transport.Task.onenterAborted( Task, From, Event, To ) self:Aborted( Task ) end - - end + + if Transport.DeployZones then + Transport.Task:SetDeployZones( Transport.DeployZones or {} ) + else + Transport.Task:SetDeployZones( self.DefaultDeployZones or {} ) + end + end From 52f429a9911ad566be9d24af3bd12888cf86e85f Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Fri, 27 Jul 2018 15:08:28 +0200 Subject: [PATCH 217/420] DCS, Detection, Controllable, Group --- Moose Development/Moose/DCS.lua | 100 +++++++++++------- .../Moose/Functional/Detection.lua | 1 + .../Moose/Wrapper/Controllable.lua | 13 +++ Moose Development/Moose/Wrapper/Group.lua | 10 +- 4 files changed, 85 insertions(+), 39 deletions(-) diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua index 253632052..0d40a523b 100644 --- a/Moose Development/Moose/DCS.lua +++ b/Moose Development/Moose/DCS.lua @@ -1,15 +1,19 @@ --- DCS API prototypes +-- See [https://wiki.hoggitworld.com/view/Simulator_Scripting_Engine_Documentation](https://wiki.hoggitworld.com/view/Simulator_Scripting_Engine_Documentation) +-- for further explanation and examples. -- @module DCS -- @image MOOSE.JPG do -- world - --- @type world + --- [DCS Enum world](https://wiki.hoggitworld.com/view/DCS_enum_world) + -- @type world -- @field #world.event event - --- @type world.event + --- [https://wiki.hoggitworld.com/view/DCS_enum_world](https://wiki.hoggitworld.com/view/DCS_enum_world) + -- @type world.event -- @field S_EVENT_INVALID - -- @field S_EVENT_SHOT + -- @field S_EVENT_SHOT [https://wiki.hoggitworld.com/view/DCS_event_shot](https://wiki.hoggitworld.com/view/DCS_event_shot) -- @field S_EVENT_HIT -- @field S_EVENT_TAKEOFF -- @field S_EVENT_LAND @@ -41,36 +45,38 @@ end -- world do -- env - --- @type env + --- [DCS Singleton env](https://wiki.hoggitworld.com/view/DCS_singleton_env) + -- @type env + --- Add message to simulator log with caption "INFO". Message box is optional. -- @function [parent=#env] info - -- @field #string message message string to add to log. - -- @field #boolean showMessageBox If the parameter is true Message Box will appear. Optional. + -- @param #string message message string to add to log. + -- @param #boolean showMessageBox If the parameter is true Message Box will appear. Optional. --- Add message to simulator log with caption "WARNING". Message box is optional. -- @function [parent=#env] warning - -- @field #string message message string to add to log. - -- @field #boolean showMessageBox If the parameter is true Message Box will appear. Optional. + -- @param #string message message string to add to log. + -- @param #boolean showMessageBox If the parameter is true Message Box will appear. Optional. --- Add message to simulator log with caption "ERROR". Message box is optional. -- @function [parent=#env] error - -- @field #string message message string to add to log. - -- @field #boolean showMessageBox If the parameter is true Message Box will appear. Optional. + -- @param #string message message string to add to log. + -- @param #boolean showMessageBox If the parameter is true Message Box will appear. Optional. --- Enables/disables appearance of message box each time lua error occurs. -- @function [parent=#env] setErrorMessageBoxEnabled - -- @field #boolean on if true message box appearance is enabled. - - env = {} --#env + -- @param #boolean on if true message box appearance is enabled. + + env = {} --#env end -- env do -- timer - --- @type timer - + --- [DCS Singleton timer](https://wiki.hoggitworld.com/view/DCS_singleton_timer) + -- @type timer --- Returns model time in seconds. -- @function [parent=#timer] getTime @@ -117,11 +123,12 @@ end do -- land - --- @type land + --- [DCS Singleton land](https://wiki.hoggitworld.com/view/DCS_singleton_land) + -- @type land -- @field #land.SurfaceType SurfaceType - - --- @type land.SurfaceType + --- [Type of surface enumerator](https://wiki.hoggitworld.com/view/DCS_singleton_land) + -- @type land.SurfaceType -- @field LAND -- @field SHALLOW_WATER -- @field WATER @@ -144,10 +151,16 @@ end -- land do -- country - --- @type country - -- @field #country.id id + --- [DCS Enum country](https://wiki.hoggitworld.com/view/DCS_enum_country) + -- @type country + -- @field #country.id id - --- @type country.id + --- [DCS Enum country](https://wiki.hoggitworld.com/view/DCS_enum_country) + -- @field #country + country = {} + + --- [DCS enumerator country](https://wiki.hoggitworld.com/view/DCS_enum_country) + -- @type country.id -- @field RUSSIA -- @field UKRAINE -- @field USA @@ -223,10 +236,9 @@ do -- country -- @field OMAN -- @field UNITED_ARAB_EMIRATES - country = {} -- #country - end -- country + do -- Command --- @type Command @@ -239,10 +251,12 @@ end -- Command do -- coalition - --- @type coalition + --- [DCS Enum coalition](https://wiki.hoggitworld.com/view/DCS_enum_coalition) + -- @type coalition -- @field #coalition.side side - --- @type coalition.side + --- [DCS Enum coalition.side](https://wiki.hoggitworld.com/view/DCS_enum_coalition) + -- @type coalition.side -- @field NEUTRAL -- @field RED -- @field BLUE @@ -349,7 +363,8 @@ do -- Types --- @type Time -- @extends #number - --- A task descriptor (internal structure for DCS World) + --- A task descriptor (internal structure for DCS World). See [https://wiki.hoggitworld.com/view/Category:Tasks](https://wiki.hoggitworld.com/view/Category:Tasks). + -- In MOOSE, these tasks can be accessed via @{Wrapper.Controllable#CONTROLLABLE}. -- @type Task -- @field #string id -- @field #Task.param param @@ -365,11 +380,13 @@ end -- do -- Object - --- @type Object + --- [DCS Class Object](https://wiki.hoggitworld.com/view/DCS_Class_Object) + -- @type Object -- @field #Object.Category Category -- @field #Object.Desc Desc - --- @type Object.Category + --- [DCS Enum Object.Category](https://wiki.hoggitworld.com/view/DCS_Class_Object) + -- @type Object.Category -- @field UNIT -- @field WEAPON -- @field STATIC @@ -439,10 +456,10 @@ end -- Object do -- CoalitionObject - --- @type CoalitionObject + --- [DCS Class CoalitionObject](https://wiki.hoggitworld.com/view/DCS_Class_Coalition_Object) + -- @type CoalitionObject -- @extends #Object - --- Returns coalition of the object. -- @function [parent=#CoalitionObject] getCoalition -- @param #CoalitionObject self @@ -453,14 +470,15 @@ do -- CoalitionObject -- @param #CoalitionObject self -- @return #country.id -CoalitionObject = {} --#CoalitionObject + CoalitionObject = {} --#CoalitionObject end -- CoalitionObject do -- Airbase - --- Represents airbases: airdromes, helipads and ships with flying decks or landing pads. + --- [DCS Class Airbase](https://wiki.hoggitworld.com/view/DCS_Class_Airbase) + -- Represents airbases: airdromes, helipads and ships with flying decks or landing pads. -- @type Airbase -- @extends #CoalitionObject -- @field #Airbase.ID ID Identifier of an airbase. It assigned to an airbase by the Mission Editor automatically. This identifier is used in AI tasks to refer an airbase that exists (spawned and not dead) or not. @@ -962,12 +980,14 @@ end -- Group do -- AI - --- @type AI + --- [https://wiki.hoggitworld.com/view/DCS_enum_AI](https://wiki.hoggitworld.com/view/DCS_enum_AI) + -- @type AI -- @field #AI.Skill Skill -- @field #AI.Task Task -- @field #AI.Option Option - --- @type AI.Skill + --- [https://wiki.hoggitworld.com/view/DCS_enum_AI](https://wiki.hoggitworld.com/view/DCS_enum_AI) + -- @type AI.Skill -- @field AVERAGE -- @field GOOD -- @field HIGH @@ -975,7 +995,8 @@ do -- AI -- @field PLAYER -- @field CLIENT - --- @type AI.Task + --- [https://wiki.hoggitworld.com/view/DCS_enum_AI](https://wiki.hoggitworld.com/view/DCS_enum_AI) + -- @type AI.Task -- @field #AI.Task.WeaponExpend WeaponExpend -- @field #AI.Task.OrbitPattern OrbitPattern -- @field #AI.Task.Designation Designation @@ -984,7 +1005,8 @@ do -- AI -- @field #AI.Task.AltitudeType AltitudeType -- @field #AI.Task.VehicleFormation VehicleFormation - --- @type AI.Task.WeaponExpend + --- [https://wiki.hoggitworld.com/view/DCS_enum_AI](https://wiki.hoggitworld.com/view/DCS_enum_AI) + -- @type AI.Task.WeaponExpend -- @field ONE -- @field TWO -- @field FOUR @@ -992,11 +1014,13 @@ do -- AI -- @field HALF -- @field ALL - --- @type AI.Task.OrbitPattern + --- [https://wiki.hoggitworld.com/view/DCS_enum_AI](https://wiki.hoggitworld.com/view/DCS_enum_AI) + -- @type AI.Task.OrbitPattern -- @field CIRCLE -- @field RACE_TRACK - --- @type AI.Task.Designation + --- [https://wiki.hoggitworld.com/view/DCS_enum_AI](https://wiki.hoggitworld.com/view/DCS_enum_AI) + -- @type AI.Task.Designation -- @field NO -- @field AUTO -- @field WP diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 33b4c9331..a547eb1d2 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -526,6 +526,7 @@ do -- DETECTION_BASE -- @param #string Event The Event string. -- @param #string To The To State string. -- @param Wrapper.Group#GROUP DetectionGroup The Group detecting. + -- @param #number DetectionTimeStamp Time stamp of detection event. function DETECTION_BASE:onafterDetectionGroup( From, Event, To, DetectionGroup, DetectionTimeStamp ) self.DetectionRun = self.DetectionRun + 1 diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index df8767019..84c25a1f4 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -356,6 +356,8 @@ end --- Clearing the Task Queue and Setting the Task on the queue from the controllable. -- @param #CONTROLLABLE self +-- @param #DCS.Task DCSTask DCS Task array. +-- @param #number WaitTime Time in seconds, before the task is set. -- @return Wrapper.Controllable#CONTROLLABLE self function CONTROLLABLE:SetTask( DCSTask, WaitTime ) self:F2( { DCSTask = DCSTask } ) @@ -424,6 +426,17 @@ end function CONTROLLABLE:TaskCondition( time, userFlag, userFlagValue, condition, duration, lastWayPoint ) self:F2( { time, userFlag, userFlagValue, condition, duration, lastWayPoint } ) +--[[ + StopCondition = { + time = Time, + userFlag = string, + userFlagValue = boolean, + condition = string, + duration = Time, + lastWaypoint = number, + } +--]] + local DCSStopCondition = {} DCSStopCondition.time = time DCSStopCondition.userFlag = userFlag diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 0ea42e035..587e5987b 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -856,7 +856,7 @@ function GROUP:IsCompletelyInZone( Zone ) return true end ---- Returns true if some units of the group are within a @{Zone}. +--- Returns true if some but NOT ALL units of the group are within a @{Zone}. -- @param #GROUP self -- @param Core.Zone#ZONE_BASE Zone The zone to test. -- @return #boolean Returns true if the Group is partially within the @{Core.Zone#ZONE_BASE} @@ -884,6 +884,14 @@ function GROUP:IsPartlyInZone( Zone ) end end +--- Returns true if part or all units of the group are within a @{Zone}. +-- @param #GROUP self +-- @param Core.Zone#ZONE_BASE Zone The zone to test. +-- @return #boolean Returns true if the Group is partially or completely within the @{Core.Zone#ZONE_BASE}. +function GROUP:IsPartlyOrCompletelyInZone( Zone ) + return self:IsPartlyInZone(Zone) or self:IsCompletelyInZone(Zone) +end + --- Returns true if none of the group units of the group are within a @{Zone}. -- @param #GROUP self -- @param Core.Zone#ZONE_BASE Zone The zone to test. From fa785a06bb89c3f053f3ef65b73641d15dfc8e23 Mon Sep 17 00:00:00 2001 From: funkyfranky <> Date: Sun, 29 Jul 2018 00:24:05 +0200 Subject: [PATCH 218/420] ARTY v1.0.5 ARTY * Groups can now be transported as cargo via ARTY:NewFromCargoGroup() function. * If immobile units are defined as cargo, targets out of range are not deleted from the queue. AI_CARGO_APC * Adjusted function documentation. AI_CARGO_DISPATCHER * Adjusted function documentation. AI_CARGO_HELICOPTER * Adjusted function documentation. CARGOG_ROUP * Ajusted function documentation. MESSAGE * Adjusted function documentation. * Added missing Settings parameter to some functions. DCS * Improved documentation. MISSION: * Adusted function documentatoin. --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 79 +++++++++-- .../Moose/AI/AI_Cargo_Dispatcher.lua | 12 +- .../Moose/AI/AI_Cargo_Helicopter.lua | 85 +++++++++--- Moose Development/Moose/Cargo/CargoGroup.lua | 12 +- Moose Development/Moose/Core/Message.lua | 13 +- Moose Development/Moose/DCS.lua | 105 ++++++++++---- .../Moose/Functional/Artillery.lua | 131 ++++++++++++++++-- Moose Development/Moose/Tasking/Mission.lua | 8 +- Moose Development/Moose/Wrapper/Group.lua | 3 +- 9 files changed, 354 insertions(+), 94 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 2c76f2786..a69f0aa27 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -117,6 +117,7 @@ function AI_CARGO_APC:New( APC, CargoSet, CombatRadius ) -- @param #string Event -- @param #string To -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. -- @return #boolean --- Pickup Handler OnAfter for AI_CARGO_APC @@ -126,17 +127,20 @@ function AI_CARGO_APC:New( APC, CargoSet, CombatRadius ) -- @param #string Event -- @param #string To -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. --- Pickup Trigger for AI_CARGO_APC -- @function [parent=#AI_CARGO_APC] Pickup -- @param #AI_CARGO_APC self -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. --- Pickup Asynchronous Trigger for AI_CARGO_APC -- @function [parent=#AI_CARGO_APC] __Pickup -- @param #AI_CARGO_APC self -- @param #number Delay -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. --- Deploy Handler OnBefore for AI_CARGO_APC -- @function [parent=#AI_CARGO_APC] OnBeforeDeploy @@ -145,6 +149,7 @@ function AI_CARGO_APC:New( APC, CargoSet, CombatRadius ) -- @param #string Event -- @param #string To -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. -- @return #boolean --- Deploy Handler OnAfter for AI_CARGO_APC @@ -154,17 +159,20 @@ function AI_CARGO_APC:New( APC, CargoSet, CombatRadius ) -- @param #string Event -- @param #string To -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. --- Deploy Trigger for AI_CARGO_APC -- @function [parent=#AI_CARGO_APC] Deploy -- @param #AI_CARGO_APC self -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. --- Deploy Asynchronous Trigger for AI_CARGO_APC -- @function [parent=#AI_CARGO_APC] __Deploy -- @param #AI_CARGO_APC self -- @param Core.Point#COORDINATE Coordinate -- @param #number Delay + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. --- Loaded Handler OnAfter for AI_CARGO_APC @@ -335,8 +343,12 @@ function AI_CARGO_APC:FollowToCarrier( Me, APCUnit, CargoGroup ) end ---- @param #AI_CARGO_APC self +--- On after Monitor event. +-- @param #AI_CARGO_APC self -- @param Wrapper.Group#GROUP APC +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. function AI_CARGO_APC:onafterMonitor( APC, From, Event, To ) self:F( { APC, From, Event, To } ) @@ -389,8 +401,12 @@ function AI_CARGO_APC:onafterMonitor( APC, From, Event, To ) end ---- @param #AI_CARGO_APC self +--- On before Load event. +-- @param #AI_CARGO_APC self -- @param Wrapper.Group#GROUP APC +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To ) self:F( { APC, From, Event, To } ) @@ -427,8 +443,13 @@ function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To ) end ---- @param #AI_CARGO_APC self +--- On after Board event. +-- @param #AI_CARGO_APC self -- @param Wrapper.Group#GROUP APC +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #string Cargo.Cargo#CARGO Cargo Cargo object. function AI_CARGO_APC:onafterBoard( APC, From, Event, To, Cargo ) self:F( { APC, From, Event, To, Cargo } ) @@ -443,8 +464,13 @@ function AI_CARGO_APC:onafterBoard( APC, From, Event, To, Cargo ) end ---- @param #AI_CARGO_APC self +--- On before Loaded event. +-- @param #AI_CARGO_APC self -- @param Wrapper.Group#GROUP APC +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @return #boolean Cargo loaded. function AI_CARGO_APC:onbeforeLoaded( APC, From, Event, To ) self:F( { APC, From, Event, To } ) @@ -470,8 +496,13 @@ function AI_CARGO_APC:onbeforeLoaded( APC, From, Event, To ) end ---- @param #AI_CARGO_APC self +--- On after Unload event. +-- @param #AI_CARGO_APC self -- @param Wrapper.Group#GROUP APC +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #boolean Deployed Cargo is deployed. function AI_CARGO_APC:onafterUnload( APC, From, Event, To, Deployed ) self:F( { APC, From, Event, To, Deployed } ) @@ -488,8 +519,14 @@ function AI_CARGO_APC:onafterUnload( APC, From, Event, To, Deployed ) end ---- @param #AI_CARGO_APC self +--- On after Unboard event. +-- @param #AI_CARGO_APC self -- @param Wrapper.Group#GROUP APC +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #string Cargo.Cargo#CARGO Cargo Cargo object. +-- @param #boolean Deployed Cargo is deployed. function AI_CARGO_APC:onafterUnboard( APC, From, Event, To, Cargo, Deployed ) self:F( { APC, From, Event, To, Cargo:GetName() } ) @@ -503,8 +540,15 @@ function AI_CARGO_APC:onafterUnboard( APC, From, Event, To, Cargo, Deployed ) end ---- @param #AI_CARGO_APC self +--- On before Unloaded event. +-- @param #AI_CARGO_APC self -- @param Wrapper.Group#GROUP APC +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #string Cargo.Cargo#CARGO Cargo Cargo object. +-- @param #boolean Deployed Cargo is deployed. +-- @return #boolean All cargo unloaded. function AI_CARGO_APC:onbeforeUnloaded( APC, From, Event, To, Cargo, Deployed ) self:F( { APC, From, Event, To, Cargo:GetName(), Deployed = Deployed } ) @@ -544,8 +588,12 @@ function AI_CARGO_APC:onbeforeUnloaded( APC, From, Event, To, Cargo, Deployed ) end ---- @param #AI_CARGO_APC self +--- On after Follow event. +-- @param #AI_CARGO_APC self -- @param Wrapper.Group#GROUP APC +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. function AI_CARGO_APC:onafterFollow( APC, From, Event, To ) self:F( { APC, From, Event, To } ) @@ -591,7 +639,8 @@ end ---- @param #AI_CARGO_APC self +--- On after Pickup event. +-- @param #AI_CARGO_APC self -- @param Wrapper.Group#GROUP APC -- @param From -- @param Event @@ -626,13 +675,14 @@ function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed ) end ---- @param #AI_CARGO_APC self +--- On after Deploy event. +-- @param #AI_CARGO_APC self -- @param Wrapper.Group#GROUP APC -- @param From -- @param Event -- @param To --- @param Core.Point#COORDINATE Coordinate --- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. +-- @param Core.Point#COORDINATE Coordinate Deploy place. +-- @param #number Speed Speed in km/h to drive to the depoly coordinate. Default is 50% of max possible speed the unit can go. function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed ) if APC and APC:IsAlive() then @@ -655,12 +705,13 @@ function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed ) end ---- @param #AI_CARGO_APC self +--- On after Home event. +-- @param #AI_CARGO_APC self -- @param Wrapper.Group#GROUP APC -- @param From -- @param Event -- @param To --- @param Core.Point#COORDINATE Coordinate +-- @param Core.Point#COORDINATE Coordinate Home place. -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. function AI_CARGO_APC:onafterHome( APC, From, Event, To, Coordinate, Speed ) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 6bce0816e..688bbd759 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -90,7 +90,7 @@ AI_CARGO_DISPATCHER.PickupCargo = {} -- @param #AI_CARGO_DISPATCHER self -- @param Core.Set#SET_GROUP SetCarrier -- @param Core.Set#SET_CARGO SetCargo --- @param Core.Set#SET_ZONE SetDeployZone +-- @param Core.Set#SET_ZONE SetDeployZones -- @return #AI_CARGO_DISPATCHER -- @usage -- @@ -434,11 +434,11 @@ end --- Loaded Handler OnAfter for AI_CARGO_DISPATCHER -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterLoaded -- @param #AI_CARGO_DISPATCHER self --- @param #string From --- @param #string Event --- @param #string To --- @param Wrapper.Group#GROUP Carrier --- @param Cargo.Cargo#CARGO Cargo +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Wrapper.Group#GROUP Carrier Carrier object. +-- @param Cargo.Cargo#CARGO Cargo Cargo object. --- Unloaded Handler OnAfter for AI_CARGO_DISPATCHER -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterUnloaded diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 139af0e16..7a272178a 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -200,8 +200,6 @@ end -- @param From -- @param Event -- @param To --- @param Core.Point#COORDINATE Coordinate --- @param #number Speed function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To ) Helicopter:F( { Name = Helicopter:GetName() } ) @@ -245,7 +243,7 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate -- @param #number Speed -function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordinate ) +function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordinate, Speed ) local HelicopterInZone = false @@ -359,10 +357,13 @@ function AI_CARGO_HELICOPTER:onafterOrbit( Helicopter, From, Event, To, Coordina end - ---- @param #AI_CARGO_HELICOPTER self +--- On Before event Load. +-- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -function AI_CARGO_HELICOPTER:onbeforeLoad( Helicopter, From, Event, To, Coordinate ) +-- @param #string From From state. +-- @param #string Event Event +-- @param #string To To state. +function AI_CARGO_HELICOPTER:onbeforeLoad( Helicopter, From, Event, To) local Boarding = false @@ -400,10 +401,15 @@ function AI_CARGO_HELICOPTER:onbeforeLoad( Helicopter, From, Event, To, Coordina end ---- @param #AI_CARGO_HELICOPTER self +--- On after Board event. +-- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Cargo.Cargo#CARGO Cargo Cargo object. function AI_CARGO_HELICOPTER:onafterBoard( Helicopter, From, Event, To, Cargo ) - self:F( { APC, From, Event, To, Cargo } ) + self:F( { Helicopter, From, Event, To, Cargo } ) if Helicopter and Helicopter:IsAlive() then self:F({ IsLoaded = Cargo:IsLoaded() } ) @@ -416,10 +422,16 @@ function AI_CARGO_HELICOPTER:onafterBoard( Helicopter, From, Event, To, Cargo ) end ---- @param #AI_CARGO_HELICOPTER self +--- On before Land event. +-- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Cargo.Cargo#CARGO Cargo Cargo object. +-- @return #boolean Cargo is loaded. function AI_CARGO_HELICOPTER:onbeforeLoaded( Helicopter, From, Event, To, Cargo ) - self:F( { APC, From, Event, To } ) + self:F( { Helicopter, From, Event, To, Cargo } ) local Loaded = true @@ -439,8 +451,12 @@ function AI_CARGO_HELICOPTER:onbeforeLoaded( Helicopter, From, Event, To, Cargo end ---- @param #AI_CARGO_HELICOPTER self +--- On after Unload event. +-- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter +-- @param #string From From state. +-- @param #string Event Event. +-- @param #boolean Deployed Cargo is deployed. function AI_CARGO_HELICOPTER:onafterUnload( Helicopter, From, Event, To, Deployed ) if Helicopter and Helicopter:IsAlive() then @@ -457,8 +473,14 @@ function AI_CARGO_HELICOPTER:onafterUnload( Helicopter, From, Event, To, Deploye end ---- @param #AI_CARGO_HELICOPTER self +--- On after Unboard event. +-- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Cargo.Cargo#CARGO Cargo Cargo object. +-- @param #boolean Deployed Cargo is deployed. function AI_CARGO_HELICOPTER:onafterUnboard( Helicopter, From, Event, To, Cargo, Deployed ) if Helicopter and Helicopter:IsAlive() then @@ -471,8 +493,15 @@ function AI_CARGO_HELICOPTER:onafterUnboard( Helicopter, From, Event, To, Cargo, end ---- @param #AI_CARGO_HELICOPTER self +--- On before Unloaded event. +-- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Cargo.Cargo#CARGO Cargo Cargo object. +-- @param #boolean Deployed Cargo is deployed. +-- @return #boolean True if all cargo has been unloaded. function AI_CARGO_HELICOPTER:onbeforeUnloaded( Helicopter, From, Event, To, Cargo, Deployed ) self:F( { APC, From, Event, To, Cargo:GetName(), Deployed = Deployed } ) @@ -508,8 +537,14 @@ function AI_CARGO_HELICOPTER:onbeforeUnloaded( Helicopter, From, Event, To, Carg end ---- @param #AI_CARGO_HELICOPTER self +--- On after Unloaded event. +-- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Cargo.Cargo#CARGO Cargo Cargo object. +-- @param #boolean Deployed Cargo is deployed. function AI_CARGO_HELICOPTER:onafterUnloaded( Helicopter, From, Event, To, Cargo, Deployed ) self:Orbit( Helicopter:GetCoordinate(), 50 ) @@ -523,12 +558,13 @@ function AI_CARGO_HELICOPTER:onafterUnloaded( Helicopter, From, Event, To, Cargo end ---- @param #AI_CARGO_HELICOPTER self +--- On after Pickup event. +-- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -- @param From -- @param Event -- @param To --- @param Core.Point#COORDINATE Coordinate +-- @param Core.Point#COORDINATE Coordinate Pickup place. -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordinate, Speed ) @@ -537,7 +573,7 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin Helicopter:Activate() self.RoutePickup = true - Coordinate.y = math.random( 50, 200 ) + Coordinate.y = math.random( 50, 500 ) local _speed=Speed or Helicopter:GetSpeedMax()*0.5 @@ -586,12 +622,16 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin end - +--- Depoloy function and queue. +-- @param #AI_CARGO_HELICOPTER self +-- @param Wrapper.Group#GROUP AICargoHelicopter +-- @param Core.Point#COORDINATE Coordinate Coordinate function AI_CARGO_HELICOPTER:_Deploy( AICargoHelicopter, Coordinate ) AICargoHelicopter:__Queue( -10, Coordinate, 100 ) end ---- @param #AI_CARGO_HELICOPTER self +--- On after Deploy event. +-- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -- @param From -- @param Event @@ -609,7 +649,7 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin --- Calculate the target route point. - Coordinate.y = math.random( 50, 200 ) + Coordinate.y = math.random( 50, 500 ) local _speed=Speed or Helicopter:GetSpeedMax()*0.5 @@ -659,12 +699,13 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin end ---- @param #AI_CARGO_HELICOPTER self +--- On after Home event. +-- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -- @param From -- @param Event -- @param To --- @param Core.Point#COORDINATE Coordinate +-- @param Core.Point#COORDINATE Coordinate Home place. -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinate, Speed ) diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 3fa26e9ab..459da2a0f 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -55,12 +55,12 @@ do -- CARGO_GROUP -- This make a new CARGO_GROUP from a @{Wrapper.Group} object. -- It will "ungroup" the group object within the sim, and will create a @{Set} of individual Unit objects. -- @param #CARGO_GROUP self - -- @param Wrapper.Group#GROUP CargoGroup - -- @param #string Type - -- @param #string Name - -- @param #number LoadRadius (optional) - -- @param #number NearRadius (optional) - -- @return #CARGO_GROUP + -- @param Wrapper.Group#GROUP CargoGroup Group to be transported as cargo. + -- @param #string Type Cargo type, e.g. "Infantry". This is the type used in SET_CARGO:New():FilterTypes("Infantry") to define the valid cargo groups of the set. + -- @param #string Name Some user defined name of the cargo group. + -- @param #number LoadRadius (optional) Distance in meters until which a cargo is loaded into the carrier. Cargo outside this radius has to be routed by other means to within the radius to be loaded. + -- @param #number NearRadius (optional) Once the units are within this radius of the carrier, they are actually loaded, i.e. disappear from the scene. + -- @return #CARGO_GROUP Cargo group object. function CARGO_GROUP:New( CargoGroup, Type, Name, LoadRadius ) local self = BASE:Inherit( self, CARGO_REPORTABLE:New( Type, Name, 0, LoadRadius ) ) -- #CARGO_GROUP self:F( { Type, Name, LoadRadius } ) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index b50f52ddb..1333a43da 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -161,6 +161,7 @@ end --- Sends a MESSAGE to a Client Group. Note that the Group needs to be defined within the ME with the skillset "Client" or "Player". -- @param #MESSAGE self -- @param Wrapper.Client#CLIENT Client is the Group of the Client. +-- @param Core.Settings#SETTINGS Settings Settings used to display the message. -- @return #MESSAGE -- @usage -- -- Send the 2 messages created with the @{New} method to the Client Group. @@ -200,8 +201,8 @@ end --- Sends a MESSAGE to a Group. -- @param #MESSAGE self --- @param Wrapper.Group#GROUP Group is the Group. --- @return #MESSAGE +-- @param Wrapper.Group#GROUP Group to which the message is displayed. +-- @return #MESSAGE Message object. function MESSAGE:ToGroup( Group, Settings ) self:F( Group.GroupName ) @@ -261,8 +262,9 @@ end --- Sends a MESSAGE to a Coalition. -- @param #MESSAGE self --- @param CoalitionSide needs to be filled out by the defined structure of the standard scripting engine @{coalition.side}. --- @return #MESSAGE +-- @param #DCS.coalition.side CoalitionSide @{#DCS.coalition.side} to which the message is displayed. +-- @param Core.Settings#SETTINGS Settings (Optional) Settings for message display. +-- @return #MESSAGE Message object. -- @usage -- -- Send a message created with the @{New} method to the RED coalition. -- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED ) @@ -306,6 +308,7 @@ end --- Sends a MESSAGE to all players. -- @param #MESSAGE self +-- @param Core.Settings#Settings (Optional) Settings for message display -- @return #MESSAGE -- @usage -- -- Send a message created to all players. @@ -315,7 +318,7 @@ end -- or -- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ) -- MessageAll:ToAll() -function MESSAGE:ToAll() +function MESSAGE:ToAll(Settings) self:F() if self.MessageType then diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua index 0d40a523b..492d1d049 100644 --- a/Moose Development/Moose/DCS.lua +++ b/Moose Development/Moose/DCS.lua @@ -8,38 +8,89 @@ do -- world --- [DCS Enum world](https://wiki.hoggitworld.com/view/DCS_enum_world) -- @type world - -- @field #world.event event + -- @field #world.event event [https://wiki.hoggitworld.com/view/DCS_enum_world](https://wiki.hoggitworld.com/view/DCS_enum_world) + -- @field #world.BirthPlace BirthPlace The birthplace enumerator is used to define where an aircraft or helicopter has spawned in association with birth events. + -- @field #world.VolumeType VolumeType The volumeType enumerator defines the types of 3d geometery used within the [world.searchObjects](https://wiki.hoggitworld.com/view/DCS_func_searchObjects) function. + + --- The world singleton contains functions centered around two different but extremely useful functions. + -- * Events and event handlers are all governed within world. + -- * A number of functions to get information about the game world. + -- + -- See [https://wiki.hoggitworld.com/view/DCS_singleton_world](https://wiki.hoggitworld.com/view/DCS_singleton_world) + -- @field #world world + world = {} --- [https://wiki.hoggitworld.com/view/DCS_enum_world](https://wiki.hoggitworld.com/view/DCS_enum_world) -- @type world.event - -- @field S_EVENT_INVALID + -- @field S_EVENT_INVALID -- @field S_EVENT_SHOT [https://wiki.hoggitworld.com/view/DCS_event_shot](https://wiki.hoggitworld.com/view/DCS_event_shot) - -- @field S_EVENT_HIT - -- @field S_EVENT_TAKEOFF - -- @field S_EVENT_LAND - -- @field S_EVENT_CRASH - -- @field S_EVENT_EJECTION - -- @field S_EVENT_REFUELING - -- @field S_EVENT_DEAD - -- @field S_EVENT_PILOT_DEAD - -- @field S_EVENT_BASE_CAPTURED - -- @field S_EVENT_MISSION_START - -- @field S_EVENT_MISSION_END - -- @field S_EVENT_TOOK_CONTROL - -- @field S_EVENT_REFUELING_STOP - -- @field S_EVENT_BIRTH - -- @field S_EVENT_HUMAN_FAILURE - -- @field S_EVENT_ENGINE_STARTUP - -- @field S_EVENT_ENGINE_SHUTDOWN - -- @field S_EVENT_PLAYER_ENTER_UNIT - -- @field S_EVENT_PLAYER_LEAVE_UNIT - -- @field S_EVENT_PLAYER_COMMENT - -- @field S_EVENT_SHOOTING_START - -- @field S_EVENT_SHOOTING_END + -- @field S_EVENT_HIT [https://wiki.hoggitworld.com/view/DCS_event_hit](https://wiki.hoggitworld.com/view/DCS_event_hit) + -- @field S_EVENT_TAKEOFF [https://wiki.hoggitworld.com/view/DCS_event_takeoff](https://wiki.hoggitworld.com/view/DCS_event_takeoff) + -- @field S_EVENT_LAND [https://wiki.hoggitworld.com/view/DCS_event_land](https://wiki.hoggitworld.com/view/DCS_event_land) + -- @field S_EVENT_CRASH [https://wiki.hoggitworld.com/view/DCS_event_crash](https://wiki.hoggitworld.com/view/DCS_event_crash) + -- @field S_EVENT_EJECTION [https://wiki.hoggitworld.com/view/DCS_event_ejection](https://wiki.hoggitworld.com/view/DCS_event_ejection) + -- @field S_EVENT_REFUELING [https://wiki.hoggitworld.com/view/DCS_event_refueling](https://wiki.hoggitworld.com/view/DCS_event_refueling) + -- @field S_EVENT_DEAD [https://wiki.hoggitworld.com/view/DCS_event_dead](https://wiki.hoggitworld.com/view/DCS_event_dead) + -- @field S_EVENT_PILOT_DEAD [https://wiki.hoggitworld.com/view/DCS_event_pilot_dead](https://wiki.hoggitworld.com/view/DCS_event_pilot_dead) + -- @field S_EVENT_BASE_CAPTURED [https://wiki.hoggitworld.com/view/DCS_event_base_captured](https://wiki.hoggitworld.com/view/DCS_event_base_captured) + -- @field S_EVENT_MISSION_START [https://wiki.hoggitworld.com/view/DCS_event_mission_start](https://wiki.hoggitworld.com/view/DCS_event_mission_start) + -- @field S_EVENT_MISSION_END [https://wiki.hoggitworld.com/view/DCS_event_mission_end](https://wiki.hoggitworld.com/view/DCS_event_mission_end) + -- @field S_EVENT_TOOK_CONTROL + -- @field S_EVENT_REFUELING_STOP [https://wiki.hoggitworld.com/view/DCS_event_refueling_stop](https://wiki.hoggitworld.com/view/DCS_event_refueling_stop) + -- @field S_EVENT_BIRTH [https://wiki.hoggitworld.com/view/DCS_event_birth](https://wiki.hoggitworld.com/view/DCS_event_birth) + -- @field S_EVENT_HUMAN_FAILURE [https://wiki.hoggitworld.com/view/DCS_event_human_failure](https://wiki.hoggitworld.com/view/DCS_event_human_failure) + -- @field S_EVENT_ENGINE_STARTUP [https://wiki.hoggitworld.com/view/DCS_event_engine_startup](https://wiki.hoggitworld.com/view/DCS_event_engine_startup) + -- @field S_EVENT_ENGINE_SHUTDOWN [https://wiki.hoggitworld.com/view/DCS_event_engine_shutdown](https://wiki.hoggitworld.com/view/DCS_event_engine_shutdown) + -- @field S_EVENT_PLAYER_ENTER_UNIT [https://wiki.hoggitworld.com/view/DCS_event_player_enter_unit](https://wiki.hoggitworld.com/view/DCS_event_player_enter_unit) + -- @field S_EVENT_PLAYER_LEAVE_UNIT [https://wiki.hoggitworld.com/view/DCS_event_player_leave_unit](https://wiki.hoggitworld.com/view/DCS_event_player_leave_unit) + -- @field S_EVENT_PLAYER_COMMENT + -- @field S_EVENT_SHOOTING_START [https://wiki.hoggitworld.com/view/DCS_event_shooting_start](https://wiki.hoggitworld.com/view/DCS_event_shooting_start) + -- @field S_EVENT_SHOOTING_END [https://wiki.hoggitworld.com/view/DCS_event_shooting_end](https://wiki.hoggitworld.com/view/DCS_event_shooting_end) + -- @field S_EVENT_MARK ADDED [https://wiki.hoggitworld.com/view/DCS_event_mark_added](https://wiki.hoggitworld.com/view/DCS_event_mark_added) + -- @field S_EVENT_MARK CHANGE [https://wiki.hoggitworld.com/view/DCS_event_mark_change](https://wiki.hoggitworld.com/view/DCS_event_mark_change) + -- @field S_EVENT_MARK REMOVE [https://wiki.hoggitworld.com/view/DCS_event_mark_remove](https://wiki.hoggitworld.com/view/DCS_event_mark_remove) -- @field S_EVENT_MAX - world = {} --#world + --- The birthplace enumerator is used to define where an aircraft or helicopter has spawned in association with birth events. + -- @type world.BirthPlace + -- @field wsBirthPlace_Air + -- @field wsBirthPlace_RunWay + -- @field wsBirthPlace_Park + -- @field wsBirthPlace_Heliport_Hot + -- @field wsBirthPlace_Heliport_Cold + + --- The volumeType enumerator defines the types of 3d geometery used within the #world.searchObjects function. + -- @type world.VolumeType + -- @field SEGMENT + -- @field BOX + -- @field SPHERE + -- @field PYRAMID + + --- Adds a function as an event handler that executes whenever a simulator event occurs. See [hoggit](https://wiki.hoggitworld.com/view/DCS_func_addEventHandler). + -- @function [parent=#world] addEventHandler + -- @param #table handler Event handler table. + --- Removes the specified event handler from handling events. + -- @function [parent=#world] removeEventHandler + -- @param #table handler Event handler table. + + --- Returns a table of the single unit object in the game who's skill level is set as "Player". See [hoggit](https://wiki.hoggitworld.com/view/DCS_func_getPlayer). + -- There is only a single player unit in a mission and in single player the user will always spawn into this unit automatically unless other client or Combined Arms slots are available. + -- @function [parent=#world] getPlayer + -- @return DCS#Unit + + --- Searches a defined volume of 3d space for the specified objects within it and then can run function on each returned object. See [hoggit](https://wiki.hoggitworld.com/view/DCS_func_searchObjects). + -- @function [parent=#world] searchObjects + -- @param #DCS.Object.Category objectcategory Category (can be a table) of objects to search. + -- @param #DCS word.VolumeType volume Shape of the search area/volume. + -- @param #ObjectSeachHandler handler A function that handles the search. + -- @param #table any Additional data. + -- @return #DCS.unit + + --- Returns a table of mark panels indexed numerically that are present within the mission. See [hoggit](https://wiki.hoggitworld.com/view/DCS_func_getMarkPanels) + -- @function [parent=#world] getMarkPanels + -- @return #table Table of marks. + end -- world @@ -48,7 +99,6 @@ do -- env --- [DCS Singleton env](https://wiki.hoggitworld.com/view/DCS_singleton_env) -- @type env - --- Add message to simulator log with caption "INFO". Message box is optional. -- @function [parent=#env] info -- @param #string message message string to add to log. @@ -68,6 +118,7 @@ do -- env -- @function [parent=#env] setErrorMessageBoxEnabled -- @param #boolean on if true message box appearance is enabled. + --- [DCS Singleton env](https://wiki.hoggitworld.com/view/DCS_singleton_env) env = {} --#env end -- env @@ -116,6 +167,7 @@ do -- timer -- @function [parent=#timer] removeFunction -- @param functionId Function identifier to remove from schedule + --- [DCS Singleton timer](https://wiki.hoggitworld.com/view/DCS_singleton_timer) timer = {} --#timer end @@ -145,6 +197,7 @@ do -- land -- @param #Vec2 point Point on the land. -- @return #land.SurfaceType + --- [DCS Singleton land](https://wiki.hoggitworld.com/view/DCS_singleton_land) land = {} --#land end -- land diff --git a/Moose Development/Moose/Functional/Artillery.lua b/Moose Development/Moose/Functional/Artillery.lua index 880b8d645..54cd4635a 100644 --- a/Moose Development/Moose/Functional/Artillery.lua +++ b/Moose Development/Moose/Functional/Artillery.lua @@ -65,6 +65,8 @@ -- @field #number IniGroupStrength Inital number of units in the ARTY group. -- @field #boolean IsArtillery If true, ARTY group has attribute "Artillery". This is automatically derived from the DCS descriptor table. -- @field #boolean ismobile If true, ARTY group can move. +-- @field #boolean iscargo If true, ARTY group is defined as possible cargo. If it is immobile, targets out of range are not deleted from the queue. +-- @field Cargo.CargoGroup#CARGO_GROUP cargogroup Cargo group object if ARTY group is a cargo that will be transported to another place. -- @field #string groupname Name of the ARTY group as defined in the mission editor. -- @field #string alias Name of the ARTY group. -- @field #table clusters Table of names of clusters the group belongs to. Can be used to address all groups within the cluster simultaniously. @@ -417,7 +419,15 @@ -- -- Setting the rearming group is independent of the position of the mark. Just create one anywhere on the map and type -- arty set, battery "Mortar Bravo", rearming group "Ammo Truck M818" --- Note that the name of the rearming group has to be given in quotation marks and spellt exactly as the group name defined in the mission editor. +-- Note that the name of the rearming group has to be given in quotation marks and spellt exactly as the group name defined in the mission editor. +-- +-- ## Transporting +-- +-- ARTY groups can be transported to another location as @{Cargo.Cargo} by means of classes such as @{AI.AI_Cargo_APC}, @{AI.AI_Cargo_Dispatcher_APC}, +-- @{AI.AI_Cargo_Helicopter}, @{AI.AI_Cargo_Dispatcher_Helicopter} or @{AI.AI_Cargo_Airplane}. +-- +-- In order to do this, one needs to define an ARTY object via the @{#ARTY.NewFromCargoGroup}(*cargogroup*, *alias*) function. +-- The first argument *cargogroup* has to be a @{Cargo.CargoGroup#CARGO_GROUP} object. The second argument *alias* is a string which can be freely chosen by the user. -- -- ## Fine Tuning -- @@ -503,7 +513,25 @@ -- -- Start ARTY process. -- normandy:Start() -- --- +-- ### Transportation as Cargo +-- This example demonstates how an ARTY group can be transported to another location as cargo. +-- -- Define a group as CARGO_GROUP +-- CargoGroupMortars=CARGO_GROUP:New(GROUP:FindByName("Mortars"), "Mortars", "Mortar Platoon Alpha", 100 , 10) +-- +-- -- Define the mortar CARGO GROUP as ARTY object +-- mortars=ARTY:NewFromCargoGroup(CargoGroupMortars, "Mortar Platoon Alpha") +-- +-- -- Start ARTY process +-- mortars:Start() +-- +-- -- Setup AI cargo dispatcher for e.g. helos +-- SetHeloCarriers = SET_GROUP:New():FilterPrefixes("CH-47D"):FilterStart() +-- SetCargoMortars = SET_CARGO:New():FilterTypes("Mortars"):FilterStart() +-- SetZoneDepoly = SET_ZONE:New():FilterPrefixes("Deploy"):FilterStart() +-- CargoHelo=AI_CARGO_DISPATCHER_HELICOPTER:New(SetHeloCarriers, SetCargoMortars, SetZoneDepoly) +-- CargoHelo:Start() +-- The ARTY group will be transported and resume its normal operation after it has been deployed. New targets can be assigned at any time also during the transportation process. +-- -- @field #ARTY ARTY={ ClassName="ARTY", @@ -528,6 +556,8 @@ ARTY={ alias=nil, clusters={}, ismobile=true, + iscargo=false, + cargogroup=nil, IniGroupStrength=0, IsArtillery=nil, RearmingDistance=100, @@ -645,7 +675,7 @@ ARTY.id="ARTY | " --- Arty script version. -- @field #string version -ARTY.version="1.0.4" +ARTY.version="1.0.5" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -677,13 +707,43 @@ ARTY.version="1.0.4" --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Creates a new ARTY object. +--- Creates a new ARTY object from a MOOSE CARGO_GROUP object. +-- @param #ARTY self +-- @param Cargo.CargoGroup#CARGO_GROUP cargogroup The CARGO GROUP object for which artillery tasks should be assigned. +-- @param alias (Optional) Alias name the group will be calling itself when sending messages. Default is the group name. +-- @return #ARTY ARTY object or nil if group does not exist or is not a ground or naval group. +function ARTY:NewFromCargoGroup(cargogroup, alias) + BASE:F2({cargogroup=cargogroup, alias=alias}) + + if cargogroup then + BASE:T(ARTY.id..string.format("ARTY script version %s. Added CARGO group %s.", ARTY.version, cargogroup:GetName())) + else + BASE:E(ARTY.id.."ERROR: Requested ARTY CARGO GROUP does not exist! (Has to be a MOOSE CARGO(!) group.)") + return nil + end + + -- Get group belonging to the cargo group. + local group=cargogroup:GetObject() + + -- Create ARTY object. + local arty=ARTY:New(group,alias) + + -- Set iscargo flag. + arty.iscargo=true + + -- Set cargo group object. + arty.cargogroup=cargogroup + + return arty +end + +--- Creates a new ARTY object from a MOOSE group object. -- @param #ARTY self -- @param Wrapper.Group#GROUP group The GROUP object for which artillery tasks should be assigned. -- @param alias (Optional) Alias name the group will be calling itself when sending messages. Default is the group name. -- @return #ARTY ARTY object or nil if group does not exist or is not a ground or naval group. function ARTY:New(group, alias) - BASE:F2(group) + BASE:F2({group=group, alias=alias}) -- Inherits from FSM_CONTROLLABLE local self=BASE:Inherit(self, FSM_CONTROLLABLE:New()) -- #ARTY @@ -697,7 +757,7 @@ function ARTY:New(group, alias) end -- Check that we actually have a GROUND group. - if group:IsGround()==false and group:IsShip()==false then + if not (group:IsGround() or group:IsShip()) then self:E(ARTY.id..string.format("ERROR: ARTY group %s has to be a GROUND or SHIP group!", group:GetName())) return nil end @@ -785,6 +845,10 @@ function ARTY:New(group, alias) self:AddTransition("*", "NewMove", "*") self:AddTransition("*", "Dead", "*") + -- Transport as cargo (not in diagram). + self:AddTransition("*", "Loaded", "InTransit") + self:AddTransition("InTransit", "UnLoaded", "CombatReady") + -- Unknown transitons. To be checked if adding these causes problems. self:AddTransition("Rearming", "Arrived", "Rearming") self:AddTransition("Rearming", "Move", "Rearming") @@ -1667,6 +1731,7 @@ function ARTY:onafterStart(Controllable, From, Event, To) text=text..string.format("Speed max = %d km/h\n", self.SpeedMax) text=text..string.format("Speed default = %d km/h\n", self.Speed) text=text..string.format("Is mobile = %s\n", tostring(self.ismobile)) + text=text..string.format("Is cargo = %s\n", tostring(self.iscargo)) text=text..string.format("Min range = %.1f km\n", self.minrange/1000) text=text..string.format("Max range = %.1f km\n", self.maxrange/1000) text=text..string.format("Total ammo count = %d\n", self.Nammo0) @@ -2458,10 +2523,29 @@ end function ARTY:onafterStatus(Controllable, From, Event, To) self:_EventFromTo("onafterStatus", Event, From, To) + -- We have a cargo group ==> check if group was loaded into a carrier. + if self.cargogroup then + if self.cargogroup:IsLoaded() and not self:is("InTransit") then + -- Group is now InTransit state. Current target is canceled. + self:T(ARTY.id..string.format("Group %s has been loaded into a carrier and is now transported.", self.alias)) + self:Loaded() + elseif self.cargogroup:IsUnLoaded() then + -- Group has been unloaded and is combat ready again. + self:T(ARTY.id..string.format("Group %s has been unloaded from the carrier.", self.alias)) + self:UnLoaded() + end + end + -- Debug current status info. if self.Debug then self:_StatusReport() end + + -- Group is being transported as cargo ==> skip everything and check again in 5 seconds. + if self:is("InTransit") then + self:__Status(-5) + return + end -- Group on the move. if self:is("Moving") then @@ -2582,6 +2666,34 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Before "Loaded" event. Checks if group is currently firing and removes the target by calling CeaseFire. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @return #boolean If true, proceed to onafterLoaded. +function ARTY:onbeforeLoaded(Controllable, From, Event, To) + if self.currentTarget then + self:CeaseFire(self.currentTarget) + end + + return true +end + +--- After "UnLoaded" event. Group is combat ready again. +-- @param #ARTY self +-- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @return #boolean If true, proceed to onafterLoaded. +function ARTY:onafterUnLoaded(Controllable, From, Event, To) + self:CombatReady() +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + --- Enter "CombatReady" state. Route the group back if necessary. -- @param #ARTY self -- @param Wrapper.Controllable#CONTROLLABLE Controllable Controllable of the group. @@ -2840,7 +2952,7 @@ function ARTY:onafterRearm(Controllable, From, Event, To) self.RearmingGroupCoord=coordRARM end - if self.RearmingGroup and self.RearmingPlaceCoord and self.SpeedMax>0 then + if self.RearmingGroup and self.RearmingPlaceCoord and self.ismobile then -- CASE 1: Rearming unit and ARTY group meet at rearming place. @@ -4615,6 +4727,7 @@ function ARTY:_TargetInRange(target, message) end -- Distance between ARTY group and target. + self:E({controllable=self.Controllable, targetcoord=target.coord}) local _dist=self.Controllable:GetCoordinate():Get2DDistance(target.coord) -- Assume we are in range. @@ -4639,8 +4752,8 @@ function ARTY:_TargetInRange(target, message) MESSAGE:New(text, 5):ToCoalitionIf(self.Controllable:GetCoalition(), (self.report and message) or (self.Debug and message)) end - -- Remove target if ARTY group cannot move, e.g. Mortas. No chance to be ever in range. - if not self.ismobile and _inrange==false then + -- Remove target if ARTY group cannot move, e.g. Mortas. No chance to be ever in range - unless they are cargo. + if not (self.ismobile or self.iscargo) and _inrange==false then self:RemoveTarget(target.name) end diff --git a/Moose Development/Moose/Tasking/Mission.lua b/Moose Development/Moose/Tasking/Mission.lua index 877754efa..2d6ac4660 100644 --- a/Moose Development/Moose/Tasking/Mission.lua +++ b/Moose Development/Moose/Tasking/Mission.lua @@ -129,10 +129,10 @@ MISSION = { --- This is the main MISSION declaration method. Each Mission is like the master or a Mission orchestration between, Clients, Tasks, Stages etc. -- @param #MISSION self -- @param Tasking.CommandCenter#COMMANDCENTER CommandCenter --- @param #string MissionName is the name of the mission. This name will be used to reference the status of each mission by the players. --- @param #string MissionPriority is a string indicating the "priority" of the Mission. f.e. "Primary", "Secondary" or "First", "Second". It is free format and up to the Mission designer to choose. There are no rules behind this field. --- @param #string MissionBriefing is a string indicating the mission briefing to be shown when a player joins a @{CLIENT}. --- @param #string MissionCoalition is a string indicating the coalition or party to which this mission belongs to. It is free format and can be chosen freely by the mission designer. Note that this field is not to be confused with the coalition concept of the ME. Examples of a Mission Coalition could be "NATO", "CCCP", "Intruders", "Terrorists"... +-- @param #string MissionName Name of the mission. This name will be used to reference the status of each mission by the players. +-- @param #string MissionPriority String indicating the "priority" of the Mission. e.g. "Primary", "Secondary". It is free format and up to the Mission designer to choose. There are no rules behind this field. +-- @param #string MissionBriefing String indicating the mission briefing to be shown when a player joins a @{CLIENT}. +-- @param DCS#coaliton.side MissionCoalition Side of the coalition, i.e. and enumerator @{#DCS.coalition.side} corresponding to RED, BLUE or NEUTRAL. -- @return #MISSION self function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefing, MissionCoalition ) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 587e5987b..4a2596c49 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -526,8 +526,7 @@ end --- Returns the average group height in meters. -- @param Wrapper.Group#GROUP self -- @param #boolean FromGround Measure from the ground or from sea level. Provide **true** for measuring from the ground. **false** or **nil** if you measure from sea level. --- @return DCS#Vec3 The height of the group. --- @return #nil The GROUP is not existing or alive. +-- @return DCS#Vec3 The height of the group or nil if is not existing or alive. function GROUP:GetHeight( FromGround ) self:F2( self.GroupName ) From 1dc6b91d37631d3e6c2b6a6c960d530dda20deb7 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 30 Jul 2018 00:11:08 +0200 Subject: [PATCH 219/420] G2G Dispatcher Draft --- .../Moose/AI/AI_G2G_Dispatcher.lua | 131 ++++++++++++++++++ Moose Development/Moose/Core/Message.lua | 2 +- .../Moose/Functional/Detection.lua | 3 +- Moose Setup/Moose.files | 1 + 4 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 Moose Development/Moose/AI/AI_G2G_Dispatcher.lua diff --git a/Moose Development/Moose/AI/AI_G2G_Dispatcher.lua b/Moose Development/Moose/AI/AI_G2G_Dispatcher.lua new file mode 100644 index 000000000..2bf2ac3a3 --- /dev/null +++ b/Moose Development/Moose/AI/AI_G2G_Dispatcher.lua @@ -0,0 +1,131 @@ +--- **AI** - (R2.4) - Manages automatic ground troups dispatching to the battle field. +-- +-- +-- Features: +-- +-- * Some nice stuff. +-- +-- # QUICK START GUIDE +-- +-- === +-- +-- ### Authors: **funkyfranky** +-- +-- @module AI.AI_G2G_Dispatcher +-- @image AI_Air_To_Air_Dispatching.JPG + +do -- AI_G2G_DISPATCHER + + --- AI_G2G_DISPATCHER class. + -- @type AI_G2G_DISPATCHER + -- @field #string ClassName Name of the class. + -- @field Functional.Detection#DETECTION_AREAS Detection Detection object responsible for identifying enemies. + -- @extends Tasking.DetectionManager#DETECTION_MANAGER + + --- Create an automatic ground . + -- + -- === + -- + -- # Demo Missions + -- + -- ### [AI\_A2A\_DISPATCHER Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching) + -- + -- === + -- + -- # YouTube Channel + -- + -- ### [DCS WORLD - MOOSE - A2A GCICAP - Build an automatic A2A Defense System](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl0S4KMNUUJpaUs6zZHjLKNx) + -- + -- === + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia3.JPG) + -- + -- It includes automatic spawning of Combat Air Patrol aircraft (CAP) and Ground Controlled Intercept aircraft (GCI) in response to enemy air movements that are detected by a ground based radar network. + -- CAP flights will take off and proceed to designated CAP zones where they will remain on station until the ground radars direct them to intercept detected enemy aircraft or they run short of fuel and must return to base (RTB). When a CAP flight leaves their zone to perform an interception or return to base a new CAP flight will spawn to take their place. + -- If all CAP flights are engaged or RTB then additional GCI interceptors will scramble to intercept unengaged enemy aircraft under ground radar control. + -- With a little time and with a little work it provides the mission designer with a convincing and completely automatic air defence system. + -- In short it is a plug in very flexible and configurable air defence module for DCS World. + -- + -- Note that in order to create a two way A2A defense system, two AI\_A2A\_DISPATCHER defense system may need to be created, for each coalition one. + -- This is a good implementation, because maybe in the future, more coalitions may become available in DCS world. + -- + -- === + -- + -- # USAGE GUIDE + -- + -- + -- + -- @field #AI_G2G_DISPATCHER + AI_G2G_DISPATCHER = { + ClassName = "AI_G2G_DISPATCHER", + Detection = nil, + } + + + + --- AI_G2G_DISPATCHER constructor. Creates a new AI_G2G_DISPATCHER object. + -- @param #AI_G2G_DISPATCHER self + -- @param Functional.Detection#DETECTION_BASE Detection The DETECTION object that will detects targets using the the Early Warning Radar network. + -- @return #AI_G2G_DISPATCHER self + function AI_G2G_DISPATCHER:New(Detection) + + -- Inherits from DETECTION_MANAGER + local self = BASE:Inherit(self, DETECTION_MANAGER:New(nil, Detection)) -- #AI_G2G_DISPATCHER + + self.Detection = Detection -- Functional.Detection#DETECTION_AREAS + + self:AddTransition( "Started", "Assign", "Started" ) + + self:__Start(5) + + return self + end + + --- Adds an APC group to transport troops to the front line. + -- @param #AI_G2G_DISPATCHER self + -- @param Wrapper.Group#GROUP group APC group. + -- @return #AI_G2G_DISPATCHER self + function AI_G2G_DISPATCHER:SetTransportAPC(group) + + + end + + --- Adds an APC group to transport troops to the front line. + -- @param #AI_G2G_DISPATCHER self + -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem The detected item. + function AI_G2G_DISPATCHER:EvaluateDetectedItem(DetectedItem) + local _coord=DetectedItem.Coordinate + _coord:MarkToAll("detected") + end + + --- Adds an APC group to transport troops to the front line. + -- @param #AI_G2G_DISPATCHER self + -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object. + -- @return #boolean True if you want the task assigning to continue while false will cancel the loop. + function AI_G2G_DISPATCHER:ProcessDetected(Detection) + + + -- Now that all obsolete tasks are removed, loop through the detected targets. + for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do + + local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem + local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT + local DetectedCount = DetectedSet:Count() + local DetectedZone = DetectedItem.Zone + + self:F( { "Target ID", DetectedItem.ItemID } ) + DetectedSet:Flush( self ) + + local DetectedID = DetectedItem.ID + local DetectionIndex = DetectedItem.Index + local DetectedItemChanged = DetectedItem.Changed + + env.info(string.format("FF detected item id %d, index = %d, changed = %s", DetectedID, DetectedItem.Index, tostring(DetectedItem.Changed))) + + + + end + end + +end + diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 1333a43da..5ccfc3b65 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -308,7 +308,7 @@ end --- Sends a MESSAGE to all players. -- @param #MESSAGE self --- @param Core.Settings#Settings (Optional) Settings for message display +-- @param Core.Settings#Settings Settings (Optional) Settings for message display. -- @return #MESSAGE -- @usage -- -- Send a message created to all players. diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index a547eb1d2..66ca390e2 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -515,6 +515,7 @@ do -- DETECTION_BASE for DetectionGroupID, DetectionGroupData in pairs( self.DetectionSetGroup:GetSet() ) do --self:F( { DetectionGroupData } ) + self:F( {"FF", DetectionGroupData } ) self:__DetectionGroup( DetectDelay, DetectionGroupData, DetectionTimeStamp ) -- Process each detection asynchronously. self.DetectionCount = self.DetectionCount + 1 DetectDelay = DetectDelay + 1 @@ -1877,7 +1878,7 @@ do -- DETECTION_UNITS -- @param #DETECTION_UNITS self -- @return #DETECTION_UNITS self function DETECTION_UNITS:CreateDetectionItems() - + env.info("FF createdetectionitmes") -- Loop the current detected items, and check if each object still exists and is detected. for DetectedItemKey, DetectedItem in pairs( self.DetectedItems ) do diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index ea44b6326..2f53e45a8 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -76,6 +76,7 @@ AI/AI_Cargo_Dispatcher.lua AI/AI_Cargo_Dispatcher_APC.lua AI/AI_Cargo_Dispatcher_Helicopter.lua AI/AI_Cargo_Dispatcher_Airplane.lua +AI/AI_G2G_Dispatcher.lua Actions/Act_Assign.lua Actions/Act_Route.lua From 849120a8856a7a847b25e8b74e68a34b3ace5529 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 30 Jul 2018 16:04:07 +0200 Subject: [PATCH 220/420] G2G --- .../Moose/AI/AI_G2G_Dispatcher.lua | 126 ++++++++++++++++-- 1 file changed, 116 insertions(+), 10 deletions(-) diff --git a/Moose Development/Moose/AI/AI_G2G_Dispatcher.lua b/Moose Development/Moose/AI/AI_G2G_Dispatcher.lua index 2bf2ac3a3..0f0a352a2 100644 --- a/Moose Development/Moose/AI/AI_G2G_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_G2G_Dispatcher.lua @@ -40,14 +40,7 @@ do -- AI_G2G_DISPATCHER -- -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia3.JPG) -- - -- It includes automatic spawning of Combat Air Patrol aircraft (CAP) and Ground Controlled Intercept aircraft (GCI) in response to enemy air movements that are detected by a ground based radar network. - -- CAP flights will take off and proceed to designated CAP zones where they will remain on station until the ground radars direct them to intercept detected enemy aircraft or they run short of fuel and must return to base (RTB). When a CAP flight leaves their zone to perform an interception or return to base a new CAP flight will spawn to take their place. - -- If all CAP flights are engaged or RTB then additional GCI interceptors will scramble to intercept unengaged enemy aircraft under ground radar control. - -- With a little time and with a little work it provides the mission designer with a convincing and completely automatic air defence system. - -- In short it is a plug in very flexible and configurable air defence module for DCS World. - -- - -- Note that in order to create a two way A2A defense system, two AI\_A2A\_DISPATCHER defense system may need to be created, for each coalition one. - -- This is a good implementation, because maybe in the future, more coalitions may become available in DCS world. + -- Blabla. -- -- === -- @@ -59,6 +52,7 @@ do -- AI_G2G_DISPATCHER AI_G2G_DISPATCHER = { ClassName = "AI_G2G_DISPATCHER", Detection = nil, + Homebase = {}, } @@ -85,9 +79,16 @@ do -- AI_G2G_DISPATCHER -- @param #AI_G2G_DISPATCHER self -- @param Wrapper.Group#GROUP group APC group. -- @return #AI_G2G_DISPATCHER self - function AI_G2G_DISPATCHER:SetTransportAPC(group) - + function AI_G2G_DISPATCHER:AddTransportAPC(group, homebase, resources) + self.TransportAPC[group]={} + self.TransportAPC[group].group=group + self.TransportAPC[group].homebase=homebase + self.TransportAPC[group].resources=resources + -- Add homebase + if not self:GetHomebase(homebase) then + self:AddHomebase(homebase) + end end --- Adds an APC group to transport troops to the front line. @@ -96,6 +97,12 @@ do -- AI_G2G_DISPATCHER function AI_G2G_DISPATCHER:EvaluateDetectedItem(DetectedItem) local _coord=DetectedItem.Coordinate _coord:MarkToAll("detected") + + + local _id=DetectedItem.ID + + + end --- Adds an APC group to transport troops to the front line. @@ -125,7 +132,106 @@ do -- AI_G2G_DISPATCHER end + end end + + +do + + --- WAREHOUSE class. + -- @type WAREHOUSE + -- @field #string ClassName Name of the class. + -- @extends Core.Fsm#FSM + + --- Create an automatic ground . + -- + -- === + -- + -- # Demo Missions + -- + -- ### None. + -- + -- === + -- + -- # YouTube Channel + -- + -- ### None. + -- + -- === + -- + -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia3.JPG) + -- + -- Warehouse + -- + -- === + -- + -- # USAGE GUIDE + -- + -- + -- + -- @field #WAREHOUSE + WAREHOUSE = { + ClassName = "WAREHOUSE", + Homebase = nil, + plane = {}, + helicopter = {}, + artillery={}, + tank = {}, + apcs = {}, + infantry={}, + } + + WAREHOUSE.category= { + Transport=1, + Figherplane=1, + AWACS=1, + Tanker=1, + + } + + --- WAREHOUSE constructor. Creates a new WAREHOUSE object. + -- @param #WAREHOUSE self + -- @param Wrapper.Airbase#AIRBASE airbase Airbase. + -- @return #WAREHOUSE self + function WAREHOUSE:NewAirbase(airbase) + + -- Inherits from DETECTION_MANAGER + local self = BASE:Inherit( self, FSM:New() ) -- #WAREHOUSE + + self.Homebase=airbase + + return self + end + + --- Add an airplane group to the warehouse stock. + -- @param #WAREHOUSE self + -- @param #string templateprefix Name of the late activated template group as defined in the mission editor. + -- @param #number n Number of groups to add to the warehouse stock. + -- @return #WAREHOUSE self + function WAREHOUSE:AddAirplane(templateprefix, n, warehousetype) + + local group=GROUP:FindByName(templateprefix) + local typename=group:GetDesc().typeName + local displayname=group:GetDesc().displayName + + -- Create a table with properties. + self.airplane[templateprefix]=self.airplane[templateprefix] or {} + + -- Increase number in stock. + if self.airplane[templateprefix].nstock then + self.airplane[templateprefix].nstock=self.airplane[templateprefix].nstock+n + else + self.airplane[templateprefix].nstock=n + end + + self.airplane[templateprefix].nstock=n + + end + +end + + + From 0a68e7e2f151d6abfffeca2996edc494f003d7c4 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Mon, 30 Jul 2018 19:48:15 +0200 Subject: [PATCH 221/420] Pictures changed --- Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua | 2 +- Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 688bbd759..0b1700e04 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -7,7 +7,7 @@ -- === -- -- @module AI.AI_Cargo_Dispatcher --- @image AI_Cargo_Dispatching_For_Helicopters.JPG +-- @image AI_Cargo_Dispatcher.JPG --- @type AI_CARGO_DISPATCHER -- @extends Core.Fsm#FSM diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index 5da5638cc..45013e153 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -9,7 +9,7 @@ -- === -- -- @module Tasking.Task_Cargo_Dispatcher --- @image MOOSE.JPG +-- @image Task_Cargo_Dispatcher.JPG do -- TASK_CARGO_DISPATCHER From 12dc173aea664cc0c62f9b750f2aa6aff07a6d03 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Mon, 30 Jul 2018 19:56:01 +0200 Subject: [PATCH 222/420] added features --- .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index 45013e153..d732b148f 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -1,5 +1,47 @@ --- **Tasking** - Creates and manages player TASK_CARGO tasks. -- +-- **Specific features:** +-- +-- * Creates a task to transport @{Cargo.Cargo} to and between deployment zones. +-- * Derived from the TASK_CARGO class, which is derived from the TASK class. +-- * Orchestrate the task flow, so go from Planned to Assigned to Success, Failed or Cancelled. +-- * Co-operation tasking, so a player joins a group of players executing the same task. +-- +-- +-- **A complete task menu system to allow players to:** +-- +-- * Join the task, abort the task. +-- * Mark the task location on the map. +-- * Provide details of the target. +-- * Route to the cargo. +-- * Route to the deploy zones. +-- * Load/Unload cargo. +-- * Board/Unboard cargo. +-- * Slingload cargo. +-- * Display the task briefing. +-- +-- +-- **A complete mission menu system to allow players to:** +-- +-- * Join a task, abort the task. +-- * Display task reports. +-- * Display mission statistics. +-- * Mark the task locations on the map. +-- * Provide details of the targets. +-- * Display the mission briefing. +-- * Provide status updates as retrieved from the command center. +-- * Automatically assign a random task as part of a mission. +-- * Manually assign a specific task as part of a mission. +-- +-- +-- **A settings system, using the settings menu:** +-- +-- * Tweak the duration of the display of messages. +-- * Switch between metric and imperial measurement system. +-- * Switch between coordinate formats used in messages: BR, BRA, LL DMS, LL DDM, MGRS. +-- * Different settings modes for A2G and A2A operations. +-- * Various other options. +-- -- === -- -- ### Author: **FlightControl** From 1146684144016881622e19e5472f8ff4ac84dbfc Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 30 Jul 2018 23:41:02 +0200 Subject: [PATCH 223/420] G2G improvments --- .../Moose/AI/AI_G2G_Dispatcher.lua | 157 ++++++++++++++++-- 1 file changed, 145 insertions(+), 12 deletions(-) diff --git a/Moose Development/Moose/AI/AI_G2G_Dispatcher.lua b/Moose Development/Moose/AI/AI_G2G_Dispatcher.lua index 0f0a352a2..f4468f9f9 100644 --- a/Moose Development/Moose/AI/AI_G2G_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_G2G_Dispatcher.lua @@ -175,13 +175,14 @@ do -- @field #WAREHOUSE WAREHOUSE = { ClassName = "WAREHOUSE", - Homebase = nil, - plane = {}, - helicopter = {}, - artillery={}, - tank = {}, - apcs = {}, - infantry={}, + coalition = nil, + homebase = nil, + plane = {}, + helo = {}, + arty = {}, + tank = {}, + apc = {}, + infantry = {}, } WAREHOUSE.category= { @@ -198,25 +199,152 @@ do -- @return #WAREHOUSE self function WAREHOUSE:NewAirbase(airbase) - -- Inherits from DETECTION_MANAGER + -- Inherits from FSM local self = BASE:Inherit( self, FSM:New() ) -- #WAREHOUSE - self.Homebase=airbase + self.homebase=airbase + self.coordinate=airbase:GetCoordinate() + self.coalition=airbase:GetCoalition() + + + self:AddTransition("*", "Start", "Idle") + self:AddTransition("*", "Status", "*") + self:AddTransition("*", "Request", "*") + + --- Triggers the FSM event "Start". + -- @function [parent=#WAREHOUSE] Start + -- @param #WAREHOUSE self + + --- Triggers the FSM event "Start" after a delay. + -- @function [parent=#WAREHOUSE] __Start + -- @param #WAREHOUSE self + -- @param #number delay Delay in seconds. + + + --- Triggers the FSM event "Status". + -- @function [parent=#WAREHOUSE] Status + -- @param #WAREHOUSE self + + --- Triggers the FSM event "Status" after a delay. + -- @function [parent=#WAREHOUSE] __Status + -- @param #WAREHOUSE self + -- @param #number delay Delay in seconds. + + + --- Triggers the FSM event "Request". + -- @function [parent=#WAREHOUSE] Request + -- @param #WAREHOUSE self + -- @param Wrapper.Airbase#AIRBASE Airbase Airbase requesting supply. + -- @param #string Asset Asset that is requested. + -- @param #string TransportType Type of transport: "Plane", "Helicopter", "APC" + + --- Triggers the FSM event "Request" after a delay. + -- @function [parent=#WAREHOUSE] __Request + -- @param #WAREHOUSE self + -- @param #number delay Delay in seconds. + -- @param Wrapper.Airbase#AIRBASE Airbase Airbase requesting supply. + -- @param #string Asset Asset that is requested. + -- @param #string TransportType Type of transport: "Plane", "Helicopter", "APC" return self end + + --- Warehouse + -- @param #WAREHOUSE self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + function WAREHOUSE:onafterStart(From, Event, To) + env.info("FF starting warehouse of airbase of "..self.homebase:GetName()) + + -- handle events + -- event takeoff + -- event landing + -- event crash/dead + -- event base captured + self:__Status(-5) + end + + + --- Warehouse + -- @param #WAREHOUSE self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + function WAREHOUSE:onafterStatus(From, Event, To) + env.info("FF checking warehouse status of "..self.homebase:GetName()) + + env.info(string.format("FF warehouse at %s: number of transport planes = %d", self.homebase:GetName(), #self.plane)) + + self:__Status(-30) + end + + --- Warehouse + -- @param #WAREHOUSE self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Airbase#AIRBASE Airbase Airbase requesting supply. + -- @param #string Asset Asset that is requested. + -- @param #number nAssed Number of groups of that asset requested. + -- @param #string TransportType Type of transport: "Plane", "Helicopter", "APC" + function WAREHOUSE:onafterRequest(From, Event, To, Airbase, Asset, nAsset, TransportType) + env.info(string.format("FF airbase %s is requesting asset %s from warehouse %s", Airbase:GetName(), Asset, self.homebase:GetName())) + + local nAsset=nAsset or 1 + + if TransportType=="Air" then + + local template=self.plane[math.random(#self.plane)] + + if template then + + local Plane=SPAWN:New(template):SpawnAtAirbase(Airbase, nil, nil, nil, false) + + local CargoGroups = SET_CARGO:New() + + local spawn=SPAWN:New("Infantry Platoon Alpha") + + for i=1,nAsset do + local spawngroup=spawn:SpawnFromVec3(self.homebase:GetZone():GetRandomPointVec3(100,500)) + local cargogroup = CARGO_GROUP:New(spawngroup, "Infantry", string.format( "Infantry Platoon %d", i), 5000, 35) + CargoGroups:AddCargo(cargogroup) + end + + local CargoPlane = AI_CARGO_AIRPLANE:New(Plane, CargoGroups) + + CargoPlane:__Pickup(5, self.homebase) + + function CargoPlane:onafterLoaded( Airplane, From, Event, To, Cargo) + CargoPlane:__Deploy(10, Airbase, 500) + end + + + end + end + + end --- Add an airplane group to the warehouse stock. -- @param #WAREHOUSE self -- @param #string templateprefix Name of the late activated template group as defined in the mission editor. -- @param #number n Number of groups to add to the warehouse stock. -- @return #WAREHOUSE self - function WAREHOUSE:AddAirplane(templateprefix, n, warehousetype) + function WAREHOUSE:AddTransportPlane(templateprefix, n) + + local n=n or 1 local group=GROUP:FindByName(templateprefix) - local typename=group:GetDesc().typeName - local displayname=group:GetDesc().displayName + local DCSgroup=group:GetDCSObject() + local DCSunit=DCSgroup:getUnit(1) + local DCSdesc=DCSunit:getDesc() + local DCSdisplay=DCSunit:getDesc().displayName + local DCScategory=DCSgroup:getCategory() + local DCStype=DCSunit:getTypeName() + --env.info(string.format("FF adding %d transport plane template %s type %s, display %s", n, tostring(templateprefix), tostring(typename), tostring(displayname))) + + --[[ -- Create a table with properties. self.airplane[templateprefix]=self.airplane[templateprefix] or {} @@ -228,6 +356,11 @@ do end self.airplane[templateprefix].nstock=n + ]] + + for i=1,n do + table.insert(self.plane, templateprefix) + end end From 9a6be14fab380291c35152b7d9e1697d7783b7e4 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 31 Jul 2018 07:26:33 +0200 Subject: [PATCH 224/420] Revised the documentation. --- .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 269 ++++++++++-------- 1 file changed, 145 insertions(+), 124 deletions(-) diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index d732b148f..f5072264b 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -1,5 +1,16 @@ --- **Tasking** - Creates and manages player TASK_CARGO tasks. -- +-- The **TASK_CARGO_DISPATCHER** allows you to setup various tasks for let human +-- players transport cargo as part of a task. +-- +-- The cargo dispatcher will implement for you mechanisms to create cargo transportation tasks: +-- +-- * As setup by the mission designer. +-- * Dynamically create CSAR missions (when a pilot is downed as part of a downed plane). +-- * Dynamically spawn new cargo and create cargo taskings! +-- +-- +-- -- **Specific features:** -- -- * Creates a task to transport @{Cargo.Cargo} to and between deployment zones. @@ -67,148 +78,158 @@ do -- TASK_CARGO_DISPATCHER --- Implements the dynamic dispatching of cargo tasks. -- - -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia3.JPG) + -- The **TASK_CARGO_DISPATCHER** allows you to setup various tasks for let human + -- players transport cargo as part of a task. -- - -- The EWR will detect units, will group them, and will dispatch @{Task}s to groups. Depending on the type of target detected, different tasks will be dispatched. - -- Find a summary below describing for which situation a task type is created: + -- There are currently two types of tasks that can be constructed: -- - -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia9.JPG) + -- * A normal cargo transport task, which tasks humans to transport cargo from a location towards a deploy zone. + -- * A CSAR cargo transport task. CSAR tasks are automatically generated when a pilot is downed... + -- It is created when a fiendly pilot has ejected from a plane, and needs to be rescued (sometimes behind enemy lines). -- - -- * **CSAR Task**: Is created when a fiendly pilot has ejected from a plane, and needs to be rescued (sometimes behind enemy lines). + -- Let's explore step by step how to setup the task dispatcher. -- - -- ## 1. TASK\_A2A\_DISPATCHER constructor: + -- # 1. Setup a mission environment. -- - -- The @{#TASK_CARGO_DISPATCHER.New}() method creates a new TASK\_A2A\_DISPATCHER instance. + -- It is easy, as it works just like any other task setup, so setup a command center and a mission. -- - -- ### 1.1. Define or set the **Mission**: + -- ## 1.1. Create a command center. -- - -- Tasking is executed to accomplish missions. Therefore, a MISSION object needs to be given as the first parameter. + -- First you need to create a command center using the @{Tasking.CommandCenter#COMMANDCENTER.New}() constructor. -- - -- local HQ = GROUP:FindByName( "HQ", "Bravo" ) - -- local CommandCenter = COMMANDCENTER:New( HQ, "Lima" ) - -- local Mission = MISSION:New( CommandCenter, "A2A Mission", "High", "Watch the air enemy units being detected.", coalition.side.RED ) - -- - -- Missions are governed by COMMANDCENTERS, so, ensure you have a COMMANDCENTER object installed and setup within your mission. - -- Create the MISSION object, and hook it under the command center. - -- - -- ### 1.2. Build a set of the groups seated by human players: - -- - -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia6.JPG) - -- - -- A set or collection of the groups wherein human players can be seated, these can be clients or units that can be joined as a slot or jumping into. + -- local CommandCenter = COMMANDCENTER + -- :New( HQ, "Lima" ) -- Create the CommandCenter. -- - -- local AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Defender" ):FilterStart() - -- - -- The set is built using the SET_GROUP class. Apply any filter criteria to identify the correct groups for your mission. - -- Only these slots or units will be able to execute the mission and will receive tasks for this mission, once available. + -- ## 1.2. Create a mission. -- - -- ### 1.3. Define the **EWR network**: + -- Tasks work in a mission, which groups these tasks to achieve a joint mission goal. + -- A command center can govern multiple missions. + -- Create a new mission, using the @{Tasking.Mission#MISSION.New}() constructor. -- - -- As part of the TASK\_A2A\_DISPATCHER constructor, an EWR network must be given as the third parameter. - -- An EWR network, or, Early Warning Radar network, is used to early detect potential airborne targets and to understand the position of patrolling targets of the enemy. + -- -- Declare the Mission for the Command Center. + -- local Mission = MISSION + -- :New( CommandCenter, + -- "Overlord", + -- "High", + -- "Transport the cargo.", + -- coalition.side.RED + -- ) -- - -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia5.JPG) -- - -- Typically EWR networks are setup using 55G6 EWR, 1L13 EWR, Hawk sr and Patriot str ground based radar units. - -- These radars have different ranges and 55G6 EWR and 1L13 EWR radars are Eastern Bloc units (eg Russia, Ukraine, Georgia) while the Hawk and Patriot radars are Western (eg US). - -- Additionally, ANY other radar capable unit can be part of the EWR network! Also AWACS airborne units, planes, helicopters can help to detect targets, as long as they have radar. - -- The position of these units is very important as they need to provide enough coverage - -- to pick up enemy aircraft as they approach so that CAP and GCI flights can be tasked to intercept them. + -- # 2. Dispatch a **transport cargo** task. -- - -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia7.JPG) + -- So, now that we have a command center and a mission, we now create the transport task. + -- We create the transport task using the @{#TASK_CARGO_DISPATCHER.AddTransportTask}() constructor. + -- + -- ## 2.1. Create the cargo in the mission. + -- + -- Because a transport task will not generate the cargo itself, you'll need to create it first. + -- + -- -- Here we define the "cargo set", which is a collection of cargo objects. + -- -- The cargo set will be the input for the cargo transportation task. + -- -- So a transportation object is handling a cargo set, which is automatically refreshed when new cargo is added/deleted. + -- local WorkmaterialsCargoSet = SET_CARGO:New():FilterTypes( "Workmaterials" ):FilterStart() + -- + -- -- Now we add cargo into the battle scene. + -- local PilotGroup = GROUP:FindByName( "Engineers" ) + -- + -- -- CARGO_GROUP can be used to setup cargo with a GROUP object underneath. + -- -- We name this group Engineers. + -- -- Note that the name of the cargo is "Engineers". + -- -- The cargoset "CargoSet" will embed all defined cargo of type "Pilots" (prefix) into its set. + -- local CargoGroup = CARGO_GROUP:New( PilotGroup, "Workmaterials", "Engineer Team 1", 500 ) + -- + -- What is also needed, is to have a set of @{Core.Group}s defined that contains the clients of the players. + -- + -- -- Allocate the Transport, which are the helicopter to retrieve the pilot, that can be manned by players. + -- local PilotGroupSet = SET_GROUP:New():FilterPrefixes( "Transport" ):FilterStart() + -- + -- ## 2.2. Setup the cargo transport task. + -- + -- First, we need to create a TASK_CARGO_DISPATCHER object. + -- + -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, PilotGroupSet ) + -- + -- So, the variable `TaskDispatcher` will contain the object of class TASK_CARGO_DISPATCHER, which will allow you to dispatch cargo transport tasks: -- - -- Additionally in a hot war situation where the border is no longer respected the placement of radars has a big effect on how fast the war escalates. - -- For example if they are a long way forward and can detect enemy planes on the ground and taking off - -- they will start to vector CAP and GCI flights to attack them straight away which will immediately draw a response from the other coalition. - -- Having the radars further back will mean a slower escalation because fewer targets will be detected and - -- therefore less CAP and GCI flights will spawn and this will tend to make just the border area active rather than a melee over the whole map. - -- It all depends on what the desired effect is. + -- * for mission `Mission` + -- * for the pilots `PilotGroupSet`. -- - -- EWR networks are **dynamically constructed**, that is, they form part of the @{Functional.Detection#DETECTION_BASE} object that is given as the input parameter of the TASK\_A2A\_DISPATCHER class. - -- By defining in a **smart way the names or name prefixes of the groups** with EWR capable units, these groups will be **automatically added or deleted** from the EWR network, - -- increasing or decreasing the radar coverage of the Early Warning System. + -- Now that we have `TaskDispatcher` object, we can now create the TransportTask manually, using the @{#TASK_CARGO_DISPATCHER.AddTransportTask}() method! -- - -- See the following example to setup an EWR network containing EWR stations and AWACS. + -- local TransportTask = TaskDispatcher:AddTransportTask( + -- "Transport workmaterials", + -- WorkmaterialsCargoSet, + -- "Transport the workers, engineers and the equipment near the Workplace." ) -- - -- local EWRSet = SET_GROUP:New():FilterPrefixes( "EWR" ):FilterCoalitions("red"):FilterStart() - -- - -- local EWRDetection = DETECTION_AREAS:New( EWRSet, 6000 ) - -- EWRDetection:SetFriendliesRange( 10000 ) - -- EWRDetection:SetRefreshTimeInterval(30) - -- - -- -- Setup the A2A dispatcher, and initialize it. - -- A2ADispatcher = TASK_CARGO_DISPATCHER:New( Mission, AttackGroups, EWRDetection ) + -- And you're done! As you can see, it is a bit of work, but the reward is great. + -- And, because all this is done using program interfaces, you can build a mission with a **dynamic cargo transport task mechanism** yourself! + -- Based on events happening within your mission, you can use the above methods to create new cargo, and setup a new task for cargo transportation to a group of players! -- - -- The above example creates a SET_GROUP instance, and stores this in the variable (object) **EWRSet**. - -- **EWRSet** is then being configured to filter all active groups with a group name starting with **EWR** to be included in the Set. - -- **EWRSet** is then being ordered to start the dynamic filtering. Note that any destroy or new spawn of a group with the above names will be removed or added to the Set. - -- Then a new **EWRDetection** object is created from the class DETECTION_AREAS. A grouping radius of 6000 is choosen, which is 6km. - -- The **EWRDetection** object is then passed to the @{#TASK_CARGO_DISPATCHER.New}() method to indicate the EWR network configuration and setup the A2A tasking and detection mechanism. - -- - -- ### 2. Define the detected **target grouping radius**: - -- - -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia8.JPG) - -- - -- The target grouping radius is a property of the Detection object, that was passed to the AI\_A2A\_DISPATCHER object, but can be changed. - -- The grouping radius should not be too small, but also depends on the types of planes and the era of the simulation. - -- Fast planes like in the 80s, need a larger radius than WWII planes. - -- Typically I suggest to use 30000 for new generation planes and 10000 for older era aircraft. - -- - -- Note that detected targets are constantly re-grouped, that is, when certain detected aircraft are moving further than the group radius, then these aircraft will become a separate - -- group being detected. This may result in additional GCI being started by the dispatcher! So don't make this value too small! - -- - -- ## 3. Set the **Engage radius**: - -- - -- Define the radius to engage any target by airborne friendlies, which are executing cap or returning from an intercept mission. - -- - -- ![Banner Image](..\Presentations\TASK_CARGO_DISPATCHER\Dia11.JPG) - -- - -- So, if there is a target area detected and reported, - -- then any friendlies that are airborne near this target area, - -- will be commanded to (re-)engage that target when available (if no other tasks were commanded). - -- For example, if 100000 is given as a value, then any friendly that is airborne within 100km from the detected target, - -- will be considered to receive the command to engage that target area. - -- You need to evaluate the value of this parameter carefully. - -- If too small, more intercept missions may be triggered upon detected target areas. - -- If too large, any airborne cap may not be able to reach the detected target area in time, because it is too far. - -- - -- ## 4. Set **Scoring** and **Messages**: - -- - -- The TASK\_A2A\_DISPATCHER is a state machine. It triggers the event Assign when a new player joins a @{Task} dispatched by the TASK\_A2A\_DISPATCHER. - -- An _event handler_ can be defined to catch the **Assign** event, and add **additional processing** to set _scoring_ and to _define messages_, - -- when the player reaches certain achievements in the task. - -- - -- The prototype to handle the **Assign** event needs to be developed as follows: - -- - -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( ... ) - -- - -- --- @param #TaskDispatcher self - -- -- @param #string From Contains the name of the state from where the Event was triggered. - -- -- @param #string Event Contains the name of the event that was triggered. In this case Assign. - -- -- @param #string To Contains the name of the state that will be transitioned to. - -- -- @param Tasking.Task_A2A#TASK_A2A Task The Task object, which is any derived object from TASK_A2A. - -- -- @param Wrapper.Unit#UNIT TaskUnit The Unit or Client that contains the Player. - -- -- @param #string PlayerName The name of the Player that joined the TaskUnit. - -- function TaskDispatcher:OnAfterAssign( From, Event, To, Task, TaskUnit, PlayerName ) - -- Task:SetScoreOnProgress( PlayerName, 20, TaskUnit ) - -- Task:SetScoreOnSuccess( PlayerName, 200, TaskUnit ) - -- Task:SetScoreOnFail( PlayerName, -100, TaskUnit ) - -- end - -- - -- The **OnAfterAssign** method (function) is added to the TaskDispatcher object. - -- This method will be called when a new player joins a unit in the set of groups in scope of the dispatcher. - -- So, this method will be called only **ONCE** when a player joins a unit in scope of the task. - -- - -- The TASK class implements various methods to additional **set scoring** for player achievements: - -- - -- * @{Tasking.Task#TASK.SetScoreOnProgress}() will add additional scores when a player achieves **Progress** while executing the task. - -- Examples of **task progress** can be destroying units, arriving at zones etc. - -- - -- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional scores when the task goes into **Success** state. - -- This means the **task has been successfully completed**. -- - -- * @{Tasking.Task#TASK.SetScoreOnSuccess}() will add additional (negative) scores when the task goes into **Failed** state. - -- This means the **task has not been successfully completed**, and the scores must be given with a negative value! + -- # 3. Dispatch CSAR tasks. + -- + -- CSAR tasks can be dynamically created when a friendly pilot ejects, or can be created manually. + -- We'll explore both options. + -- + -- ## 3.1. CSAR task dynamic creation. + -- + -- Because there is an "event" in a running simulation that creates CSAR tasks, the method @{#TASK_CARGO_DISPATCHER.StartCSARTasks}() will create automatically: + -- + -- 1. a new downed pilot at the location where the plane was shot + -- 2. declare that pilot as cargo + -- 3. creates a CSAR task automatically to retrieve that pilot + -- 4. requires deploy zones to be specified where to transport the downed pilot to, in order to complete that task. + -- + -- You create a CSAR task dynamically in a very easy way: + -- + -- TaskDispatcher:StartCSARTasks( + -- "CSAR", + -- { ZONE_UNIT:New( "Hospital", STATIC:FindByName( "Hospital" ), 100 ) }, + -- "One of our pilots has ejected. Go out to Search and Rescue our pilot!\n" .. + -- "Use the radio menu to let the command center assist you with the CSAR tasking." + -- ) + -- + -- The method @{#TASK_CARGO_DISPATCHER.StopCSARTasks}() will automatically stop with the creation of CSAR tasks when friendly pilots eject. + -- + -- **Remarks:** + -- + -- * the ZONE_UNIT can also be a ZONE, or a ZONE_POLYGON object, or any other ZONE_ object! + -- * you can declare the array of zones in another variable, or course! + -- + -- + -- ## 3.2. CSAR task manual creation. + -- + -- We create the CSAR task using the @{#TASK_CARGO_DISPATCHER.AddCSARTask}() constructor. + -- + -- The method will create a new CSAR task, and will generate the pilots cargo itself, at the specified coordinate. + -- + -- What is first needed, is to have a set of @{Core.Group}s defined that contains the clients of the players. + -- + -- -- Allocate the Transport, which are the helicopter to retrieve the pilot, that can be manned by players. + -- local GroupSet = SET_GROUP:New():FilterPrefixes( "Transport" ):FilterStart() + -- + -- We need to create a TASK_CARGO_DISPATCHER object. + -- + -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, GroupSet ) + -- + -- So, the variable `TaskDispatcher` will contain the object of class TASK_CARGO_DISPATCHER, which will allow you to dispatch cargo CSAR tasks: + -- + -- * for mission `Mission`. + -- * for the group of players (pilots) captured within the `GroupSet` (those groups with a name starting with `"Transport"`). + -- + -- Now that we have a PilotsCargoSet and a GroupSet, we can now create the CSAR task manually. + -- + -- -- Declare the CSAR task. + -- local CSARTask = TaskDispatcher:AddCSARTask( + -- "CSAR Task", + -- Coordinate, + -- 270, + -- "Bring the pilot back!" + -- ) + -- + -- Note that when you declare a CSAR task manually, you'll still need to specify a deployment zone! + -- -- -- @field #TASK_CARGO_DISPATCHER TASK_CARGO_DISPATCHER = { @@ -363,11 +384,11 @@ do -- TASK_CARGO_DISPATCHER -- -- -- Add a CSAR task to rescue a downed pilot from within a coordinate. -- local Coordinate = PlaneUnit:GetPointVec2() - -- TaskA2ADispatcher:AddCSARTask( Coordinate ) + -- TaskA2ADispatcher:AddCSARTask( "CSAR Task", Coordinate ) -- -- -- Add a CSAR task to rescue a downed pilot from within a coordinate of country RUSSIA, which is pointing to the west (270°). -- local Coordinate = PlaneUnit:GetPointVec2() - -- TaskA2ADispatcher:AddCSARTask( Coordinate, 270, Country.RUSSIA ) + -- TaskA2ADispatcher:AddCSARTask( "CSAR Task", Coordinate, 270, Country.RUSSIA ) -- function TASK_CARGO_DISPATCHER:AddCSARTask( CSARTaskPrefix, CSARCoordinate, CSARHeading, CSARCountry, CSARBriefing ) From 5a034ecf4f5680290953ee70530255c910a12d78 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Tue, 31 Jul 2018 15:43:31 +0200 Subject: [PATCH 225/420] G2G Warehouse --- .../Moose/AI/AI_Cargo_Airplane.lua | 113 +++++++++----- .../Moose/AI/AI_G2G_Dispatcher.lua | 139 ++++++++++++++---- Moose Development/Moose/Core/Base.lua | 9 +- 3 files changed, 189 insertions(+), 72 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index bbbe19ed0..b1422cb11 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -23,8 +23,8 @@ AI_CARGO_AIRPLANE = { --- Creates a new AI_CARGO_AIRPLANE object. -- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane --- @param Core.Set#SET_CARGO CargoSet +-- @param Wrapper.Group#GROUP Airplane Plane used for transportation of cargo. +-- @param Core.Set#SET_CARGO CargoSet Cargo set to be transported. -- @return #AI_CARGO_AIRPLANE function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) @@ -54,7 +54,7 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) -- @param #string From -- @param #string Event -- @param #string To - -- @param Wrapper.Airbase#AIRBASE Airbase + -- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troops are picked up. -- @return #boolean --- Pickup Handler OnAfter for AI_CARGO_AIRPLANE @@ -63,18 +63,18 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) -- @param #string From -- @param #string Event -- @param #string To - -- @param Wrapper.Airbase#AIRBASE Airbase + -- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troops are picked up. --- Pickup Trigger for AI_CARGO_AIRPLANE -- @function [parent=#AI_CARGO_AIRPLANE] Pickup -- @param #AI_CARGO_AIRPLANE self - -- @param Wrapper.Airbase#AIRBASE Airbase + -- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troops are picked up. --- Pickup Asynchronous Trigger for AI_CARGO_AIRPLANE -- @function [parent=#AI_CARGO_AIRPLANE] __Pickup -- @param #AI_CARGO_AIRPLANE self - -- @param #number Delay - -- @param Wrapper.Airbase#AIRBASE Airbase + -- @param #number Delay Delay in seconds. + -- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troops are picked up. --- Deploy Handler OnBefore for AI_CARGO_AIRPLANE -- @function [parent=#AI_CARGO_AIRPLANE] OnBeforeDeploy @@ -82,7 +82,8 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) -- @param #string From -- @param #string Event -- @param #string To - -- @param Wrapper.Airbase#AIRBASE Airbase + -- @param Wrapper.Airbase#AIRBASE Airbase Destination airbase where troops are deployed. + -- @param #number Speed Speed in km/h for travelling to deploy base. -- @return #boolean --- Deploy Handler OnAfter for AI_CARGO_AIRPLANE @@ -91,20 +92,22 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) -- @param #string From -- @param #string Event -- @param #string To - -- @param Wrapper.Airbase#AIRBASE Airbase + -- @param Wrapper.Airbase#AIRBASE Airbase Destination airbase where troops are deployed. + -- @param #number Speed Speed in km/h for travelling to deploy base. --- Deploy Trigger for AI_CARGO_AIRPLANE -- @function [parent=#AI_CARGO_AIRPLANE] Deploy -- @param #AI_CARGO_AIRPLANE self - -- @param Wrapper.Airbase#AIRBASE Airbase + -- @param Wrapper.Airbase#AIRBASE Airbase Destination airbase where troops are deployed. + -- @param #number Speed Speed in km/h for travelling to deploy base. --- Deploy Asynchronous Trigger for AI_CARGO_AIRPLANE -- @function [parent=#AI_CARGO_AIRPLANE] __Deploy -- @param #AI_CARGO_AIRPLANE self - -- @param Wrapper.Airbase#AIRBASE Airbase - -- @param #number Delay - - + -- @param #number Delay Delay in seconds. + -- @param Wrapper.Airbase#AIRBASE Airbase Destination airbase where troops are deployed. + -- @param #number Speed Speed in km/h for travelling to deploy base. + self:SetCarrier( Airplane ) return self @@ -190,13 +193,12 @@ function AI_CARGO_AIRPLANE:FindCarrier( Coordinate, Radius ) end ---- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane +--- On after "Landed" event. Called on engine shutdown. +-- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Group#GROUP Airplane Transport plane. -- @param From -- @param Event -- @param To - -- @param Wrapper.Airbase#AIRBASE Airbase --- @param #number Speed function AI_CARGO_AIRPLANE:onafterLanded( Airplane, From, Event, To ) if Airplane and Airplane:IsAlive() then @@ -216,8 +218,8 @@ function AI_CARGO_AIRPLANE:onafterLanded( Airplane, From, Event, To ) end - ---- @param #AI_CARGO_AIRPLANE self +--- On after "Pickup" event. Routes transport to pickup airbase. +-- @param #AI_CARGO_AIRPLANE self -- @param Wrapper.Group#GROUP Airplane -- @param From -- @param Event @@ -227,24 +229,34 @@ end function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Airbase, Speed ) if Airplane and Airplane:IsAlive() then - self:Route( Airplane, Airbase, Speed ) + + -- Aircraft might be on the ground of the pickup airbase already. + if Airplane:InAir() then + self:Route( Airplane, Airbase, Speed ) + end + -- TODO: Improve :Route() so that the aircraft can be routed from another airbase to the pickup airbase. + self.RoutePickup = true + + -- Set airbase as starting point in the next Route() call. self.Airbase = Airbase end end - ---- @param #AI_CARGO_AIRPLANE self +--- On after Depoly event. Routes plane to deploy airbase. +-- @param #AI_CARGO_AIRPLANE self -- @param Wrapper.Group#GROUP Airplane -- @param From -- @param Event -- @param To --- @param Wrapper.Airbase#AIRBASE Airbase --- @param #number Speed +-- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troups should be deployed. +-- @param #number Speed Speed in km/h for travelling to deploy base. function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Airbase, Speed ) if Airplane and Airplane:IsAlive() then + + -- Route to self:Route( Airplane, Airbase, Speed ) self.RouteDeploy = true self.Airbase = Airbase @@ -253,26 +265,37 @@ function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Airbase, Sp end ---- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane +--- On after Load event. +-- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Group#GROUP Airplane Transport plane. +-- @param From +-- @param Event +-- @param To +-- @param Wrapper.Point#COORDINATE Coordinate function AI_CARGO_AIRPLANE:onafterLoad( Airplane, From, Event, To, Coordinate ) if Airplane and Airplane:IsAlive() then for _, Cargo in pairs( self.CargoSet:GetSet() ) do + local Cargo=Cargo --Cargo.Cargo#CARGO if Cargo:IsInLoadRadius( Coordinate ) then self:__Board( 5 ) Cargo:Board( Airplane, 25 ) self.Cargo = Cargo break end + end end end ---- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane +--- On after Board event. +-- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Group#GROUP Airplane Cargo plane. +-- @param From +-- @param Event +-- @param To function AI_CARGO_AIRPLANE:onafterBoard( Airplane, From, Event, To ) if Airplane and Airplane:IsAlive() then @@ -286,8 +309,12 @@ function AI_CARGO_AIRPLANE:onafterBoard( Airplane, From, Event, To ) end ---- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane +--- On after Loaded event. +-- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Group#GROUP Airplane Cargo plane. +-- @param From +-- @param Event +-- @param To function AI_CARGO_AIRPLANE:onafterLoaded( Airplane, From, Event, To ) if Airplane and Airplane:IsAlive() then @@ -296,8 +323,12 @@ function AI_CARGO_AIRPLANE:onafterLoaded( Airplane, From, Event, To ) end ---- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane +--- On after Unload event. +-- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Group#GROUP Airplane Cargo plane. +-- @param From +-- @param Event +-- @param To function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To ) if Airplane and Airplane:IsAlive() then @@ -307,8 +338,12 @@ function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To ) end ---- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane +--- On after Unboard event. +-- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Group#GROUP Airplane Cargo plane. +-- @param From +-- @param Event +-- @param To function AI_CARGO_AIRPLANE:onafterUnboard( Airplane, From, Event, To ) if Airplane and Airplane:IsAlive() then @@ -321,8 +356,12 @@ function AI_CARGO_AIRPLANE:onafterUnboard( Airplane, From, Event, To ) end ---- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane +--- On after Unloaded event. +-- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Group#GROUP Airplane Cargo plane. +-- @param From +-- @param Event +-- @param To function AI_CARGO_AIRPLANE:onafterUnloaded( Airplane, From, Event, To ) if Airplane and Airplane:IsAlive() then diff --git a/Moose Development/Moose/AI/AI_G2G_Dispatcher.lua b/Moose Development/Moose/AI/AI_G2G_Dispatcher.lua index f4468f9f9..36f833a80 100644 --- a/Moose Development/Moose/AI/AI_G2G_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_G2G_Dispatcher.lua @@ -185,12 +185,12 @@ do infantry = {}, } + -- @field category WAREHOUSE.category= { Transport=1, - Figherplane=1, + Fighter=1, AWACS=1, - Tanker=1, - + Tanker=1, } --- WAREHOUSE constructor. Creates a new WAREHOUSE object. @@ -205,11 +205,11 @@ do self.homebase=airbase self.coordinate=airbase:GetCoordinate() self.coalition=airbase:GetCoalition() - - - self:AddTransition("*", "Start", "Idle") - self:AddTransition("*", "Status", "*") - self:AddTransition("*", "Request", "*") + + self:AddTransition("*", "Start", "Running") + self:AddTransition("*", "Status", "*") + self:AddTransition("*", "Request", "*") + self:AddTransition("*", "Delivered", "*") --- Triggers the FSM event "Start". -- @function [parent=#WAREHOUSE] Start @@ -236,6 +236,7 @@ do -- @param #WAREHOUSE self -- @param Wrapper.Airbase#AIRBASE Airbase Airbase requesting supply. -- @param #string Asset Asset that is requested. + -- @param #number nAsset Number of assets requested. Default 1. -- @param #string TransportType Type of transport: "Plane", "Helicopter", "APC" --- Triggers the FSM event "Request" after a delay. @@ -244,7 +245,19 @@ do -- @param #number delay Delay in seconds. -- @param Wrapper.Airbase#AIRBASE Airbase Airbase requesting supply. -- @param #string Asset Asset that is requested. + -- @param #number nAsset Number of assets requested. Default 1. -- @param #string TransportType Type of transport: "Plane", "Helicopter", "APC" + + --- Triggers the FSM event "Delivered". + -- @function [parent=#WAREHOUSE] Delivered + -- @param #WAREHOUSE self + -- @param Wrapper.Group#GROUP group Group that was delivered. + + --- Triggers the FSM event "Delivered" after a delay. + -- @function [parent=#WAREHOUSE] __Delivered + -- @param #number delay Delay in seconds. + -- @param #WAREHOUSE self + -- @param Wrapper.Group#GROUP group Group that was delivered. return self end @@ -255,7 +268,7 @@ do -- @param #string Event Event. -- @param #string To To state. function WAREHOUSE:onafterStart(From, Event, To) - env.info("FF starting warehouse of airbase of "..self.homebase:GetName()) + env.info("FF starting warehouse at airbase "..self.homebase:GetName()) -- handle events -- event takeoff @@ -272,7 +285,7 @@ do -- @param #string Event Event. -- @param #string To To state. function WAREHOUSE:onafterStatus(From, Event, To) - env.info("FF checking warehouse status of "..self.homebase:GetName()) + env.info("FF checking warehouse status of airbase "..self.homebase:GetName()) env.info(string.format("FF warehouse at %s: number of transport planes = %d", self.homebase:GetName(), #self.plane)) @@ -295,35 +308,80 @@ do if TransportType=="Air" then - local template=self.plane[math.random(#self.plane)] + -- Get a random template from the stock list. + local _chosenone=math.random(#self.plane) + -- Select template group name. + local template=self.plane[_chosenone] + if template then + -- Spawn plane at warehouse homebase. local Plane=SPAWN:New(template):SpawnAtAirbase(Airbase, nil, nil, nil, false) + if Plane==nil then + -- Plane was not spawned correctly. Try again in 60 seconds. + self:__Request( 60, Airbase, Asset, nAsset, TransportType) + return + else + -- Remove chosen plane from list. + table.remove(self.plane,_chosenone) + end + + -- New empty cargo set. local CargoGroups = SET_CARGO:New() + -- Spawn requested assets. local spawn=SPAWN:New("Infantry Platoon Alpha") for i=1,nAsset do local spawngroup=spawn:SpawnFromVec3(self.homebase:GetZone():GetRandomPointVec3(100,500)) local cargogroup = CARGO_GROUP:New(spawngroup, "Infantry", string.format( "Infantry Platoon %d", i), 5000, 35) - CargoGroups:AddCargo(cargogroup) + CargoGroups:AddCargo(cargogroup) end - local CargoPlane = AI_CARGO_AIRPLANE:New(Plane, CargoGroups) + -- Define cargo airplane. + local CargoPlane = AI_CARGO_AIRPLANE:New(Plane, CargoGroups) + -- Pickup cargo at homebase. CargoPlane:__Pickup(5, self.homebase) - function CargoPlane:onafterLoaded( Airplane, From, Event, To, Cargo) + -- Set warehouse state so that we can retreive it later. + Plane:SetState(Plane, "WAREHOUSE", self) + + -- Once the cargo was loaded start off to deploy airbase. + function CargoPlane:OnAfterLoaded(Airplane, From, Event, To) CargoPlane:__Deploy(10, Airbase, 500) end - + --- Function + -- @param Wrapper.Group#GROUP Airplane + function CargoPlane:OnAfterUnloaded(Airplane, From, Event, To) + local group=CargoPlane.Cargo:GetObject() + local Airplane=Airplane --Wrapper.Group#GROUP + local warehouse Airplane:GetState(Airplane, "WAREHOUSE") --#WAREHOUSE + warehouse:__Delivered(1, group) + end + end end end + + --- Warehouse + -- @param #WAREHOUSE self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Group#GROUP Group The group that was delivered. + -- @param #string Asset Asset that is requested. + -- @param #number nAssed Number of groups of that asset requested. + -- @param #string TransportType Type of transport: "Plane", "Helicopter", "APC" + function WAREHOUSE:onafterDelivered(From, Event, To, Group) + local road=Group:GetCoordinate():GetClosestPointToRoad() + local speed=Group:GetSpeedMax()*0.5 + Group:RouteGroundTo(road, speed, "Off Road") + end --- Add an airplane group to the warehouse stock. -- @param #WAREHOUSE self @@ -341,27 +399,44 @@ do local DCSdisplay=DCSunit:getDesc().displayName local DCScategory=DCSgroup:getCategory() local DCStype=DCSunit:getTypeName() - - --env.info(string.format("FF adding %d transport plane template %s type %s, display %s", n, tostring(templateprefix), tostring(typename), tostring(displayname))) - - --[[ - -- Create a table with properties. - self.airplane[templateprefix]=self.airplane[templateprefix] or {} - - -- Increase number in stock. - if self.airplane[templateprefix].nstock then - self.airplane[templateprefix].nstock=self.airplane[templateprefix].nstock+n - else - self.airplane[templateprefix].nstock=n - end - - self.airplane[templateprefix].nstock=n - ]] - + + -- Add this n times to the table. for i=1,n do table.insert(self.plane, templateprefix) end + return self + end + + + --- Add an airplane group to the warehouse stock. + -- @param #WAREHOUSE self + -- @param #string templateprefix Name of the late activated template group as defined in the mission editor. + -- @param #number n Number of groups to add to the warehouse stock. + -- @return #WAREHOUSE self + function WAREHOUSE:AddInfantry(templateprefix, n) + + local n=n or 1 + + local group=GROUP:FindByName(templateprefix) + + if group then + + local DCSgroup=group:GetDCSObject() + local DCSunit=DCSgroup:getUnit(1) + local DCSdesc=DCSunit:getDesc() + local DCSdisplay=DCSunit:getDesc().displayName + local DCScategory=DCSgroup:getCategory() + local DCStype=DCSunit:getTypeName() + + -- Add this n times to the table. + for i=1,n do + table.insert(self.infantry, {templatename=templateprefix, category=DCScategory, typename=DCStype}) + end + + end + + return self end end diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index 83d9cd5a7..1b4d96efb 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -798,8 +798,7 @@ end -- @param Object The object that will hold the Value set by the Key. -- @param Key The key that is used as a reference of the value. Note that the key can be a #string, but it can also be any other type! -- @param Value The value to is stored in the object. --- @return The Value set. --- @return #nil The Key was not found and thus the Value could not be retrieved. +-- @return The Value set. function BASE:SetState( Object, Key, Value ) local ClassNameAndID = Object:GetClassNameAndID() @@ -816,7 +815,7 @@ end -- @param #BASE self -- @param Object The object that holds the Value set by the Key. -- @param Key The key that is used to retrieve the value. Note that the key can be a #string, but it can also be any other type! --- @return The Value retrieved. +-- @return The Value retrieved or nil if the Key was not found and thus the Value could not be retrieved. function BASE:GetState( Object, Key ) local ClassNameAndID = Object:GetClassNameAndID() @@ -829,6 +828,10 @@ function BASE:GetState( Object, Key ) return nil end +--- Clear the state of an object. +-- @param #BASE self +-- @param Object The object that holds the Value set by the Key. +-- @param StateName The key that is should be cleared. function BASE:ClearState( Object, StateName ) local ClassNameAndID = Object:GetClassNameAndID() From 12be42ee2fb435759361278912ef53fd319a927b Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 31 Jul 2018 21:47:25 +0200 Subject: [PATCH 226/420] Documentation updates --- .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 57 ++++++++++++++----- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index f5072264b..d1473dd4d 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -81,13 +81,13 @@ do -- TASK_CARGO_DISPATCHER -- The **TASK_CARGO_DISPATCHER** allows you to setup various tasks for let human -- players transport cargo as part of a task. -- - -- There are currently two types of tasks that can be constructed: + -- There are currently **two types of tasks** that can be constructed: -- - -- * A normal cargo transport task, which tasks humans to transport cargo from a location towards a deploy zone. - -- * A CSAR cargo transport task. CSAR tasks are automatically generated when a pilot is downed... - -- It is created when a fiendly pilot has ejected from a plane, and needs to be rescued (sometimes behind enemy lines). + -- * A **normal cargo transport** task, which tasks humans to transport cargo from a location towards a deploy zone. + -- * A **CSAR** cargo transport task. CSAR tasks are **automatically generated** when a friendly (AI) plane is downed and the friendly pilot ejects... + -- You as a player (the helo pilot) can go out in the battlefield, fly behind enemy lines, and rescue the pilot (back to a deploy zone). -- - -- Let's explore step by step how to setup the task dispatcher. + -- Let's explore **step by step** how to setup the task cargo dispatcher. -- -- # 1. Setup a mission environment. -- @@ -127,21 +127,21 @@ do -- TASK_CARGO_DISPATCHER -- -- -- Here we define the "cargo set", which is a collection of cargo objects. -- -- The cargo set will be the input for the cargo transportation task. - -- -- So a transportation object is handling a cargo set, which is automatically refreshed when new cargo is added/deleted. + -- -- So a transportation object is handling a cargo set, which is automatically updated when new cargo is added/deleted. -- local WorkmaterialsCargoSet = SET_CARGO:New():FilterTypes( "Workmaterials" ):FilterStart() -- -- -- Now we add cargo into the battle scene. -- local PilotGroup = GROUP:FindByName( "Engineers" ) -- -- -- CARGO_GROUP can be used to setup cargo with a GROUP object underneath. - -- -- We name this group Engineers. - -- -- Note that the name of the cargo is "Engineers". - -- -- The cargoset "CargoSet" will embed all defined cargo of type "Pilots" (prefix) into its set. + -- -- We name the type of this group "Workmaterials", so that this cargo group will be included within the WorkmaterialsCargoSet. + -- -- Note that the name of the cargo is "Engineer Team 1". -- local CargoGroup = CARGO_GROUP:New( PilotGroup, "Workmaterials", "Engineer Team 1", 500 ) -- -- What is also needed, is to have a set of @{Core.Group}s defined that contains the clients of the players. -- - -- -- Allocate the Transport, which are the helicopter to retrieve the pilot, that can be manned by players. + -- -- Allocate the Transport, which are the helicopters to retrieve the pilot, that can be manned by players. + -- -- The name of these helicopter groups containing one client begins with "Transport", as modelled within the mission editor. -- local PilotGroupSet = SET_GROUP:New():FilterPrefixes( "Transport" ):FilterStart() -- -- ## 2.2. Setup the cargo transport task. @@ -152,16 +152,20 @@ do -- TASK_CARGO_DISPATCHER -- -- So, the variable `TaskDispatcher` will contain the object of class TASK_CARGO_DISPATCHER, which will allow you to dispatch cargo transport tasks: -- - -- * for mission `Mission` - -- * for the pilots `PilotGroupSet`. + -- * for mission `Mission`. + -- * for the group set `PilotGroupSet`. -- - -- Now that we have `TaskDispatcher` object, we can now create the TransportTask manually, using the @{#TASK_CARGO_DISPATCHER.AddTransportTask}() method! + -- Now that we have `TaskDispatcher` object, we can now **create the TransportTask**, using the @{#TASK_CARGO_DISPATCHER.AddTransportTask}() method! -- -- local TransportTask = TaskDispatcher:AddTransportTask( -- "Transport workmaterials", -- WorkmaterialsCargoSet, -- "Transport the workers, engineers and the equipment near the Workplace." ) -- + -- As a result of this code, the `TransportTask` (returned) variable will contain an object of @{#TASK_CARGO_TRANSPORT}! + -- We pass to the method the title of the task, and the `WorkmaterialsCargoSet`, which is the set of cargo groups to be transported! + -- This object can also be used to setup additional things, or to control this specific task with special actions. + -- -- And you're done! As you can see, it is a bit of work, but the reward is great. -- And, because all this is done using program interfaces, you can build a mission with a **dynamic cargo transport task mechanism** yourself! -- Based on events happening within your mission, you can use the above methods to create new cargo, and setup a new task for cargo transportation to a group of players! @@ -228,8 +232,35 @@ do -- TASK_CARGO_DISPATCHER -- "Bring the pilot back!" -- ) -- + -- As a result of this code, the `CSARTask` (returned) variable will contain an object of @{#TASK_CARGO_CSAR}! + -- We pass to the method the title of the task, and the `WorkmaterialsCargoSet`, which is the set of cargo groups to be transported! + -- This object can also be used to setup additional things, or to control this specific task with special actions. -- Note that when you declare a CSAR task manually, you'll still need to specify a deployment zone! -- + -- # 4. Setup the deploy zone(s). + -- + -- The task cargo dispatcher also foresees methods to setup the deployment zones to where the cargo needs to be transported! + -- + -- There are two levels on which deployment zones can be configured: + -- + -- * Default deploy zones: The TASK_CARGO_DISPATCHER object can have default deployment zones, which will apply over all tasks active in the task dispatcher. + -- * Task specific deploy zones: The TASK_CARGO_DISPATCHER object can have specific deployment zones which apply to a specific task only! + -- + -- Note that for Task specific deployment zones, there are separate deployment zone creation methods per task type! + -- + -- ## 4.1. Setup default deploy zones. + -- + -- Use the @{#TASK_CARGO_DISPATCHER.SetDefaultDeployZone}() to setup one deployment zone, and @{#TASK_CARGO_DISPATCHER.SetDefaultDeployZones}() to setup multiple default deployment zones in one call. + -- + -- ## 4.2. Setup task specific deploy zones for a **transport task**. + -- + -- Use the @{#TASK_CARGO_DISPATCHER.SetTransportDeployZone}() to setup one deployment zone, and @{#TASK_CARGO_DISPATCHER.SetTransportDeployZones}() to setup multiple default deployment zones in one call. + -- + -- ## 4.3. Setup task specific deploy zones for a **CSAR task**. + -- + -- Use the @{#TASK_CARGO_DISPATCHER.SetCSARDeployZone}() to setup one deployment zone, and @{#TASK_CARGO_DISPATCHER.SetCSARDeployZones}() to setup multiple default deployment zones in one call. + -- + -- -- -- @field #TASK_CARGO_DISPATCHER TASK_CARGO_DISPATCHER = { From e0564876f4d85cd3e0cfbe21010e73b5723673e3 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 1 Aug 2018 01:11:24 +0200 Subject: [PATCH 227/420] RANGE 1.2.1, WAREHOUSE 0.1.0 --- .../Moose/AI/AI_G2G_Dispatcher.lua | 306 -------------- Moose Development/Moose/DCS.lua | 1 + Moose Development/Moose/Functional/Range.lua | 30 +- .../Moose/Functional/Warehouse.lua | 383 ++++++++++++++++++ Moose Development/Moose/Wrapper/Group.lua | 37 +- Moose Setup/Moose.files | 1 + 6 files changed, 447 insertions(+), 311 deletions(-) create mode 100644 Moose Development/Moose/Functional/Warehouse.lua diff --git a/Moose Development/Moose/AI/AI_G2G_Dispatcher.lua b/Moose Development/Moose/AI/AI_G2G_Dispatcher.lua index 36f833a80..734c4ef4d 100644 --- a/Moose Development/Moose/AI/AI_G2G_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_G2G_Dispatcher.lua @@ -137,309 +137,3 @@ do -- AI_G2G_DISPATCHER end - - -do - - --- WAREHOUSE class. - -- @type WAREHOUSE - -- @field #string ClassName Name of the class. - -- @extends Core.Fsm#FSM - - --- Create an automatic ground . - -- - -- === - -- - -- # Demo Missions - -- - -- ### None. - -- - -- === - -- - -- # YouTube Channel - -- - -- ### None. - -- - -- === - -- - -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia3.JPG) - -- - -- Warehouse - -- - -- === - -- - -- # USAGE GUIDE - -- - -- - -- - -- @field #WAREHOUSE - WAREHOUSE = { - ClassName = "WAREHOUSE", - coalition = nil, - homebase = nil, - plane = {}, - helo = {}, - arty = {}, - tank = {}, - apc = {}, - infantry = {}, - } - - -- @field category - WAREHOUSE.category= { - Transport=1, - Fighter=1, - AWACS=1, - Tanker=1, - } - - --- WAREHOUSE constructor. Creates a new WAREHOUSE object. - -- @param #WAREHOUSE self - -- @param Wrapper.Airbase#AIRBASE airbase Airbase. - -- @return #WAREHOUSE self - function WAREHOUSE:NewAirbase(airbase) - - -- Inherits from FSM - local self = BASE:Inherit( self, FSM:New() ) -- #WAREHOUSE - - self.homebase=airbase - self.coordinate=airbase:GetCoordinate() - self.coalition=airbase:GetCoalition() - - self:AddTransition("*", "Start", "Running") - self:AddTransition("*", "Status", "*") - self:AddTransition("*", "Request", "*") - self:AddTransition("*", "Delivered", "*") - - --- Triggers the FSM event "Start". - -- @function [parent=#WAREHOUSE] Start - -- @param #WAREHOUSE self - - --- Triggers the FSM event "Start" after a delay. - -- @function [parent=#WAREHOUSE] __Start - -- @param #WAREHOUSE self - -- @param #number delay Delay in seconds. - - - --- Triggers the FSM event "Status". - -- @function [parent=#WAREHOUSE] Status - -- @param #WAREHOUSE self - - --- Triggers the FSM event "Status" after a delay. - -- @function [parent=#WAREHOUSE] __Status - -- @param #WAREHOUSE self - -- @param #number delay Delay in seconds. - - - --- Triggers the FSM event "Request". - -- @function [parent=#WAREHOUSE] Request - -- @param #WAREHOUSE self - -- @param Wrapper.Airbase#AIRBASE Airbase Airbase requesting supply. - -- @param #string Asset Asset that is requested. - -- @param #number nAsset Number of assets requested. Default 1. - -- @param #string TransportType Type of transport: "Plane", "Helicopter", "APC" - - --- Triggers the FSM event "Request" after a delay. - -- @function [parent=#WAREHOUSE] __Request - -- @param #WAREHOUSE self - -- @param #number delay Delay in seconds. - -- @param Wrapper.Airbase#AIRBASE Airbase Airbase requesting supply. - -- @param #string Asset Asset that is requested. - -- @param #number nAsset Number of assets requested. Default 1. - -- @param #string TransportType Type of transport: "Plane", "Helicopter", "APC" - - --- Triggers the FSM event "Delivered". - -- @function [parent=#WAREHOUSE] Delivered - -- @param #WAREHOUSE self - -- @param Wrapper.Group#GROUP group Group that was delivered. - - --- Triggers the FSM event "Delivered" after a delay. - -- @function [parent=#WAREHOUSE] __Delivered - -- @param #number delay Delay in seconds. - -- @param #WAREHOUSE self - -- @param Wrapper.Group#GROUP group Group that was delivered. - - return self - end - - --- Warehouse - -- @param #WAREHOUSE self - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - function WAREHOUSE:onafterStart(From, Event, To) - env.info("FF starting warehouse at airbase "..self.homebase:GetName()) - - -- handle events - -- event takeoff - -- event landing - -- event crash/dead - -- event base captured - self:__Status(-5) - end - - - --- Warehouse - -- @param #WAREHOUSE self - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - function WAREHOUSE:onafterStatus(From, Event, To) - env.info("FF checking warehouse status of airbase "..self.homebase:GetName()) - - env.info(string.format("FF warehouse at %s: number of transport planes = %d", self.homebase:GetName(), #self.plane)) - - self:__Status(-30) - end - - --- Warehouse - -- @param #WAREHOUSE self - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - -- @param Wrapper.Airbase#AIRBASE Airbase Airbase requesting supply. - -- @param #string Asset Asset that is requested. - -- @param #number nAssed Number of groups of that asset requested. - -- @param #string TransportType Type of transport: "Plane", "Helicopter", "APC" - function WAREHOUSE:onafterRequest(From, Event, To, Airbase, Asset, nAsset, TransportType) - env.info(string.format("FF airbase %s is requesting asset %s from warehouse %s", Airbase:GetName(), Asset, self.homebase:GetName())) - - local nAsset=nAsset or 1 - - if TransportType=="Air" then - - -- Get a random template from the stock list. - local _chosenone=math.random(#self.plane) - - -- Select template group name. - local template=self.plane[_chosenone] - - if template then - - -- Spawn plane at warehouse homebase. - local Plane=SPAWN:New(template):SpawnAtAirbase(Airbase, nil, nil, nil, false) - - if Plane==nil then - -- Plane was not spawned correctly. Try again in 60 seconds. - self:__Request( 60, Airbase, Asset, nAsset, TransportType) - return - else - -- Remove chosen plane from list. - table.remove(self.plane,_chosenone) - end - - -- New empty cargo set. - local CargoGroups = SET_CARGO:New() - - -- Spawn requested assets. - local spawn=SPAWN:New("Infantry Platoon Alpha") - - for i=1,nAsset do - local spawngroup=spawn:SpawnFromVec3(self.homebase:GetZone():GetRandomPointVec3(100,500)) - local cargogroup = CARGO_GROUP:New(spawngroup, "Infantry", string.format( "Infantry Platoon %d", i), 5000, 35) - CargoGroups:AddCargo(cargogroup) - end - - -- Define cargo airplane. - local CargoPlane = AI_CARGO_AIRPLANE:New(Plane, CargoGroups) - - -- Pickup cargo at homebase. - CargoPlane:__Pickup(5, self.homebase) - - -- Set warehouse state so that we can retreive it later. - Plane:SetState(Plane, "WAREHOUSE", self) - - -- Once the cargo was loaded start off to deploy airbase. - function CargoPlane:OnAfterLoaded(Airplane, From, Event, To) - CargoPlane:__Deploy(10, Airbase, 500) - end - - --- Function - -- @param Wrapper.Group#GROUP Airplane - function CargoPlane:OnAfterUnloaded(Airplane, From, Event, To) - local group=CargoPlane.Cargo:GetObject() - local Airplane=Airplane --Wrapper.Group#GROUP - local warehouse Airplane:GetState(Airplane, "WAREHOUSE") --#WAREHOUSE - warehouse:__Delivered(1, group) - end - - end - end - - end - - --- Warehouse - -- @param #WAREHOUSE self - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. - -- @param Wrapper.Group#GROUP Group The group that was delivered. - -- @param #string Asset Asset that is requested. - -- @param #number nAssed Number of groups of that asset requested. - -- @param #string TransportType Type of transport: "Plane", "Helicopter", "APC" - function WAREHOUSE:onafterDelivered(From, Event, To, Group) - local road=Group:GetCoordinate():GetClosestPointToRoad() - local speed=Group:GetSpeedMax()*0.5 - Group:RouteGroundTo(road, speed, "Off Road") - end - - --- Add an airplane group to the warehouse stock. - -- @param #WAREHOUSE self - -- @param #string templateprefix Name of the late activated template group as defined in the mission editor. - -- @param #number n Number of groups to add to the warehouse stock. - -- @return #WAREHOUSE self - function WAREHOUSE:AddTransportPlane(templateprefix, n) - - local n=n or 1 - - local group=GROUP:FindByName(templateprefix) - local DCSgroup=group:GetDCSObject() - local DCSunit=DCSgroup:getUnit(1) - local DCSdesc=DCSunit:getDesc() - local DCSdisplay=DCSunit:getDesc().displayName - local DCScategory=DCSgroup:getCategory() - local DCStype=DCSunit:getTypeName() - - -- Add this n times to the table. - for i=1,n do - table.insert(self.plane, templateprefix) - end - - return self - end - - - --- Add an airplane group to the warehouse stock. - -- @param #WAREHOUSE self - -- @param #string templateprefix Name of the late activated template group as defined in the mission editor. - -- @param #number n Number of groups to add to the warehouse stock. - -- @return #WAREHOUSE self - function WAREHOUSE:AddInfantry(templateprefix, n) - - local n=n or 1 - - local group=GROUP:FindByName(templateprefix) - - if group then - - local DCSgroup=group:GetDCSObject() - local DCSunit=DCSgroup:getUnit(1) - local DCSdesc=DCSunit:getDesc() - local DCSdisplay=DCSunit:getDesc().displayName - local DCScategory=DCSgroup:getCategory() - local DCStype=DCSunit:getTypeName() - - -- Add this n times to the table. - for i=1,n do - table.insert(self.infantry, {templatename=templateprefix, category=DCScategory, typename=DCStype}) - end - - end - - return self - end - -end - - - diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua index 492d1d049..ba6bc1c42 100644 --- a/Moose Development/Moose/DCS.lua +++ b/Moose Development/Moose/DCS.lua @@ -959,6 +959,7 @@ do -- Group -- @field HELICOPTER -- @field GROUND -- @field SHIP + -- @field TRAIN -- Static Functions diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index 1e39d0735..e7c015056 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -68,6 +68,8 @@ -- @field #number dtBombtrack Time step [sec] used for tracking released bomb/rocket positions. Default 0.005 seconds. -- @field #number BombtrackThreshold Bombs/rockets/missiles are only tracked if player-range distance is smaller than this threashold [m]. Default 25000 m. -- @field #number Tmsg Time [sec] messages to players are displayed. Default 30 sec. +-- @field #string examinergroupname Name of the examiner group which should get all messages. +-- @field #boolean examinerexclusive If true, only the examiner gets messages. If false, clients and examiner get messages. -- @field #number strafemaxalt Maximum altitude above ground for registering for a strafe run. Default is 914 m = 3000 ft. -- @field #number ndisplayresult Number of (player) results that a displayed. Default is 10. -- @field Utilities.Utils#SMOKECOLOR BombSmokeColor Color id used for smoking bomb targets. @@ -234,6 +236,8 @@ RANGE={ dtBombtrack=0.005, BombtrackThreshold=25000, Tmsg=30, + examinergroupname=nil, + examinerexclusive=nil, strafemaxalt=914, ndisplayresult=10, BombSmokeColor=SMOKECOLOR.Red, @@ -279,8 +283,8 @@ RANGE.MenuF10={} RANGE.id="RANGE | " --- Range script version. --- @field #number version -RANGE.version="1.2.0" +-- @field #string version +RANGE.version="1.2.1" --TODO list: --TODO: Add custom weapons, which can be specified by the user. @@ -434,6 +438,15 @@ function RANGE:SetMessageTimeDuration(time) self.Tmsg=time or RANGE.Defaults.Tmsg end +--- Set messages to examiner. The examiner will receive messages from all clients. +-- @param #RANGE self +-- @param #string examinergroupname Name of the group of the examiner. +-- @param #boolean exclusively If true, messages are send exclusively to the examiner, i.e. not to the clients. +function RANGE:SetMessageToExaminer(examinergroupname, exclusively) + self.examinergroupname=examinergroupname + self.examinerexclusive=exclusively +end + --- Set max number of player results that are displayed. -- @param #RANGE self -- @param #number nmax Number of results. Default is 10. @@ -2119,13 +2132,24 @@ function RANGE:_DisplayMessageToGroup(_unit, _text, _time, _clear) -- Group ID. local _gid=_unit:GetGroup():GetID() - if _gid then + if _gid and not self.examinerexclusive then if _clear == true then trigger.action.outTextForGroup(_gid, _text, _time, _clear) else trigger.action.outTextForGroup(_gid, _text, _time) end end + + if self.examinergroupname~=nil then + local _examinerid=GROUP:FindByName(self.examinergroupname):GetID() + if _examinerid then + if _clear == true then + trigger.action.outTextForGroup(_examinerid, _text, _time, _clear) + else + trigger.action.outTextForGroup(_examinerid, _text, _time) + end + end + end end diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua new file mode 100644 index 000000000..c5ee2d6a7 --- /dev/null +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -0,0 +1,383 @@ +--- **Functional** - (R2.4) - Manages assets of an airbase and transportation to other airbases. +-- +-- +-- Features: +-- +-- * Some nice stuff. +-- +-- # QUICK START GUIDE +-- +-- === +-- +-- ### Authors: **funkyfranky** +-- +-- @module Functional.Warehouse +-- @image Warehouse.JPG + +--- WAREHOUSE class. +-- @type WAREHOUSE +-- @field #string ClassName Name of the class. +-- @extends Core.Fsm#FSM + +--- Manages ground assets of an airbase and offers the possibility to transport them to another airbase or warehouse. +-- +-- === +-- +-- # Demo Missions +-- +-- ### None. +-- +-- === +-- +-- # YouTube Channel +-- +-- ### None. +-- +-- === +-- +-- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_pic.JPG) +-- +-- Warehouse +-- +-- === +-- +-- # USAGE GUIDE +-- +-- +-- +-- @field #WAREHOUSE +WAREHOUSE = { + ClassName = "WAREHOUSE", + coalition = nil, + homebase = nil, + stock = {}, +} + +--- Type Warehouse stock table. table.insert(self.stock, {templatename=templategroupname, category=DCScategory, type=DCStype, transport=transport, fighther=fighter, tanker=tanker, awacs=awacs, artillery=artillery}) +-- @type WAREHOUSE.Stock +-- @field #string templatename Name of the template group. +-- @field DCS#Category category Category of the group. Airplane, helicopter, ... +-- @field #string type Type of the group +-- @field #boolean fighter If true, group is a fighter airplane. +-- @field #boolean transport If truie, group can transport other units either by air or ground. +-- @field #boolean tanker If true, group is a tanker and can refuel other air units. +-- @field #boolean awacs If true, group has AWACS capabilities. +-- @field #boolean artillery If true, group is an artillery unit. + +--- Warehouse classes. +-- @field Warehouse.Class Class +WAREHOUSE.Class = { + TRANSPORT=1, + FIGHTER=2, + TANKER=3, + AWACS=4, + ARTY=5, +} + +--- Warehouse categories +-- @field Category +WAREHOUSE.Category = { + AIRPLANE = 0, + HELICOPTER = 1, + GROUND = 2, + SHIP = 3, + TRAIN = 4, +} + +--- Warehouse class version. +-- @field #number version +WAREHOUSE.version="0.1.0" + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- TODO: Warehuse todo list. +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +-- TODO: A lot! + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Constructor(s) +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- WAREHOUSE constructor. Creates a new WAREHOUSE object. +-- @param #WAREHOUSE self +-- @param Wrapper.Airbase#AIRBASE airbase Airbase. +-- @return #WAREHOUSE self +function WAREHOUSE:NewAirbase(airbase) + BASE:E({airbase=airbase}) + + -- Print version. + env.info(string.format("Adding warehouse v%s for airbase %s", WAREHOUSE.version, airbase:GetName())) + + -- Inherit everthing from FSM class. + local self = BASE:Inherit( self, FSM:New() ) -- #WAREHOUSE + + -- Set some string id for output to DCS.log file. + self.wid=string.format("WAREHOUSE %s | ", airbase:GetName()) + + -- Set some variables. + self.homebase=airbase + self.coordinate=airbase:GetCoordinate() + self.coalition=airbase:GetCoalition() + + self:AddTransition("*", "Start", "Running") + self:AddTransition("*", "Status", "*") + self:AddTransition("*", "Request", "*") + self:AddTransition("*", "Delivered", "*") + + --- Triggers the FSM event "Start". + -- @function [parent=#WAREHOUSE] Start + -- @param #WAREHOUSE self + + --- Triggers the FSM event "Start" after a delay. + -- @function [parent=#WAREHOUSE] __Start + -- @param #WAREHOUSE self + -- @param #number delay Delay in seconds. + + + --- Triggers the FSM event "Status". + -- @function [parent=#WAREHOUSE] Status + -- @param #WAREHOUSE self + + --- Triggers the FSM event "Status" after a delay. + -- @function [parent=#WAREHOUSE] __Status + -- @param #WAREHOUSE self + -- @param #number delay Delay in seconds. + + + --- Triggers the FSM event "Request". + -- @function [parent=#WAREHOUSE] Request + -- @param #WAREHOUSE self + -- @param Wrapper.Airbase#AIRBASE Airbase Airbase requesting supply. + -- @param #string Asset Asset that is requested. + -- @param #number nAsset Number of assets requested. Default 1. + -- @param #string TransportType Type of transport: "Plane", "Helicopter", "APC" + + --- Triggers the FSM event "Request" after a delay. + -- @function [parent=#WAREHOUSE] __Request + -- @param #WAREHOUSE self + -- @param #number delay Delay in seconds. + -- @param Wrapper.Airbase#AIRBASE Airbase Airbase requesting supply. + -- @param #string Asset Asset that is requested. + -- @param #number nAsset Number of assets requested. Default 1. + -- @param #string TransportType Type of transport: "Plane", "Helicopter", "APC" + + --- Triggers the FSM event "Delivered". + -- @function [parent=#WAREHOUSE] Delivered + -- @param #WAREHOUSE self + -- @param Wrapper.Group#GROUP group Group that was delivered. + + --- Triggers the FSM event "Delivered" after a delay. + -- @function [parent=#WAREHOUSE] __Delivered + -- @param #number delay Delay in seconds. + -- @param #WAREHOUSE self + -- @param Wrapper.Group#GROUP group Group that was delivered. + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- FSM states +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Warehouse +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function WAREHOUSE:onafterStart(From, Event, To) + env.info("FF starting warehouse at airbase "..self.homebase:GetName()) + + -- handle events + -- event takeoff + -- event landing + -- event crash/dead + -- event base captured + self:__Status(-5) +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Warehouse +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function WAREHOUSE:onafterStatus(From, Event, To) + env.info("FF checking warehouse status of airbase "..self.homebase:GetName()) + + env.info(string.format("FF warehouse at %s: number of stock = %d", self.homebase:GetName(), #self.stock)) + + self:__Status(-30) +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Warehouse +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Wrapper.Airbase#AIRBASE Airbase Airbase requesting supply. +-- @param #string Asset Asset that is requested. +-- @param #number nAssed Number of groups of that asset requested. +-- @param #string TransportType Type of transport: "Plane", "Helicopter", "APC" +function WAREHOUSE:onafterRequest(From, Event, To, Airbase, Asset, nAsset, TransportType) + env.info(string.format("FF airbase %s is requesting asset %s from warehouse %s", Airbase:GetName(), Asset, self.homebase:GetName())) + + local nAsset=nAsset or 1 + + if TransportType=="Air" then + + -- Get a random template from the stock list. + local _chosenone=math.random(#self.stock) + + -- Select template group name. + --TODO: FILTER HERE'! + local template=self.stock[_chosenone].templatename + + if template then + + -- Spawn plane at warehouse homebase. + local Plane=SPAWN:New(template):SpawnAtAirbase(Airbase, nil, nil, nil, false) + + if Plane==nil then + -- Plane was not spawned correctly. Try again in 60 seconds. + self:__Request( 60, Airbase, Asset, nAsset, TransportType) + return + else + -- Remove chosen plane from list. + table.remove(self.stock,_chosenone) + end + + -- New empty cargo set. + local CargoGroups = SET_CARGO:New() + + -- Spawn requested assets. + local spawn=SPAWN:New("Infantry Platoon Alpha") + + for i=1,nAsset do + local spawngroup=spawn:SpawnFromVec3(self.homebase:GetZone():GetRandomPointVec3(100,500)) + local cargogroup = CARGO_GROUP:New(spawngroup, "Infantry", string.format( "Infantry Platoon %d", i), 5000, 35) + CargoGroups:AddCargo(cargogroup) + end + + -- Define cargo airplane. + local CargoPlane = AI_CARGO_AIRPLANE:New(Plane, CargoGroups) + + -- Pickup cargo at homebase. + CargoPlane:__Pickup(5, self.homebase) + + -- Set warehouse state so that we can retreive it later. + Plane:SetState(Plane, "WAREHOUSE", self) + + -- Once the cargo was loaded start off to deploy airbase. + function CargoPlane:OnAfterLoaded(Airplane, From, Event, To) + CargoPlane:__Deploy(10, Airbase, 500) + end + + --- Function + -- @param Wrapper.Group#GROUP Airplane + function CargoPlane:OnAfterUnloaded(Airplane, From, Event, To) + local group=CargoPlane.Cargo:GetObject() + local Airplane=Airplane --Wrapper.Group#GROUP + local warehouse Airplane:GetState(Airplane, "WAREHOUSE") --#WAREHOUSE + warehouse:__Delivered(1, group) + end + + end + end + +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Warehouse +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Wrapper.Group#GROUP Group The group that was delivered. +-- @param #string Asset Asset that is requested. +-- @param #number nAssed Number of groups of that asset requested. +-- @param #string TransportType Type of transport: "Plane", "Helicopter", "APC" +function WAREHOUSE:onafterDelivered(From, Event, To, Group) + local road=Group:GetCoordinate():GetClosestPointToRoad() + local speed=Group:GetSpeedMax()*0.5 + Group:RouteGroundTo(road, speed, "Off Road") +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- User functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Add an airplane group to the warehouse stock. +-- @param #WAREHOUSE self +-- @param #string templategroupname Name of the late activated template group as defined in the mission editor. +-- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1. +-- @param #boolean istransport If true, this group will act as transport unit to transport other assets to another airbase. If false, this unit will not be used as transport unit. By default the behavior is determined for the group's attributes. +-- @return #WAREHOUSE self +function WAREHOUSE:AddAsset(templategroupname, ngroups, istransport) + + local n=ngroups or 1 + + local group=GROUP:FindByName(templategroupname) + + if group then + + local DCSgroup=group:GetDCSObject() + local DCSunit=DCSgroup:getUnit(1) + local DCSdesc=DCSunit:getDesc() + local DCSdisplay=DCSunit:getDesc().displayName + local DCScategory=DCSgroup:getCategory() + local DCStype=DCSunit:getTypeName() + + env.info(string.format("group name = %s", group:GetName())) + env.info(string.format("display name = %s", DCSdisplay)) + env.info(string.format("category = %s", DCScategory)) + env.info(string.format("type = %s", DCStype)) + env.info(string.format("attribute infantry = %s", tostring(group:HasAttribute("Infantry")))) + self:E({desc=DCSdesc}) + + local transport=group:HasAttribute("Transport helicopters") or group:HasAttribute("Transports") or group:HasAttribute("Infantry carriers") + local fighter=group:HasAttribute("Fighters") or group:HasAttribute("Interceptors") or group:HasAttribute("Multirole fighters") + local tanker=group:HasAttribute("Tankers") + local awacs=group:HasAttribute("AWACS") + local apc=group:HasAttribute("Infantry carriers") + local artillery=group:HasAttribute("Artillery") + env.info(string.format("attribute transport = %s", tostring(transport))) + env.info(string.format("attribute apc = %s", tostring(apc))) + env.info(string.format("attribute figther = %s", tostring(fighter))) + env.info(string.format("attribute tanker = %s", tostring(tanker))) + env.info(string.format("attribute awacs = %s", tostring(awacs))) + env.info(string.format("attribute artillery = %s", tostring(artillery))) + + -- Add this n times to the table. + for i=1,n do + table.insert(self.stock, {templatename=templategroupname, category=DCScategory, type=DCStype, transport=transport, fighther=fighter, tanker=tanker, awacs=awacs, artillery=artillery}) + end + + else + -- Group name does not exist! + self:E(string.format("ERROR: Template group name not defined in the mission editor. Check the spelling! templategroupname=%s",tostring(templategroupname))) + end + + return self +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Helper functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Filter stock assets by table entry. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE.Stock entry +function WAREHOUSE:_FilterStock(entry) + --entry.artillery + + return +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 4a2596c49..f5e40c30c 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -335,8 +335,7 @@ end --- Returns the country of the DCS Group. -- @param #GROUP self --- @return DCS#country.id The country identifier. --- @return #nil The DCS Group is not existing or alive. +-- @return DCS#country.id The country identifier or nil if the DCS Group is not existing or alive. function GROUP:GetCountry() self:F2( self.GroupName ) @@ -350,6 +349,40 @@ function GROUP:GetCountry() return nil end + +--- Check if at least one (or all) unit(s) has (have) a certain attribute. +-- See [hoggit documentation](https://wiki.hoggitworld.com/view/DCS_func_hasAttribute). +-- @param #GROUP self +-- @param #string attribute The name of the attribute the group is supposed to have. Valid attributes can be found in the "db_attributes.lua" file which is located at in "C:\Program Files\Eagle Dynamics\DCS World\Scripts\Database". +-- @param #boolean all If true, all units of the group must have the attribute in order to return true. Default is only one unit of a heterogenious group needs to have the attribute. +-- @return #boolean Group has this attribute. +function GROUP:HasAttribute(attribute, all) + + -- Get all units of the group. + local _units=self:GetUnits() + + local _allhave=true + local _onehas=false + + for _,_unit in pairs(_units) do + local _unit=_unit --Wrapper.Unit#UNIT + if _unit then + local _hastit=_unit:HasAttribute(attribute) + if _hastit==true then + _onehas=true + else + _allhave=false + end + end + end + + if all==true then + return _allhave + else + return _onehas + end +end + --- Returns the maximum speed of the group. -- If the group is heterogenious and consists of different units, the max speed of the slowest unit is returned. -- @param #GROUP self diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index 2f53e45a8..cee63ea07 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -57,6 +57,7 @@ Functional/ZoneCaptureCoalition.lua Functional/Artillery.lua Functional/Suppression.lua Functional/PseudoATC.lua +Functional/Warehouse.lua AI/AI_Balancer.lua AI/AI_A2A.lua From d4449f791364ae6f7c138c29a8f0e981e6d204d9 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 1 Aug 2018 15:56:07 +0200 Subject: [PATCH 228/420] WAREHOUSE --- .../Moose/Functional/Warehouse.lua | 122 +++++++++++++++--- 1 file changed, 101 insertions(+), 21 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index c5ee2d6a7..524c8b779 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -17,6 +17,10 @@ --- WAREHOUSE class. -- @type WAREHOUSE -- @field #string ClassName Name of the class. +-- @field DCS#Coalition coalition Coalition the warehouse belongs to. +-- @field Core.Point#COORDINATE coordinate Coordinate of the warehouse. +-- @field Wrapper.Airbase#AIRBASE airbase Airbase the warehouse belongs to. +-- @field #table stock Table holding all assets in stock. Table entries are of type @{#WAREHOUSE.Stock}. -- @extends Core.Fsm#FSM --- Manages ground assets of an airbase and offers the possibility to transport them to another airbase or warehouse. @@ -47,10 +51,11 @@ -- -- @field #WAREHOUSE WAREHOUSE = { - ClassName = "WAREHOUSE", - coalition = nil, - homebase = nil, - stock = {}, + ClassName = "WAREHOUSE", + coalition = nil, + homebase = nil, + coordinate = nil, + stock = {}, } --- Type Warehouse stock table. table.insert(self.stock, {templatename=templategroupname, category=DCScategory, type=DCStype, transport=transport, fighther=fighter, tanker=tanker, awacs=awacs, artillery=artillery}) @@ -59,11 +64,20 @@ WAREHOUSE = { -- @field DCS#Category category Category of the group. Airplane, helicopter, ... -- @field #string type Type of the group -- @field #boolean fighter If true, group is a fighter airplane. +-- @field #boolean attackhelo If true, group is an attack helicopter. -- @field #boolean transport If truie, group can transport other units either by air or ground. -- @field #boolean tanker If true, group is a tanker and can refuel other air units. -- @field #boolean awacs If true, group has AWACS capabilities. -- @field #boolean artillery If true, group is an artillery unit. +--- Asset descriptor. +-- @field Warehouse.AssetDescriptor Assetdescriptor +WAREHOUSE.Descriptor = { + TEMPLATENAME="templatename", + CATEGORY="category", + +} + --- Warehouse classes. -- @field Warehouse.Class Class WAREHOUSE.Class = { @@ -72,16 +86,18 @@ WAREHOUSE.Class = { TANKER=3, AWACS=4, ARTY=5, + ATTACKHELO=6, } --- Warehouse categories -- @field Category WAREHOUSE.Category = { - AIRPLANE = 0, - HELICOPTER = 1, - GROUND = 2, - SHIP = 3, - TRAIN = 4, + AIRPLANE = "plane", + HELICOPTER = "helo", + GROUND = "apc", + SHIP = "ship", + TRAIN = "train", + SELF = "self", } --- Warehouse class version. @@ -207,12 +223,63 @@ function WAREHOUSE:onafterStatus(From, Event, To) env.info(string.format("FF warehouse at %s: number of stock = %d", self.homebase:GetName(), #self.stock)) - self:__Status(-30) + self:__Status(30) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- On before "Request" event. Checks if the request can be fullfilled. +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Wrapper.Airbase#AIRBASE Airbase Airbase requesting supply. +-- @param #string AssetDescriptor Asset that is requested. Can be "templatename", ... +-- @param depends AssetDescriptorvalue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, +-- @param #number nAssed Number of groups of that asset requested. +-- @param #string TransportType Type of transport: "Plane", "Helicopter", "APC" +-- @return boolean If true, request is granted. +-- +-- @usage mywarehouse:Request(AIRBASE:)... +function WAREHOUSE:onbeforeRequest(From, Event, To, Airbase, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType) ---- Warehouse + -- Distance from warehouse to + local distance=self.coordinate:Get2DDistance(Airbase:GetCoordinate()) + + -- + local _stockrequest=self._FilterStock(self.stock, AssetDescriptor, AssetDescriptorValue) + + -- Asset is not in stock ==> request denied. + if #_stockrequest==0 then + self:E(self.wid..string.format("Request denied! Asset is currently not in stock.")) + return false + end + + local _TT=TransportType:lower() + if _TT==nil then + if AssetDescriptor=="" then + end + end + + if TransportType:lower() == "plane" then + + elseif TransportType:lower() == "helicopter" then + + elseif TransportType:lower() == "apc" then + + elseif TransportType:lower() == "train" then + + elseif TransportType:lower() == "ship" then + + elseif TransportType:lower() == "self" then + + else + self:E(self.wid..string.format("ERROR: unknown transport type requested! type = %s", tostring(TransportType))) + end + +end + + +--- On after "Request" event. -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. @@ -229,10 +296,12 @@ function WAREHOUSE:onafterRequest(From, Event, To, Airbase, Asset, nAsset, Trans if TransportType=="Air" then -- Get a random template from the stock list. - local _chosenone=math.random(#self.stock) + local _chosenone=math.random(#self.stock) --#WAREHOUSE.Stock + + -- Select template group name. - --TODO: FILTER HERE'! + --TODO: FILTER HERE! local template=self.stock[_chosenone].templatename if template then @@ -270,17 +339,19 @@ function WAREHOUSE:onafterRequest(From, Event, To, Airbase, Asset, nAsset, Trans -- Set warehouse state so that we can retreive it later. Plane:SetState(Plane, "WAREHOUSE", self) - -- Once the cargo was loaded start off to deploy airbase. + --- Once the cargo was loaded start off to deploy airbase. function CargoPlane:OnAfterLoaded(Airplane, From, Event, To) CargoPlane:__Deploy(10, Airbase, 500) end - --- Function - -- @param Wrapper.Group#GROUP Airplane + --- Function called when cargo has arrived and was unloaded. function CargoPlane:OnAfterUnloaded(Airplane, From, Event, To) + local group=CargoPlane.Cargo:GetObject() local Airplane=Airplane --Wrapper.Group#GROUP - local warehouse Airplane:GetState(Airplane, "WAREHOUSE") --#WAREHOUSE + local warehouse=Airplane:GetState(Airplane, "WAREHOUSE") --#WAREHOUSE + + -- Trigger Delivered event. warehouse:__Delivered(1, group) end @@ -370,11 +441,20 @@ end --- Filter stock assets by table entry. -- @param #WAREHOUSE self --- @param #WAREHOUSE.Stock entry -function WAREHOUSE:_FilterStock(entry) - --entry.artillery +-- @param #WAREHOUSE.Stock stock +-- @param #string item Descriptor +-- @param depends value +function WAREHOUSE:_FilterStock(stock, item, value) - return + local filtered={} + + for _,_stock in pairs(stock) do + if _stock[item]==value then + table.insert(filtered, _stock) + end + end + + return filtered end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- From 52e69cb697f605ca3877d280801d54c88413050c Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 2 Aug 2018 00:29:14 +0200 Subject: [PATCH 229/420] WAREHOUSE --- Moose Development/Moose/DCS.lua | 8 +- .../Moose/Functional/Warehouse.lua | 240 +++++++++++------- 2 files changed, 159 insertions(+), 89 deletions(-) diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua index ba6bc1c42..c8e997f5c 100644 --- a/Moose Development/Moose/DCS.lua +++ b/Moose Development/Moose/DCS.lua @@ -81,11 +81,11 @@ do -- world --- Searches a defined volume of 3d space for the specified objects within it and then can run function on each returned object. See [hoggit](https://wiki.hoggitworld.com/view/DCS_func_searchObjects). -- @function [parent=#world] searchObjects - -- @param #DCS.Object.Category objectcategory Category (can be a table) of objects to search. - -- @param #DCS word.VolumeType volume Shape of the search area/volume. - -- @param #ObjectSeachHandler handler A function that handles the search. + -- @param DCS#Object.Category objectcategory Category (can be a table) of objects to search. + -- @param DCS#word.VolumeType volume Shape of the search area/volume. + -- @param ObjectSeachHandler handler A function that handles the search. -- @param #table any Additional data. - -- @return #DCS.unit + -- @return DCS#Unit --- Returns a table of mark panels indexed numerically that are present within the mission. See [hoggit](https://wiki.hoggitworld.com/view/DCS_func_getMarkPanels) -- @function [parent=#world] getMarkPanels diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 524c8b779..bb13c9d07 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -1,4 +1,4 @@ ---- **Functional** - (R2.4) - Manages assets of an airbase and transportation to other airbases. +--- **Functional** - (R2.4) - Manages assets of an airbase and transportation to other airbases upon request. -- -- -- Features: @@ -19,7 +19,7 @@ -- @field #string ClassName Name of the class. -- @field DCS#Coalition coalition Coalition the warehouse belongs to. -- @field Core.Point#COORDINATE coordinate Coordinate of the warehouse. --- @field Wrapper.Airbase#AIRBASE airbase Airbase the warehouse belongs to. +-- @field Wrapper.Airbase#AIRBASE homebase Airbase the warehouse belongs to. -- @field #table stock Table holding all assets in stock. Table entries are of type @{#WAREHOUSE.Stock}. -- @extends Core.Fsm#FSM @@ -58,50 +58,50 @@ WAREHOUSE = { stock = {}, } ---- Type Warehouse stock table. table.insert(self.stock, {templatename=templategroupname, category=DCScategory, type=DCStype, transport=transport, fighther=fighter, tanker=tanker, awacs=awacs, artillery=artillery}) --- @type WAREHOUSE.Stock +--- Item of the warehouse stock table. +-- @type WAREHOUSE.Stockitem -- @field #string templatename Name of the template group. --- @field DCS#Category category Category of the group. Airplane, helicopter, ... --- @field #string type Type of the group --- @field #boolean fighter If true, group is a fighter airplane. --- @field #boolean attackhelo If true, group is an attack helicopter. --- @field #boolean transport If truie, group can transport other units either by air or ground. --- @field #boolean tanker If true, group is a tanker and can refuel other air units. --- @field #boolean awacs If true, group has AWACS capabilities. --- @field #boolean artillery If true, group is an artillery unit. +-- @field DCS#Group.Category category Category of the group. +-- @field #string unittype Type of the first unit of the group as obtained by the Object.getTypeName() DCS API function. +-- @field #WAREHOUSE.Attribute attribute Generalized attribute of the group. ---- Asset descriptor. --- @field Warehouse.AssetDescriptor Assetdescriptor +--- Descriptors enumerator describing the type of the asset in stock. +-- @type WAREHOUSE.Descriptor WAREHOUSE.Descriptor = { TEMPLATENAME="templatename", CATEGORY="category", - + UNITTYPE="unittype", + ATTRIBUTE="attribute", } ---- Warehouse classes. --- @field Warehouse.Class Class -WAREHOUSE.Class = { - TRANSPORT=1, - FIGHTER=2, - TANKER=3, - AWACS=4, - ARTY=5, - ATTACKHELO=6, +--- Warehouse unit categories. These are used for +-- @type WAREHOUSE.Attribute +WAREHOUSE.Attribute = { + TRANSPORT="transport", + FIGHTER="fighter", + TANKER="tanker", + AWACS="awacs", + ARTILLERY="artillery", + ATTACKHELICOPTER="attackhelicopter", + INFANTRY="infantry", + BOMBER="bomber", + TANK="tank", } ---- Warehouse categories --- @field Category -WAREHOUSE.Category = { +--- Cargo transport type. +-- @type WAREHOUSE.TransportType +-- @field #string AIRPLANE plane blabla +WAREHOUSE.TransportType = { AIRPLANE = "plane", HELICOPTER = "helo", - GROUND = "apc", + GROUND = "ground", SHIP = "ship", TRAIN = "train", - SELF = "self", + SELFPROPELLED = "selfporpelled", } --- Warehouse class version. --- @field #number version +-- @field #string version WAREHOUSE.version="0.1.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -164,9 +164,13 @@ function WAREHOUSE:NewAirbase(airbase) -- @function [parent=#WAREHOUSE] Request -- @param #WAREHOUSE self -- @param Wrapper.Airbase#AIRBASE Airbase Airbase requesting supply. - -- @param #string Asset Asset that is requested. - -- @param #number nAsset Number of assets requested. Default 1. - -- @param #string TransportType Type of transport: "Plane", "Helicopter", "APC" + -- @param #WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. + -- @param #depends AssetDescriptorvalue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. + -- @param #number nAsset Number of groups requested that match the asset specification. + -- @param #WAREHOUSE.TransportType TransportType Type of transport. + -- @return boolean If true, request is granted. + -- + -- @usage mywarehouse:Request(AIRBASE:)... --- Triggers the FSM event "Request" after a delay. -- @function [parent=#WAREHOUSE] __Request @@ -221,22 +225,24 @@ end function WAREHOUSE:onafterStatus(From, Event, To) env.info("FF checking warehouse status of airbase "..self.homebase:GetName()) - env.info(string.format("FF warehouse at %s: number of stock = %d", self.homebase:GetName(), #self.stock)) - + --env.info(string.format("FF warehouse at %s: number of stock = %d", self.homebase:GetName(), #self.stock)) + self:_DisplayStockItems(self.stock) self:__Status(30) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + + --- On before "Request" event. Checks if the request can be fullfilled. -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Wrapper.Airbase#AIRBASE Airbase Airbase requesting supply. --- @param #string AssetDescriptor Asset that is requested. Can be "templatename", ... --- @param depends AssetDescriptorvalue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, --- @param #number nAssed Number of groups of that asset requested. --- @param #string TransportType Type of transport: "Plane", "Helicopter", "APC" +-- @param #WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. +-- @param depends AssetDescriptorvalue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. +-- @param #number nAsset Number of groups requested that match the asset specification. +-- @param #WAREHOUSE.TransportType TransportType Type of transport. -- @return boolean If true, request is granted. -- -- @usage mywarehouse:Request(AIRBASE:)... @@ -244,78 +250,83 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Airbase, AssetDescriptor, As -- Distance from warehouse to local distance=self.coordinate:Get2DDistance(Airbase:GetCoordinate()) - - -- - local _stockrequest=self._FilterStock(self.stock, AssetDescriptor, AssetDescriptorValue) + + -- Filter the requested assets. + local _stockrequest=self:_FilterStock(self.stock, AssetDescriptor, AssetDescriptorValue) -- Asset is not in stock ==> request denied. - if #_stockrequest==0 then - self:E(self.wid..string.format("Request denied! Asset is currently not in stock.")) + if #_stockrequest < nAsset then + self:E(self.wid..string.format("Request denied! Not enought assets currently in stock. Requested %d < %d in stock.", nAsset, #_stockrequest)) return false end + -- Shortcut local _TT=TransportType:lower() - if _TT==nil then - if AssetDescriptor=="" then - end - end - if TransportType:lower() == "plane" then + if _TT == WAREHOUSE.TransportType.AIRPLANE then + -- here check the availability of transport units! + elseif _TT == WAREHOUSE.TransportType.HELICOPTER then - elseif TransportType:lower() == "helicopter" then + elseif _TT == WAREHOUSE.TransportType.GROUND then - elseif TransportType:lower() == "apc" then + elseif _TT == WAREHOUSE.TransportType.SHIP then - elseif TransportType:lower() == "train" then + elseif _TT == WAREHOUSE.TransportType.TRAIN then - elseif TransportType:lower() == "ship" then - - elseif TransportType:lower() == "self" then + elseif _TT == WAREHOUSE.TransportType.SELFPROPELLED then else self:E(self.wid..string.format("ERROR: unknown transport type requested! type = %s", tostring(TransportType))) end + return true end ---- On after "Request" event. +--- On before "Request" event. Checks if the request can be fullfilled. -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Wrapper.Airbase#AIRBASE Airbase Airbase requesting supply. --- @param #string Asset Asset that is requested. --- @param #number nAssed Number of groups of that asset requested. --- @param #string TransportType Type of transport: "Plane", "Helicopter", "APC" -function WAREHOUSE:onafterRequest(From, Event, To, Airbase, Asset, nAsset, TransportType) - env.info(string.format("FF airbase %s is requesting asset %s from warehouse %s", Airbase:GetName(), Asset, self.homebase:GetName())) +-- @param #WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. +-- @param depends AssetDescriptorvalue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. +-- @param #number nAsset Number of groups requested that match the asset specification. +-- @param #WAREHOUSE.TransportType TransportType Type of transport. +-- @return boolean If true, request is granted. +-- +-- @usage mywarehouse:Request(AIRBASE:)... +function WAREHOUSE:onafterRequest(From, Event, To, Airbase, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType) + env.info(self.wid..string.format("Airbase %s requesting asset %s = %s.", Airbase:GetName(), tostring(AssetDescriptor), tostring(AssetDescriptorValue))) local nAsset=nAsset or 1 - if TransportType=="Air" then + -- Filter the requested assets. + local _stock=self:_FilterStock(self.stock, AssetDescriptor, AssetDescriptorValue) - -- Get a random template from the stock list. - local _chosenone=math.random(#self.stock) --#WAREHOUSE.Stock + -- Get a random template from the stock list. + local _chosenone=math.random(#_stock) - - - -- Select template group name. - --TODO: FILTER HERE! - local template=self.stock[_chosenone].templatename + -- Select template group name. + local template=_stock[_chosenone].templatename + + + if TransportType==WAREHOUSE.TransportType.AIRPLANE then + if template then -- Spawn plane at warehouse homebase. + --TODO: this is wrong. we need to filter the transports and get the right template! local Plane=SPAWN:New(template):SpawnAtAirbase(Airbase, nil, nil, nil, false) if Plane==nil then -- Plane was not spawned correctly. Try again in 60 seconds. - self:__Request( 60, Airbase, Asset, nAsset, TransportType) + self:__Request(60, Airbase, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType) return else - -- Remove chosen plane from list. - table.remove(self.stock,_chosenone) + -- Remove chosen asset from list. + table.remove(self.stock,_stock.pos) end -- New empty cargo set. @@ -354,8 +365,13 @@ function WAREHOUSE:onafterRequest(From, Event, To, Airbase, Asset, nAsset, Trans -- Trigger Delivered event. warehouse:__Delivered(1, group) end - + + else + self:E(self.wid.."ERROR: template does not exist!") end + + else + self:E(self.wid.."ERROR: unknown transport type!") end end @@ -406,25 +422,60 @@ function WAREHOUSE:AddAsset(templategroupname, ngroups, istransport) env.info(string.format("display name = %s", DCSdisplay)) env.info(string.format("category = %s", DCScategory)) env.info(string.format("type = %s", DCStype)) - env.info(string.format("attribute infantry = %s", tostring(group:HasAttribute("Infantry")))) self:E({desc=DCSdesc}) + -- Get generalized attributes. local transport=group:HasAttribute("Transport helicopters") or group:HasAttribute("Transports") or group:HasAttribute("Infantry carriers") local fighter=group:HasAttribute("Fighters") or group:HasAttribute("Interceptors") or group:HasAttribute("Multirole fighters") local tanker=group:HasAttribute("Tankers") local awacs=group:HasAttribute("AWACS") - local apc=group:HasAttribute("Infantry carriers") local artillery=group:HasAttribute("Artillery") - env.info(string.format("attribute transport = %s", tostring(transport))) - env.info(string.format("attribute apc = %s", tostring(apc))) - env.info(string.format("attribute figther = %s", tostring(fighter))) - env.info(string.format("attribute tanker = %s", tostring(tanker))) - env.info(string.format("attribute awacs = %s", tostring(awacs))) - env.info(string.format("attribute artillery = %s", tostring(artillery))) + local infantry=group:HasAttribute("Infantry") + local attackhelicopter=group:HasAttribute("Attack helicopters") + local bomber=group:HasAttribute("Bombers") + local tank=group:HasAttribute("Old Tanks") or group:HasAttribute("Modern Tanks") + + -- Debug output. + env.info(string.format("attribute transport = %s", tostring(transport))) + env.info(string.format("attribute figther = %s", tostring(fighter))) + env.info(string.format("attribute tanker = %s", tostring(tanker))) + env.info(string.format("attribute awacs = %s", tostring(awacs))) + env.info(string.format("attribute artillery = %s", tostring(artillery))) + env.info(string.format("attribute infantry = %s", tostring(infantry))) + env.info(string.format("attribute attackhelo = %s", tostring(attackhelicopter))) + env.info(string.format("attribute bomber = %s", tostring(bomber))) + env.info(string.format("attribute tank = %s", tostring(tank))) + + + local attribute="unknown" --#WAREHOUSE.Attribute + if transport then + attribute=WAREHOUSE.Attribute.TRANSPORT + elseif fighter then + attribute=WAREHOUSE.Attribute.FIGHTER + elseif tanker then + attribute=WAREHOUSE.Attribute.TANKER + elseif awacs then + attribute=WAREHOUSE.Attribute.AWACS + elseif artillery then + attribute=WAREHOUSE.Attribute.ARTILLERY + elseif infantry then + attribute=WAREHOUSE.Attribute.INFANTRY + elseif attackhelicopter then + attribute=WAREHOUSE.Attribute.ATTACKHELICOPTER + elseif bomber then + attribute=WAREHOUSE.Attribute.BOMBER + elseif tank then + attribute=WAREHOUSE.Attribute.TANK + end -- Add this n times to the table. for i=1,n do - table.insert(self.stock, {templatename=templategroupname, category=DCScategory, type=DCStype, transport=transport, fighther=fighter, tanker=tanker, awacs=awacs, artillery=artillery}) + local stockitem={} --#WAREHOUSE.Stockitem + stockitem.templatename=templategroupname + stockitem.category=DCScategory + stockitem.attribute=attribute + stockitem.unittype=DCStype + table.insert(self.stock, stockitem) end else @@ -441,15 +492,19 @@ end --- Filter stock assets by table entry. -- @param #WAREHOUSE self --- @param #WAREHOUSE.Stock stock +-- @param #table stock Table holding all assets in stock of the warehouse. Each entry is of type @{#WAREHOUSE.Stockitem}. -- @param #string item Descriptor --- @param depends value +-- @param depends value Value of the descriptor. +-- @return #table Filtered stock items table. function WAREHOUSE:_FilterStock(stock, item, value) + -- Filtered array. local filtered={} - for _,_stock in pairs(stock) do + -- Loop over stock items. + for _i,_stock in ipairs(stock) do if _stock[item]==value then + _stock.pos=_i table.insert(filtered, _stock) end end @@ -457,6 +512,21 @@ function WAREHOUSE:_FilterStock(stock, item, value) return filtered end +--- Filter stock assets by table entry. +-- @param #WAREHOUSE self +-- @param #table stock Table holding all assets in stock of the warehouse. Each entry is of type @{#WAREHOUSE.Stockitem}. +function WAREHOUSE:_DisplayStockItems(stock) + + local text=self.wid..string.format("Warehouse %s stock assets:\n", self.homebase:GetName()) + for _,_stock in pairs(stock) do + local mystock=_stock --#WAREHOUSE.Stockitem + text=text..string.format("template = %s, category = %d, unittype = %s, attribute = %s\n", mystock.templatename, mystock.category, mystock.unittype, mystock.attribute) + end + + env.info(text) + MESSAGE:New(text,30):ToAll() +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- From f8b1056c9873cda51b9dd0e3acfe5175b51a6fbd Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 2 Aug 2018 16:35:25 +0200 Subject: [PATCH 230/420] WAREHOUSE --- .../Moose/Functional/Warehouse.lua | 415 ++++++++++++------ Moose Development/Moose/Wrapper/Airbase.lua | 2 +- 2 files changed, 283 insertions(+), 134 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index bb13c9d07..3d90f7c65 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -17,9 +17,13 @@ --- WAREHOUSE class. -- @type WAREHOUSE -- @field #string ClassName Name of the class. +-- @field #boolean Debug If true, send debug messages to all. +-- @field #boolean Report If true, send status messages to coalition. -- @field DCS#Coalition coalition Coalition the warehouse belongs to. --- @field Core.Point#COORDINATE coordinate Coordinate of the warehouse. -- @field Wrapper.Airbase#AIRBASE homebase Airbase the warehouse belongs to. +-- @field Core.Point#COORDINATE coordinate Coordinate of the warehouse. +-- @field Core.Zone#ZONE spawnzone Zone in which assets are spawned. +-- @field #number assetid Unique id of asset items in stock. Essentially a running number starting at one and incremented when a new asset is added. -- @field #table stock Table holding all assets in stock. Table entries are of type @{#WAREHOUSE.Stock}. -- @extends Core.Fsm#FSM @@ -52,14 +56,19 @@ -- @field #WAREHOUSE WAREHOUSE = { ClassName = "WAREHOUSE", + Debug = false, + Report = true, coalition = nil, homebase = nil, coordinate = nil, + spawnzone = nil, + assetid = 0, stock = {}, } --- Item of the warehouse stock table. -- @type WAREHOUSE.Stockitem +-- @field #number id Unique id of the asset. -- @field #string templatename Name of the template group. -- @field DCS#Group.Category category Category of the group. -- @field #string unittype Type of the first unit of the group as obtained by the Object.getTypeName() DCS API function. @@ -68,6 +77,7 @@ WAREHOUSE = { --- Descriptors enumerator describing the type of the asset in stock. -- @type WAREHOUSE.Descriptor WAREHOUSE.Descriptor = { + ID="id", TEMPLATENAME="templatename", CATEGORY="category", UNITTYPE="unittype", @@ -77,7 +87,9 @@ WAREHOUSE.Descriptor = { --- Warehouse unit categories. These are used for -- @type WAREHOUSE.Attribute WAREHOUSE.Attribute = { - TRANSPORT="transport", + TRANSPORT_PLANE="transportplane", + TRANSPORT_HELO="transporthelo", + TRANSPORT_APC="transportapc", FIGHTER="fighter", TANKER="tanker", AWACS="awacs", @@ -86,15 +98,17 @@ WAREHOUSE.Attribute = { INFANTRY="infantry", BOMBER="bomber", TANK="tank", + TRUCK="truck", + OTHER="other", } --- Cargo transport type. -- @type WAREHOUSE.TransportType -- @field #string AIRPLANE plane blabla WAREHOUSE.TransportType = { - AIRPLANE = "plane", - HELICOPTER = "helo", - GROUND = "ground", + AIRPLANE = "transportplane", + HELICOPTER = "transporthelo", + APC = "transportapc", SHIP = "ship", TRAIN = "train", SELFPROPELLED = "selfporpelled", @@ -134,7 +148,14 @@ function WAREHOUSE:NewAirbase(airbase) self.homebase=airbase self.coordinate=airbase:GetCoordinate() self.coalition=airbase:GetCoalition() - + + -- Get the closest point on road. + local _road=self.coordinate:GetClosestPointToRoad():GetVec2() + + -- Define the default spawn zone. + self.spawnzone=ZONE:New("Spawnzone",_road, 200) + + -- Add FSM transitions. self:AddTransition("*", "Start", "Running") self:AddTransition("*", "Status", "*") self:AddTransition("*", "Request", "*") @@ -163,9 +184,12 @@ function WAREHOUSE:NewAirbase(airbase) --- Triggers the FSM event "Request". -- @function [parent=#WAREHOUSE] Request -- @param #WAREHOUSE self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. -- @param Wrapper.Airbase#AIRBASE Airbase Airbase requesting supply. -- @param #WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. - -- @param #depends AssetDescriptorvalue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. + -- @param AssetDescriptorValue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. -- @param #number nAsset Number of groups requested that match the asset specification. -- @param #WAREHOUSE.TransportType TransportType Type of transport. -- @return boolean If true, request is granted. @@ -175,11 +199,14 @@ function WAREHOUSE:NewAirbase(airbase) --- Triggers the FSM event "Request" after a delay. -- @function [parent=#WAREHOUSE] __Request -- @param #WAREHOUSE self - -- @param #number delay Delay in seconds. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. -- @param Wrapper.Airbase#AIRBASE Airbase Airbase requesting supply. - -- @param #string Asset Asset that is requested. - -- @param #number nAsset Number of assets requested. Default 1. - -- @param #string TransportType Type of transport: "Plane", "Helicopter", "APC" + -- @param #WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. + -- @param AssetDescriptorValue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. + -- @param #number nAsset Number of groups requested that match the asset specification. + -- @param #WAREHOUSE.TransportType TransportType Type of transport. --- Triggers the FSM event "Delivered". -- @function [parent=#WAREHOUSE] Delivered @@ -240,14 +267,16 @@ end -- @param #string To To state. -- @param Wrapper.Airbase#AIRBASE Airbase Airbase requesting supply. -- @param #WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. --- @param depends AssetDescriptorvalue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. +-- @param AssetDescriptorValue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. -- @param #number nAsset Number of groups requested that match the asset specification. -- @param #WAREHOUSE.TransportType TransportType Type of transport. --- @return boolean If true, request is granted. --- --- @usage mywarehouse:Request(AIRBASE:)... +-- @return #boolean If true, request is granted. function WAREHOUSE:onbeforeRequest(From, Event, To, Airbase, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType) + -- Default. + nAsset=nAsset or 1 + TransportType=TransportType or WAREHOUSE.TransportType.SELFPROPELLED + -- Distance from warehouse to local distance=self.coordinate:Get2DDistance(Airbase:GetCoordinate()) @@ -256,120 +285,177 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Airbase, AssetDescriptor, As -- Asset is not in stock ==> request denied. if #_stockrequest < nAsset then - self:E(self.wid..string.format("Request denied! Not enought assets currently in stock. Requested %d < %d in stock.", nAsset, #_stockrequest)) + local text=string.format("Request denied! Not enought assets currently in stock. Requested %d < %d in stock.", nAsset, #_stockrequest) + MESSAGE:New(text, 10):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:E(self.wid..text) return false end + -- Get the attibute of the requested asset. + local _stockitem=_stockrequest[1] --#WAREHOUSE.Stockitem + local _assetattribute=self:_GetAttribute(_stockitem.templatename) + + --if _assetattribute==WAREHOUSE.Attribute. + -- Shortcut local _TT=TransportType:lower() + local _instock + -- Check the availability of transport units. if _TT == WAREHOUSE.TransportType.AIRPLANE then - -- here check the availability of transport units! + _instock=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.TRANSPORT_PLANE) elseif _TT == WAREHOUSE.TransportType.HELICOPTER then - + _instock=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.TRANSPORT_HELO) elseif _TT == WAREHOUSE.TransportType.GROUND then - + _instock=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.TRANSPORT_APC) elseif _TT == WAREHOUSE.TransportType.SHIP then - + _instock=0 elseif _TT == WAREHOUSE.TransportType.TRAIN then - + _instock=0 elseif _TT == WAREHOUSE.TransportType.SELFPROPELLED then - + _instock=_stockrequest else self:E(self.wid..string.format("ERROR: unknown transport type requested! type = %s", tostring(TransportType))) + return false end + + if #_instock==0 then + local text=string.format("Request denied! No transport unit currently available.") + MESSAGE:New(text, 10):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:E(self.wid..text) + return false + end + return true end ---- On before "Request" event. Checks if the request can be fullfilled. +--- On after "Request" event. Initiates the transport of the assets to the requesting airbase. -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Wrapper.Airbase#AIRBASE Airbase Airbase requesting supply. -- @param #WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. --- @param depends AssetDescriptorvalue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. +-- @param AssetDescriptorValue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. -- @param #number nAsset Number of groups requested that match the asset specification. -- @param #WAREHOUSE.TransportType TransportType Type of transport. --- @return boolean If true, request is granted. --- --- @usage mywarehouse:Request(AIRBASE:)... function WAREHOUSE:onafterRequest(From, Event, To, Airbase, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType) env.info(self.wid..string.format("Airbase %s requesting asset %s = %s.", Airbase:GetName(), tostring(AssetDescriptor), tostring(AssetDescriptorValue))) - local nAsset=nAsset or 1 - -- Filter the requested assets. - local _stock=self:_FilterStock(self.stock, AssetDescriptor, AssetDescriptorValue) + local _assetstock=self:_FilterStock(self.stock, AssetDescriptor, AssetDescriptorValue) -- Get a random template from the stock list. - local _chosenone=math.random(#_stock) + local _chosenone=math.random(#_assetstock) - -- Select template group name. - local template=_stock[_chosenone].templatename + -- Select asset template group name. + local assettemplate=_assetstock[_chosenone].templatename + + + -- New empty cargo set. + local CargoGroups = SET_CARGO:New() + + -- Spawn the assets. + local _delid={} + for i=1,nAsset do + + -- Get stock item. + local _assetitem=_assetstock[i] --#WAREHOUSE.Stockitem + table.insert(_delid,_assetitem.id) + + -- Spawn group in spawn zone. + local spawn=SPAWN(_assetitem.templatename) + local spawngroup=spawn:SpawnFromVec3(self.spawnzone:GetRandomPointVec3()) + + -- Add spawned group to cargo group object. + --TODO: check near and load radius. + local cargogroup = CARGO_GROUP:New(spawngroup, "Infantry", string.format( "Infantry Platoon %d", i), 5000, 35) + CargoGroups:AddCargo(cargogroup) + end + + -- Delete spawned items from warehouse stock. + for _,_id in pairs(_delid) do + self:_DeleteStockItem(_id) + end + + --[[ + -- Spawn requested assets. + local spawn=SPAWN:New("Infantry Platoon Alpha") + self.homebase:GetZone():GetRandomCoordinate(inner,outer) + local spawngroup=spawn:SpawnFromVec3(self.homebase:GetZone():GetRandomPointVec3(100,500)) + for i=1,nAsset do + local spawngroup=spawn:SpawnFromVec3(self.homebase:GetZone():GetRandomPointVec3(100,500)) + local cargogroup = CARGO_GROUP:New(spawngroup, "Infantry", string.format( "Infantry Platoon %d", i), 5000, 35) + CargoGroups:AddCargo(cargogroup) + end + ]] + + + -- Filter the requested assets. + local _transportstock + local _transportitem --#WAREHOUSE.Stockitem + if TransportType~=WAREHOUSE.TransportType.SELFPROPELLED then + _transportstock=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, TransportType) + _chosenone=math.random(#_transportstock) + -- Select asset template group name. + _transportitem=_transportstock[_chosenone] + end if TransportType==WAREHOUSE.TransportType.AIRPLANE then - - - if template then - -- Spawn plane at warehouse homebase. - --TODO: this is wrong. we need to filter the transports and get the right template! - local Plane=SPAWN:New(template):SpawnAtAirbase(Airbase, nil, nil, nil, false) - - if Plane==nil then - -- Plane was not spawned correctly. Try again in 60 seconds. - self:__Request(60, Airbase, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType) - return - else - -- Remove chosen asset from list. - table.remove(self.stock,_stock.pos) - end - - -- New empty cargo set. - local CargoGroups = SET_CARGO:New() - - -- Spawn requested assets. - local spawn=SPAWN:New("Infantry Platoon Alpha") - - for i=1,nAsset do - local spawngroup=spawn:SpawnFromVec3(self.homebase:GetZone():GetRandomPointVec3(100,500)) - local cargogroup = CARGO_GROUP:New(spawngroup, "Infantry", string.format( "Infantry Platoon %d", i), 5000, 35) - CargoGroups:AddCargo(cargogroup) - end - - -- Define cargo airplane. - local CargoPlane = AI_CARGO_AIRPLANE:New(Plane, CargoGroups) - - -- Pickup cargo at homebase. - CargoPlane:__Pickup(5, self.homebase) - - -- Set warehouse state so that we can retreive it later. - Plane:SetState(Plane, "WAREHOUSE", self) - - --- Once the cargo was loaded start off to deploy airbase. - function CargoPlane:OnAfterLoaded(Airplane, From, Event, To) - CargoPlane:__Deploy(10, Airbase, 500) - end - - --- Function called when cargo has arrived and was unloaded. - function CargoPlane:OnAfterUnloaded(Airplane, From, Event, To) - - local group=CargoPlane.Cargo:GetObject() - local Airplane=Airplane --Wrapper.Group#GROUP - local warehouse=Airplane:GetState(Airplane, "WAREHOUSE") --#WAREHOUSE - - -- Trigger Delivered event. - warehouse:__Delivered(1, group) - end - + -- Spawn plane at warehouse homebase. + --TODO: Check available parking spots in onbefore! + local Plane=SPAWN:New(_transportitem.templatename):SpawnAtAirbase(Airbase, SPAWN.Takeoff.Cold, nil, AIRBASE.TerminalType.OpenBig, false) + + if Plane==nil then + -- Plane was not spawned correctly. Try again in 60 seconds. + local text="Technical problems with the transport plane occurred. Request was cancelled! Try again later." + --self:__Request(60, Airbase, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType) + --TODO: despawn units and but them back into the warehouse. + return else - self:E(self.wid.."ERROR: template does not exist!") + -- Remove chosen transport asset from list. + self:_DeleteStockItem() end + -- Define cargo airplane. + local CargoPlane = AI_CARGO_AIRPLANE:New(Plane, CargoGroups) + + -- Pickup cargo at homebase. + CargoPlane:__Pickup(5, self.homebase) + + -- Set warehouse state so that we can retreive it later. + Plane:SetState(Plane, "WAREHOUSE", self) + + --- Once the cargo was loaded start off to deploy airbase. + function CargoPlane:OnAfterLoaded(Airplane, From, Event, To) + CargoPlane:__Deploy(10, Airbase, 500) + end + + --- Function called when cargo has arrived and was unloaded. + function CargoPlane:OnAfterUnloaded(Airplane, From, Event, To) + + local group=CargoPlane.Cargo:GetObject() + local Airplane=Airplane --Wrapper.Group#GROUP + local warehouse=Airplane:GetState(Airplane, "WAREHOUSE") --#WAREHOUSE + + -- Trigger Delivered event. + warehouse:__Delivered(1, group) + end + + elseif TransportType==WAREHOUSE.TransportType.HELICOPTER then + + elseif TransportType==WAREHOUSE.TransportType.APC then + + elseif TransportType==WAREHOUSE.TransportType.TRAIN then + + elseif TransportType==WAREHOUSE.TransportType.SHIP then + + elseif TransportType==WAREHOUSE.TransportType.SELFPROPELLED then + else self:E(self.wid.."ERROR: unknown transport type!") end @@ -405,6 +491,7 @@ end -- @return #WAREHOUSE self function WAREHOUSE:AddAsset(templategroupname, ngroups, istransport) + -- Set default. local n=ngroups or 1 local group=GROUP:FindByName(templategroupname) @@ -424,57 +511,17 @@ function WAREHOUSE:AddAsset(templategroupname, ngroups, istransport) env.info(string.format("type = %s", DCStype)) self:E({desc=DCSdesc}) - -- Get generalized attributes. - local transport=group:HasAttribute("Transport helicopters") or group:HasAttribute("Transports") or group:HasAttribute("Infantry carriers") - local fighter=group:HasAttribute("Fighters") or group:HasAttribute("Interceptors") or group:HasAttribute("Multirole fighters") - local tanker=group:HasAttribute("Tankers") - local awacs=group:HasAttribute("AWACS") - local artillery=group:HasAttribute("Artillery") - local infantry=group:HasAttribute("Infantry") - local attackhelicopter=group:HasAttribute("Attack helicopters") - local bomber=group:HasAttribute("Bombers") - local tank=group:HasAttribute("Old Tanks") or group:HasAttribute("Modern Tanks") - - -- Debug output. - env.info(string.format("attribute transport = %s", tostring(transport))) - env.info(string.format("attribute figther = %s", tostring(fighter))) - env.info(string.format("attribute tanker = %s", tostring(tanker))) - env.info(string.format("attribute awacs = %s", tostring(awacs))) - env.info(string.format("attribute artillery = %s", tostring(artillery))) - env.info(string.format("attribute infantry = %s", tostring(infantry))) - env.info(string.format("attribute attackhelo = %s", tostring(attackhelicopter))) - env.info(string.format("attribute bomber = %s", tostring(bomber))) - env.info(string.format("attribute tank = %s", tostring(tank))) - - - local attribute="unknown" --#WAREHOUSE.Attribute - if transport then - attribute=WAREHOUSE.Attribute.TRANSPORT - elseif fighter then - attribute=WAREHOUSE.Attribute.FIGHTER - elseif tanker then - attribute=WAREHOUSE.Attribute.TANKER - elseif awacs then - attribute=WAREHOUSE.Attribute.AWACS - elseif artillery then - attribute=WAREHOUSE.Attribute.ARTILLERY - elseif infantry then - attribute=WAREHOUSE.Attribute.INFANTRY - elseif attackhelicopter then - attribute=WAREHOUSE.Attribute.ATTACKHELICOPTER - elseif bomber then - attribute=WAREHOUSE.Attribute.BOMBER - elseif tank then - attribute=WAREHOUSE.Attribute.TANK - end + local attribute=self:_GetAttribute(templategroupname) -- Add this n times to the table. for i=1,n do local stockitem={} --#WAREHOUSE.Stockitem + self.assetid=self.assetid+1 + stockitem.id=self.assetid stockitem.templatename=templategroupname stockitem.category=DCScategory - stockitem.attribute=attribute stockitem.unittype=DCStype + stockitem.attribute=attribute table.insert(self.stock, stockitem) end @@ -494,7 +541,7 @@ end -- @param #WAREHOUSE self -- @param #table stock Table holding all assets in stock of the warehouse. Each entry is of type @{#WAREHOUSE.Stockitem}. -- @param #string item Descriptor --- @param depends value Value of the descriptor. +-- @param value Value of the descriptor. -- @return #table Filtered stock items table. function WAREHOUSE:_FilterStock(stock, item, value) @@ -524,7 +571,109 @@ function WAREHOUSE:_DisplayStockItems(stock) end env.info(text) - MESSAGE:New(text,30):ToAll() + MESSAGE:New(text, 10):ToAll() +end + +--- Check if a group has a generalized attribute. +-- @param #WAREHOUSE self +-- @param #string groupname Name of the group. +-- @param #WAREHOUSE.Attribute attribute Attribute to check. +-- @return #boolean True if group has the specified attribute. +function WAREHOUSE:_HasAttribute(groupname, attribute) + + local group=GROUP:FindByName(groupname) + + if group then + local groupattribute=self:_HasAttribute(groupname,attribute) + return groupattribute==attribute + end + + return false +end + +--- Get the generalized attribute of a group. +-- @param #WAREHOUSE self +-- @param #string groupname Name of the group. +-- @return #WAREHOUSE.Attribute Generalized attribute of the group. +function WAREHOUSE:_GetAttribute(groupname) + + local group=GROUP:FindByName(groupname) + + local attribute=WAREHOUSE.Attribute.OTHER --#WAREHOUSE.Attribute + + if group then + + -- Get generalized attributes. + -- Transports: Helos, planes and APCs + local transportplane=group:HasAttribute("Transports") and group:HasAttribute("Planes") + local transporthelo=group:HasAttribute("Transport helicopters") + local transportapc=group:HasAttribute("Infantry carriers") + local fighter=group:HasAttribute("Fighters") or group:HasAttribute("Interceptors") or group:HasAttribute("Multirole fighters") + local tanker=group:HasAttribute("Tankers") + local awacs=group:HasAttribute("AWACS") + local artillery=group:HasAttribute("Artillery") + local infantry=group:HasAttribute("Infantry") + local attackhelicopter=group:HasAttribute("Attack helicopters") + local bomber=group:HasAttribute("Bombers") + local tank=group:HasAttribute("Old Tanks") or group:HasAttribute("Modern Tanks") + local truck=group:HasAttribute("Trucks") + + -- Debug output. + env.info(string.format("transport pane = %s", tostring(transportplane))) + env.info(string.format("transport helo = %s", tostring(transporthelo))) + env.info(string.format("transport apc = %s", tostring(transportapc))) + env.info(string.format("figther = %s", tostring(fighter))) + env.info(string.format("tanker = %s", tostring(tanker))) + env.info(string.format("awacs = %s", tostring(awacs))) + env.info(string.format("artillery = %s", tostring(artillery))) + env.info(string.format("infantry = %s", tostring(infantry))) + env.info(string.format("attack helo = %s", tostring(attackhelicopter))) + env.info(string.format("bomber = %s", tostring(bomber))) + env.info(string.format("tank = %s", tostring(tank))) + env.info(string.format("truck = %s", tostring(truck))) + + if transportplane then + attribute=WAREHOUSE.Attribute.TRANSPORT_PLANE + elseif transporthelo then + attribute=WAREHOUSE.Attribute.TRANSPORT_HELO + elseif transportapc then + attribute=WAREHOUSE.Attribute.TRANSPORT_APC + elseif fighter then + attribute=WAREHOUSE.Attribute.FIGHTER + elseif tanker then + attribute=WAREHOUSE.Attribute.TANKER + elseif awacs then + attribute=WAREHOUSE.Attribute.AWACS + elseif artillery then + attribute=WAREHOUSE.Attribute.ARTILLERY + elseif infantry then + attribute=WAREHOUSE.Attribute.INFANTRY + elseif attackhelicopter then + attribute=WAREHOUSE.Attribute.ATTACKHELICOPTER + elseif bomber then + attribute=WAREHOUSE.Attribute.BOMBER + elseif tank then + attribute=WAREHOUSE.Attribute.TANK + elseif truck then + attribute=WAREHOUSE.Attribute.TRUCK + else + attribute=WAREHOUSE.Attribute.OTHER + end + + end + + return attribute +end + +--- Delete item from stock. +-- @param #WAREHOUSE self +-- @param #number uid The id of the item to be deleted. +function WAREHOUSE:_DeleteStockItem(uid) + for _i,_item in pairs(self.stock) do + if _item.id==uid then + self.stock[_i]=nil + end + end end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 9c32fb42c..ed6f169d5 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -349,7 +349,7 @@ function AIRBASE.GetAllAirbases(coalition) local airbases={} for _,airbase in pairs(_DATABASE.AIRBASES) do - if (coalition~=nil and self:GetCoalition()==coalition) or coalition==nil then + if (coalition~=nil and airbase:GetCoalition()==coalition) or coalition==nil then table.insert(airbases, airbase) end end From 4a424dd3d8dab18cfe57a0a812af863597a2da70 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Fri, 3 Aug 2018 00:01:41 +0200 Subject: [PATCH 231/420] WAREHOUSE --- .../Moose/AI/AI_Cargo_Airplane.lua | 5 +- .../Moose/Functional/Warehouse.lua | 146 ++++++++++++------ 2 files changed, 99 insertions(+), 52 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index b1422cb11..f1fdfab54 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -274,7 +274,8 @@ end -- @param Wrapper.Point#COORDINATE Coordinate function AI_CARGO_AIRPLANE:onafterLoad( Airplane, From, Event, To, Coordinate ) - if Airplane and Airplane:IsAlive() then + if Airplane and Airplane:IsAlive()==true or Airplane:IsAlive()==false then + --if Airplane then for _, Cargo in pairs( self.CargoSet:GetSet() ) do local Cargo=Cargo --Cargo.Cargo#CARGO @@ -472,6 +473,8 @@ function AI_CARGO_AIRPLANE:Route( Airplane, Airbase, Speed ) self:T3( Points ) Template.route.points = Points + + Template.uncontrolled=false --self:Respawn( Template ) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 3d90f7c65..6c983738a 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -22,7 +22,8 @@ -- @field DCS#Coalition coalition Coalition the warehouse belongs to. -- @field Wrapper.Airbase#AIRBASE homebase Airbase the warehouse belongs to. -- @field Core.Point#COORDINATE coordinate Coordinate of the warehouse. --- @field Core.Zone#ZONE spawnzone Zone in which assets are spawned. +-- @field Core.Zone#ZONE spawnzone Zone in which assets are spawned. +-- @field #number markerid ID of the warehouse marker at the airbase. -- @field #number assetid Unique id of asset items in stock. Essentially a running number starting at one and incremented when a new asset is added. -- @field #table stock Table holding all assets in stock. Table entries are of type @{#WAREHOUSE.Stock}. -- @extends Core.Fsm#FSM @@ -62,6 +63,7 @@ WAREHOUSE = { homebase = nil, coordinate = nil, spawnzone = nil, + markerid = nil, assetid = 0, stock = {}, } @@ -153,7 +155,9 @@ function WAREHOUSE:NewAirbase(airbase) local _road=self.coordinate:GetClosestPointToRoad():GetVec2() -- Define the default spawn zone. - self.spawnzone=ZONE:New("Spawnzone",_road, 200) + self.spawnzone=ZONE_RADIUS:New("Spawnzone",_road, 200) + self.spawnzone:BoundZone(60,country.id.GERMANY) + self.spawnzone:GetCoordinate():MarkToAll("Spawnzone") -- Add FSM transitions. self:AddTransition("*", "Start", "Running") @@ -232,14 +236,15 @@ end -- @param #string Event Event. -- @param #string To To state. function WAREHOUSE:onafterStart(From, Event, To) - env.info("FF starting warehouse at airbase "..self.homebase:GetName()) + self:E(self.wid..string.format("Starting warehouse at airbase %s.", self.homebase:GetName())) -- handle events -- event takeoff -- event landing -- event crash/dead -- event base captured - self:__Status(-5) + + self:__Status(5) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -250,10 +255,32 @@ end -- @param #string Event Event. -- @param #string To To state. function WAREHOUSE:onafterStatus(From, Event, To) - env.info("FF checking warehouse status of airbase "..self.homebase:GetName()) + self:E(self.wid..string.format("Checking warehouse status of airbase %s", self.homebase:GetName())) - --env.info(string.format("FF warehouse at %s: number of stock = %d", self.homebase:GetName(), #self.stock)) - self:_DisplayStockItems(self.stock) + -- Create a mark with the current assets in stock. + if self.markerid~=nil then + trigger.action.removeMark(self.markerid) + end + local marktext="Warehouse stock:\n" + local text="Warehouse stock:\n" + + local _data=self:GetStockInfo(self.stock) + for _attribute,_count in pairs(_data) do + marktext=marktext..string.format("%s=%d, ", _attribute,_count) -- Dont use \n because too many make DCS crash! + text=text..string.format("%s = %d\n", _attribute,_count) + end + self.markerid=self.coordinate:MarkToCoalition(marktext, self.coalition, true) + + -- Debug output. + self:E(self.wid..text) + MESSAGE:New(text, 10):ToAllIf(self.Debug) + + -- Display complete list of stock itmes. + if self.Debug then + --self:_DisplayStockItems(self.stock) + end + + -- Call status again in 30 sec. self:__Status(30) end @@ -272,7 +299,8 @@ end -- @param #WAREHOUSE.TransportType TransportType Type of transport. -- @return #boolean If true, request is granted. function WAREHOUSE:onbeforeRequest(From, Event, To, Airbase, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType) - + env.info(self.wid..string.format("Airbase %s requesting asset %s = %s.", Airbase:GetName(), tostring(AssetDescriptor), tostring(AssetDescriptorValue))) + -- Default. nAsset=nAsset or 1 TransportType=TransportType or WAREHOUSE.TransportType.SELFPROPELLED @@ -294,13 +322,11 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Airbase, AssetDescriptor, As -- Get the attibute of the requested asset. local _stockitem=_stockrequest[1] --#WAREHOUSE.Stockitem local _assetattribute=self:_GetAttribute(_stockitem.templatename) - - --if _assetattribute==WAREHOUSE.Attribute. - + -- Shortcut - local _TT=TransportType:lower() - local _instock + local _instock=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, TransportType) + --[[ -- Check the availability of transport units. if _TT == WAREHOUSE.TransportType.AIRPLANE then _instock=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.TRANSPORT_PLANE) @@ -318,15 +344,16 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Airbase, AssetDescriptor, As self:E(self.wid..string.format("ERROR: unknown transport type requested! type = %s", tostring(TransportType))) return false end + ]] - if #_instock==0 then + -- Check that a transport unit is available. + if #_instock==0 and TransportType~=WAREHOUSE.TransportType.SELFPROPELLED then local text=string.format("Request denied! No transport unit currently available.") MESSAGE:New(text, 10):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:E(self.wid..text) return false end - return true end @@ -343,54 +370,43 @@ end -- @param #WAREHOUSE.TransportType TransportType Type of transport. function WAREHOUSE:onafterRequest(From, Event, To, Airbase, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType) env.info(self.wid..string.format("Airbase %s requesting asset %s = %s.", Airbase:GetName(), tostring(AssetDescriptor), tostring(AssetDescriptorValue))) - + + -- Filter the requested assets. local _assetstock=self:_FilterStock(self.stock, AssetDescriptor, AssetDescriptorValue) - -- Get a random template from the stock list. - local _chosenone=math.random(#_assetstock) - - -- Select asset template group name. - local assettemplate=_assetstock[_chosenone].templatename - - -- New empty cargo set. local CargoGroups = SET_CARGO:New() - + -- Spawn the assets. local _delid={} + local _spawngroups={} for i=1,nAsset do -- Get stock item. local _assetitem=_assetstock[i] --#WAREHOUSE.Stockitem table.insert(_delid,_assetitem.id) - -- Spawn group in spawn zone. - local spawn=SPAWN(_assetitem.templatename) - local spawngroup=spawn:SpawnFromVec3(self.spawnzone:GetRandomPointVec3()) - - -- Add spawned group to cargo group object. - --TODO: check near and load radius. - local cargogroup = CARGO_GROUP:New(spawngroup, "Infantry", string.format( "Infantry Platoon %d", i), 5000, 35) - CargoGroups:AddCargo(cargogroup) + -- Find a random point within the spawn zone. + local spawnvec3=self.spawnzone:GetRandomVec3() + local spawncoord=COORDINATE:NewFromVec3(spawnvec3) + spawncoord:MarkToAll(string.format("spawnpoint %d",i)) + + -- Spawn with ALIAS here or DCS crashes! + _spawngroups[i]=SPAWN:NewWithAlias(_assetitem.templatename,string.format("%s_%d", _assetitem.templatename,i)):SpawnFromVec3(spawnvec3) end + -- Add spawned groups to cargo group object. + for _i,_spawngroup in pairs(_spawngroups) do + --TODO: check near and load radius. + local cargogroup = CARGO_GROUP:New(_spawngroup, AssetDescriptorValue, string.format("%s %d",AssetDescriptorValue, _i), 5000, 35) + CargoGroups:AddCargo(cargogroup) + end + -- Delete spawned items from warehouse stock. for _,_id in pairs(_delid) do - self:_DeleteStockItem(_id) + self:_DeleteStockItem(_id) end - - --[[ - -- Spawn requested assets. - local spawn=SPAWN:New("Infantry Platoon Alpha") - self.homebase:GetZone():GetRandomCoordinate(inner,outer) - local spawngroup=spawn:SpawnFromVec3(self.homebase:GetZone():GetRandomPointVec3(100,500)) - for i=1,nAsset do - local spawngroup=spawn:SpawnFromVec3(self.homebase:GetZone():GetRandomPointVec3(100,500)) - local cargogroup = CARGO_GROUP:New(spawngroup, "Infantry", string.format( "Infantry Platoon %d", i), 5000, 35) - CargoGroups:AddCargo(cargogroup) - end - ]] -- Filter the requested assets. @@ -398,7 +414,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Airbase, AssetDescriptor, Ass local _transportitem --#WAREHOUSE.Stockitem if TransportType~=WAREHOUSE.TransportType.SELFPROPELLED then _transportstock=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, TransportType) - _chosenone=math.random(#_transportstock) + local _chosenone=math.random(#_transportstock) -- Select asset template group name. _transportitem=_transportstock[_chosenone] end @@ -408,7 +424,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Airbase, AssetDescriptor, Ass -- Spawn plane at warehouse homebase. --TODO: Check available parking spots in onbefore! - local Plane=SPAWN:New(_transportitem.templatename):SpawnAtAirbase(Airbase, SPAWN.Takeoff.Cold, nil, AIRBASE.TerminalType.OpenBig, false) + local Plane=SPAWN:New(_transportitem.templatename):InitUnControlled(true):SpawnAtAirbase(self.homebase, SPAWN.Takeoff.Cold, nil, AIRBASE.TerminalType.OpenBig, false) if Plane==nil then -- Plane was not spawned correctly. Try again in 60 seconds. @@ -421,11 +437,14 @@ function WAREHOUSE:onafterRequest(From, Event, To, Airbase, AssetDescriptor, Ass self:_DeleteStockItem() end - -- Define cargo airplane. + -- Define cargo airplane object. local CargoPlane = AI_CARGO_AIRPLANE:New(Plane, CargoGroups) + CargoPlane.Airbase=self.homebase -- Pickup cargo at homebase. - CargoPlane:__Pickup(5, self.homebase) + --CargoPlane:Pickup(self.homebase) + --CargoPlane:__Landed(1) + CargoPlane:__Load(1, Plane:GetCoordinate()) -- Set warehouse state so that we can retreive it later. Plane:SetState(Plane, "WAREHOUSE", self) @@ -618,7 +637,8 @@ function WAREHOUSE:_GetAttribute(groupname) local tank=group:HasAttribute("Old Tanks") or group:HasAttribute("Modern Tanks") local truck=group:HasAttribute("Trucks") - -- Debug output. + -- Debug output. + --[[ env.info(string.format("transport pane = %s", tostring(transportplane))) env.info(string.format("transport helo = %s", tostring(transporthelo))) env.info(string.format("transport apc = %s", tostring(transportapc))) @@ -631,7 +651,8 @@ function WAREHOUSE:_GetAttribute(groupname) env.info(string.format("bomber = %s", tostring(bomber))) env.info(string.format("tank = %s", tostring(tank))) env.info(string.format("truck = %s", tostring(truck))) - + ]] + if transportplane then attribute=WAREHOUSE.Attribute.TRANSPORT_PLANE elseif transporthelo then @@ -665,6 +686,29 @@ function WAREHOUSE:_GetAttribute(groupname) return attribute end +--- Returns the number of assets for each generalized attribute. +-- @param #WAREHOUSE self +-- @param #table stock The stock of the warehouse. +-- @return #table Data table holding the numbers. +function WAREHOUSE:GetStockInfo(stock) + + local _data={} + for _j,_attribute in pairs(WAREHOUSE.Attribute) do + + local n=0 + for _i,_item in pairs(stock) do + local _ite=_item --#WAREHOUSE.Stockitem + if _ite.attribute==_attribute then + n=n+1 + end + end + + _data[_attribute]=n + end + + return _data +end + --- Delete item from stock. -- @param #WAREHOUSE self -- @param #number uid The id of the item to be deleted. From 77f78260843eb8a0854ae8b339279e8dce50a9aa Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Fri, 3 Aug 2018 16:02:16 +0200 Subject: [PATCH 232/420] WAREHOUSE --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 4 +- .../Moose/AI/AI_Cargo_Helicopter.lua | 23 ++-- .../Moose/Functional/Warehouse.lua | 118 +++++++++++++++--- 3 files changed, 117 insertions(+), 28 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index a69f0aa27..13f88324e 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -139,7 +139,7 @@ function AI_CARGO_APC:New( APC, CargoSet, CombatRadius ) -- @function [parent=#AI_CARGO_APC] __Pickup -- @param #AI_CARGO_APC self -- @param #number Delay - -- @param Core.Point#COORDINATE Coordinate + -- @param Core.Point#COORDINATE Coordinate Pickup place. If not given, loading starts at the current location. -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. --- Deploy Handler OnBefore for AI_CARGO_APC @@ -170,8 +170,8 @@ function AI_CARGO_APC:New( APC, CargoSet, CombatRadius ) --- Deploy Asynchronous Trigger for AI_CARGO_APC -- @function [parent=#AI_CARGO_APC] __Deploy -- @param #AI_CARGO_APC self - -- @param Core.Point#COORDINATE Coordinate -- @param #number Delay + -- @param Core.Point#COORDINATE Coordinate -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 7a272178a..54d2050c8 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -73,17 +73,20 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) -- @param #string Event -- @param #string To -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. --- Pickup Trigger for AI_CARGO_HELICOPTER -- @function [parent=#AI_CARGO_HELICOPTER] Pickup -- @param #AI_CARGO_HELICOPTER self -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. --- Pickup Asynchronous Trigger for AI_CARGO_HELICOPTER -- @function [parent=#AI_CARGO_HELICOPTER] __Pickup -- @param #AI_CARGO_HELICOPTER self - -- @param #number Delay + -- @param #number Delay Delay in seconds. -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h to go to the pickup coordinate. Default is 50% of max possible speed the unit can go. --- Deploy Handler OnBefore for AI_CARGO_HELICOPTER -- @function [parent=#AI_CARGO_HELICOPTER] OnBeforeDeploy @@ -91,7 +94,8 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) -- @param #string From -- @param #string Event -- @param #string To - -- @param Core.Point#COORDINATE Coordinate + -- @param Core.Point#COORDINATE Coordinate Place at which cargo is deployed. + -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. -- @return #boolean --- Deploy Handler OnAfter for AI_CARGO_HELICOPTER @@ -101,18 +105,21 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) -- @param #string Event -- @param #string To -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. --- Deploy Trigger for AI_CARGO_HELICOPTER -- @function [parent=#AI_CARGO_HELICOPTER] Deploy -- @param #AI_CARGO_HELICOPTER self - -- @param Core.Point#COORDINATE Coordinate + -- @param Core.Point#COORDINATE Coordinate Place at which the cargo is deployed. + -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. --- Deploy Asynchronous Trigger for AI_CARGO_HELICOPTER -- @function [parent=#AI_CARGO_HELICOPTER] __Deploy + -- @param #number Delay Delay in seconds. -- @param #AI_CARGO_HELICOPTER self - -- @param Core.Point#COORDINATE Coordinate - -- @param #number Delay - + -- @param Core.Point#COORDINATE Coordinate Place at which the cargo is deployed. + -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. + -- We need to capture the Crash events for the helicopters. -- The helicopter reference is used in the semaphore AI_CARGO_QUEUE. @@ -632,11 +639,11 @@ end --- On after Deploy event. -- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Group#GROUP Helicopter +-- @param Wrapper.Group#GROUP Helicopter Transport helicopter. -- @param From -- @param Event -- @param To --- @param Core.Point#COORDINATE Coordinate +-- @param Core.Point#COORDINATE Coordinate Place at which the cargo is deployed. -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordinate, Speed ) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 6c983738a..48d365019 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -124,7 +124,13 @@ WAREHOUSE.version="0.1.0" -- TODO: Warehuse todo list. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: A lot! +-- TODO: Add event handlers. +-- TODO: Add AI_APC +-- TODO: Add AI_HELICOPTER +-- TODO: Write documentation. +-- TODO: Put active groups into the warehouse. +-- TODO: Spawn warehouse assets as uncontrolled or AI off and activate them when requested. +-- TODO: Handle cases with immobile units. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor(s) @@ -388,32 +394,46 @@ function WAREHOUSE:onafterRequest(From, Event, To, Airbase, AssetDescriptor, Ass table.insert(_delid,_assetitem.id) -- Find a random point within the spawn zone. - local spawnvec3=self.spawnzone:GetRandomVec3() - local spawncoord=COORDINATE:NewFromVec3(spawnvec3) + --local spawnvec3=self.spawnzone:GetRandomVec3 + local spawncoord=self.spawnzone:GetRandomCoordinate() spawncoord:MarkToAll(string.format("spawnpoint %d",i)) -- Spawn with ALIAS here or DCS crashes! - _spawngroups[i]=SPAWN:NewWithAlias(_assetitem.templatename,string.format("%s_%d", _assetitem.templatename,i)):SpawnFromVec3(spawnvec3) + _spawngroups[i]=SPAWN:NewWithAlias(_assetitem.templatename,string.format("%s_%d", _assetitem.templatename,i)):SpawnFromCoordinate(spawncoord) --:SpawnFromVec3(spawnvec3) end - - -- Add spawned groups to cargo group object. - for _i,_spawngroup in pairs(_spawngroups) do - --TODO: check near and load radius. - local cargogroup = CARGO_GROUP:New(_spawngroup, AssetDescriptorValue, string.format("%s %d",AssetDescriptorValue, _i), 5000, 35) - CargoGroups:AddCargo(cargogroup) - end - + -- Delete spawned items from warehouse stock. for _,_id in pairs(_delid) do self:_DeleteStockItem(_id) end + if TransportType==WAREHOUSE.TransportType.SELFPROPELLED then + for _i,_spawngroup in pairs(_spawngroups) do + local group=_spawngroup --Wrapper.Group#GROUP + local ToCoordinate=Airbase:GetZone():GetRandomCoordinate() + group:RouteGroundOnRoad(ToCoordinate) + end + end + + + --TODO: naje nearradius depended on types. + local _loadradius=5000 + local _nearradius=35 + + -- Add spawned groups to cargo group object. + for _i,_spawngroup in pairs(_spawngroups) do + --TODO: check near and load radius. + local cargogroup = CARGO_GROUP:New(_spawngroup, AssetDescriptorValue, string.format("%s %d",AssetDescriptorValue, _i), _loadradius, _nearradius) + CargoGroups:AddCargo(cargogroup) + end + + + -- Filter the requested assets. - local _transportstock local _transportitem --#WAREHOUSE.Stockitem - if TransportType~=WAREHOUSE.TransportType.SELFPROPELLED then - _transportstock=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, TransportType) + if TransportType ~= WAREHOUSE.TransportType.SELFPROPELLED then + local _transportstock=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, TransportType) local _chosenone=math.random(#_transportstock) -- Select asset template group name. _transportitem=_transportstock[_chosenone] @@ -434,7 +454,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Airbase, AssetDescriptor, Ass return else -- Remove chosen transport asset from list. - self:_DeleteStockItem() + self:_DeleteStockItem(_transportitem.id) end -- Define cargo airplane object. @@ -442,8 +462,6 @@ function WAREHOUSE:onafterRequest(From, Event, To, Airbase, AssetDescriptor, Ass CargoPlane.Airbase=self.homebase -- Pickup cargo at homebase. - --CargoPlane:Pickup(self.homebase) - --CargoPlane:__Landed(1) CargoPlane:__Load(1, Plane:GetCoordinate()) -- Set warehouse state so that we can retreive it later. @@ -467,11 +485,75 @@ function WAREHOUSE:onafterRequest(From, Event, To, Airbase, AssetDescriptor, Ass elseif TransportType==WAREHOUSE.TransportType.HELICOPTER then + local Helo=SPAWN:New(_transportitem.templatename):SpawnAtAirbase(self.homebase, SPAWN.Takeoff.Cold, nil, AIRBASE.TerminalType.HelicopterUsable, false) + + if Helo==nil then + -- Plane was not spawned correctly. Try again in 60 seconds. + local text="Technical problems with the transport helicopter occurred. Request was cancelled! Try again later." + return + else + -- Remove chosen transport asset from list. + self:_DeleteStockItem(_transportitem.id) + end + + -- Define cargo airplane object. + local CargoHelo = AI_CARGO_HELICOPTER:New(Helo, CargoGroups) + + -- Pickup cargo from the spawn zone. + CargoHelo:__Pickup(5, self.spawnzone:GetCoordinate()) + + --- Once the cargo was loaded start off to deploy airbase. + function CargoHelo:OnAfterLoaded(Carrier, From, Event, To) + CargoHelo:__Deploy(10, Airbase:GetZone():GetRandomCoordinate()) + end + + --- Function called when cargo has arrived and was unloaded. + function CargoHelo:OnAfterUnloaded(Airplane, From, Event, To) + + local group=CargoHelo.Cargo:GetObject() + local Airplane=Airplane --Wrapper.Group#GROUP + local warehouse=Airplane:GetState(Airplane, "WAREHOUSE") --#WAREHOUSE + + -- Trigger Delivered event. + warehouse:__Delivered(1, group) + end + + elseif TransportType==WAREHOUSE.TransportType.APC then + + -- Spawn APC in spawn zone. + local APC=SPAWN:New(_transportitem.templatename):SpawnFromCoordinate(self.spawnzone:GetCoordinate()) + + -- Set up cargo APC. + local CargoAPC=AI_CARGO_APC:New(APC, CargoGroups, 0) + + -- Init pickup/loading of cargo. (No drive-to coordinate given, since we are already at in the spawn zone.) + CargoAPC:__Pickup(5) + + --- Once the cargo was loaded start off to deploy airbase. + function CargoAPC:OnAfterLoaded(Airplane, From, Event, To) + CargoAPC:__Deploy(5, Airbase:GetZone():GetCoordinate()) + end + + --- Function called when cargo has arrived and was unloaded. + function CargoAPC:OnAfterUnloaded(Airplane, From, Event, To) + + local group=CargoAPC.Cargo:GetObject() + local Airplane=Airplane --Wrapper.Group#GROUP + local warehouse=Airplane:GetState(Airplane, "WAREHOUSE") --#WAREHOUSE + + -- Trigger Delivered event. + warehouse:__Delivered(1, group) + end + elseif TransportType==WAREHOUSE.TransportType.TRAIN then + self:E(self.wid.."ERROR: transport by train not supported yet!") + elseif TransportType==WAREHOUSE.TransportType.SHIP then + + self:E(self.wid.."ERROR: transport by ship not supported yet!") elseif TransportType==WAREHOUSE.TransportType.SELFPROPELLED then From e93f2c54b283ed47e7c99a1d581a88f175b6a650 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sat, 4 Aug 2018 16:47:18 +0200 Subject: [PATCH 233/420] Working version, but needs to be upgraded. --- .../Moose/AI/AI_Cargo_Airplane.lua | 32 ++++++- .../Moose/AI/AI_Cargo_Dispatcher.lua | 93 ++++++++++++++++--- .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 2 +- .../Moose/AI/AI_Cargo_Dispatcher_Airplane.lua | 32 ++++--- .../AI/AI_Cargo_Dispatcher_Helicopter.lua | 2 +- Moose Development/Moose/Core/Set.lua | 39 ++++++++ 6 files changed, 171 insertions(+), 29 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index bbbe19ed0..dc3076e95 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -111,6 +111,18 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) end +function AI_CARGO_AIRPLANE:IsTransporting() + + return self.Transporting == true +end + +function AI_CARGO_AIRPLANE:IsRelocating() + + return self.Relocating == true +end + + + --- Set the Carrier. -- @param #AI_CARGO_AIRPLANE self -- @param Wrapper.Group#GROUP Airplane @@ -155,7 +167,8 @@ function AI_CARGO_AIRPLANE:SetCarrier( Airplane ) function Airplane:OnEventEngineShutdown( EventData ) - AICargo:Landed() + self:F("Calling") + AICargo:Landed( self.Airplane ) end self.Coalition = self.Airplane:GetCoalition() @@ -199,6 +212,10 @@ end -- @param #number Speed function AI_CARGO_AIRPLANE:onafterLanded( Airplane, From, Event, To ) + self:F({Airplane, From, Event, To}) + self:F({IsAlive=Airplane:IsAlive()}) + self:F({RoutePickup=self.RoutePickup}) + if Airplane and Airplane:IsAlive() then if self.RoutePickup == true then @@ -230,6 +247,8 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Airbase, Sp self:Route( Airplane, Airbase, Speed ) self.RoutePickup = true self.Airbase = Airbase + self.Transporting = true + self.Relocating = false end end @@ -248,6 +267,8 @@ function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Airbase, Sp self:Route( Airplane, Airbase, Speed ) self.RouteDeploy = true self.Airbase = Airbase + self.Transporting = false + self.Relocating = false end end @@ -260,7 +281,9 @@ function AI_CARGO_AIRPLANE:onafterLoad( Airplane, From, Event, To, Coordinate ) if Airplane and Airplane:IsAlive() then for _, Cargo in pairs( self.CargoSet:GetSet() ) do - if Cargo:IsInLoadRadius( Coordinate ) then + self:F({Cargo:GetName()}) + local InRadius = Cargo:IsInLoadRadius( Coordinate ) + if InRadius then self:__Board( 5 ) Cargo:Board( Airplane, 25 ) self.Cargo = Cargo @@ -359,6 +382,7 @@ function AI_CARGO_AIRPLANE:Route( Airplane, Airbase, Speed ) FromWaypoint.helipadId = nil FromWaypoint.airdromeId = nil + local ParkingSpots = self.Airbase:FindFreeParkingSpotForAircraft( Airplane, AIRBASE.TerminalType.OpenBig ) local AirbaseID = self.Airbase:GetID() local AirbaseCategory = self.Airbase:GetDesc().category @@ -377,8 +401,8 @@ function AI_CARGO_AIRPLANE:Route( Airplane, Airbase, Speed ) -- These cause a lot of confusion. local UnitTemplate = Template.units[UnitID] - UnitTemplate.parking = 15 - UnitTemplate.parking_id = "1" + UnitTemplate.parking = nil + UnitTemplate.parking_id = nil UnitTemplate.alt = 0 local SX = UnitTemplate.x diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 0b1700e04..6b3732121 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -74,7 +74,7 @@ AI_CARGO_DISPATCHER = { ClassName = "AI_CARGO_DISPATCHER", SetCarrier = nil, - SetDeployZones = nil, + DeployZonesSet = nil, AI_Cargo = {}, PickupCargo = {} } @@ -90,23 +90,21 @@ AI_CARGO_DISPATCHER.PickupCargo = {} -- @param #AI_CARGO_DISPATCHER self -- @param Core.Set#SET_GROUP SetCarrier -- @param Core.Set#SET_CARGO SetCargo --- @param Core.Set#SET_ZONE SetDeployZones -- @return #AI_CARGO_DISPATCHER -- @usage -- -- -- Create a new cargo dispatcher --- SetCarrier = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() --- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- SetCarriers = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() +-- SetCargos = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() -- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() --- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZone ) +-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo ) -- -function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZones ) +function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo ) local self = BASE:Inherit( self, FSM:New() ) -- #AI_CARGO_DISPATCHER self.SetCarrier = SetCarrier -- Core.Set#SET_GROUP self.SetCargo = SetCargo -- Core.Set#SET_CARGO - self.SetDeployZones = SetDeployZones -- Core.Set#SET_ZONE self:SetStartState( "Idle" ) @@ -144,6 +142,58 @@ function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZones ) end +--- Creates a new AI_CARGO_DISPATCHER object. +-- @param #AI_CARGO_DISPATCHER self +-- @param Core.Set#SET_GROUP SetCarrier +-- @param Core.Set#SET_CARGO SetCargo +-- @param Core.Set#SET_ZONE DeployZonesSet +-- @return #AI_CARGO_DISPATCHER +-- @usage +-- +-- -- Create a new cargo dispatcher +-- SetCarriers = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() +-- SetCargos = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- DeployZonesSet = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() +-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZone ) +-- +function AI_CARGO_DISPATCHER:NewWithZones( SetCarriers, SetCargos, DeployZonesSet ) + + local self = AI_CARGO_DISPATCHER:New( SetCarriers, SetCargos ) -- #AI_CARGO_DISPATCHER + + self.DeployZonesSet = DeployZonesSet + + return self +end + + +--- Creates a new AI_CARGO_DISPATCHER object. +-- @param #AI_CARGO_DISPATCHER self +-- @param Core.Set#SET_GROUP SetCarrier +-- @param Core.Set#SET_CARGO SetCargo +-- @param Core.Set#SET_AIRBASE PickupAirbasesSet +-- @param Core.Set#SET_AIRBASE DeployAirbasesSet +-- @return #AI_CARGO_DISPATCHER +-- @usage +-- +-- -- Create a new cargo dispatcher +-- SetCarriers = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() +-- SetCargos = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- PickupAirbasesSet = SET_AIRBASES:New() +-- DeployAirbasesSet = SET_AIRBASES:New() +-- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, PickupAirbasesSet, DeployAirbasesSet ) +-- +function AI_CARGO_DISPATCHER:NewWithAirbases( SetCarriers, SetCargos, PickupAirbasesSet, DeployAirbasesSet ) + + local self = AI_CARGO_DISPATCHER:New( SetCarriers, SetCargos ) -- #AI_CARGO_DISPATCHER + + self.DeployAirbasesSet = DeployAirbasesSet + self.PickupAirbasesSet = PickupAirbasesSet + + return self +end + + + --- Set the home zone. -- When there is nothing anymore to pickup, the carriers will go to a random coordinate in this zone. -- They will await here new orders. @@ -361,7 +411,16 @@ function AI_CARGO_DISPATCHER:onafterMonitor() if PickupCargo then self.CarrierHome[Carrier] = nil local PickupCoordinate = PickupCargo:GetCoordinate():GetRandomCoordinateInRadius( self.PickupOuterRadius, self.PickupInnerRadius ) - AI_Cargo:Pickup( PickupCoordinate, math.random( self.PickupMinSpeed, self.PickupMaxSpeed ) ) + + if self.PickupAirbasesSet then + -- Find airbase within 2km from the cargo with the set. + local PickupAirbase = self.PickupAirbasesSet:FindAirbaseInRange( PickupCoordinate, 4000 ) + if PickupAirbase then + AI_Cargo:Pickup( PickupAirbase, math.random( self.PickupMinSpeed, self.PickupMaxSpeed ) ) + end + else + AI_Cargo:Pickup( PickupCoordinate, math.random( self.PickupMinSpeed, self.PickupMaxSpeed ) ) + end break else if self.HomeZone then @@ -464,12 +523,22 @@ end -- @return #AI_CARGO_DISPATCHER function AI_CARGO_DISPATCHER:OnAfterLoaded( From, Event, To, Carrier, Cargo ) - local DeployZone = self.SetDeployZones:GetRandomZone() + if self.DeployZonesSet then - local DeployCoordinate = DeployZone:GetCoordinate():GetRandomCoordinateInRadius( self.DeployOuterRadius, self.DeployInnerRadius ) - self.AI_Cargo[Carrier]:Deploy( DeployCoordinate, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ) ) + local DeployZone = self.DeployZonesSet:GetRandomZone() + + local DeployCoordinate = DeployZone:GetCoordinate():GetRandomCoordinateInRadius( self.DeployOuterRadius, self.DeployInnerRadius ) + self.AI_Cargo[Carrier]:Deploy( DeployCoordinate, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ) ) - self.PickupCargo[Carrier] = nil + end + + if self.DeployAirbasesSet then + + local DeployAirbase = self.DeployAirbasesSet:GetRandomAirbase() + self.AI_Cargo[Carrier]:Deploy( DeployAirbase, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ) ) + end + + self.PickupCargo[Carrier] = nil end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index 561125786..413ca7c26 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -101,7 +101,7 @@ AI_CARGO_DISPATCHER_APC = { -- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() -- AICargoDispatcher = AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZone, 500 ) -- -function AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZone, CombatRadius ) +function AI_CARGO_DISPATCHER_APC:NewWithZones( SetAPC, SetCargo, SetDeployZone, CombatRadius ) local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone ) ) -- #AI_CARGO_DISPATCHER_APC diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua index c208bab9a..52dd0a712 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua @@ -16,8 +16,8 @@ --- Brings a dynamic cargo handling capability for AI groups. -- -- Airplanes can be mobilized to intelligently transport infantry and other cargo within the simulation. --- The AI\_CARGO\_DISPATCHER\_AIRPLANE module uses the @{Cargo} capabilities within the MOOSE framework. --- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER\_AIRPLANE object recognize the cargo. +-- The AI_CARGO_DISPATCHER_AIRPLANE module uses the @{Cargo} capabilities within the MOOSE framework. +-- CARGO derived objects must be declared within the mission to make the AI_CARGO_DISPATCHER_AIRPLANE object recognize the cargo. -- Please consult the @{Cargo} module for more information. -- -- @@ -29,23 +29,33 @@ AI_CARGO_DISPATCHER_AIRPLANE = { --- Creates a new AI_CARGO_DISPATCHER_AIRPLANE object. -- @param #AI_CARGO_DISPATCHER_AIRPLANE self --- @param Core.Set#SET_GROUP SetAirplane --- @param Core.Set#SET_CARGO SetCargo --- @param Core.Set#SET_ZONE SetDeployZone +-- @param Core.Set#SET_GROUP SetAirplanes +-- @param Core.Set#SET_CARGO SetCargos +-- @param Core.Set#SET_AIRBASE PickupAirbasesSet +-- @param Core.Set#SET_AIRBASE DeployAirbasesSet -- @return #AI_CARGO_DISPATCHER_AIRPLANE -- @usage -- -- -- Create a new cargo dispatcher --- SetAirplane = SET_GROUP:New():FilterPrefixes( "Airplane" ):FilterStart() --- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() --- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() --- AICargoDispatcher = AI_CARGO_DISPATCHER_AIRPLANE:New( SetAirplane, SetCargo ) +-- SetAirplanes = SET_GROUP:New():FilterPrefixes( "Airplane" ):FilterStart() +-- SetCargos = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- PickupAirbasesSet = SET_AIRBASE:New() +-- DeployAirbasesSet = SET_AIRBASE:New() +-- AICargoDispatcher = AI_CARGO_DISPATCHER_AIRPLANE:New( SetAirplanes, SetCargos, PickupAirbasesSet, DeployAirbasesSet ) -- -function AI_CARGO_DISPATCHER_AIRPLANE:New( SetAirplane, SetCargo, SetDeployZones ) +function AI_CARGO_DISPATCHER_AIRPLANE:New( SetAirplanes, SetCargos, PickupAirbasesSet, DeployAirbasesSet ) - local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetAirplane, SetCargo, SetDeployZones ) ) -- #AI_CARGO_DISPATCHER_AIRPLANE + local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithAirbases( SetAirplanes, SetCargos, PickupAirbasesSet, DeployAirbasesSet ) ) -- #AI_CARGO_DISPATCHER_AIRPLANE + + self:SetDeploySpeed( 200, 150 ) + self:SetPickupSpeed( 200, 150 ) + self:SetPickupRadius( 0, 0 ) + self:SetDeployRadius( 0, 0 ) return self end +function AI_CARGO_DISPATCHER_AIRPLANE:AICargo( Airplane, SetCargo ) + return AI_CARGO_AIRPLANE:New( Airplane, SetCargo ) +end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index eaf9666d2..26b5d9aab 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -102,7 +102,7 @@ AI_CARGO_DISPATCHER_HELICOPTER = { -- function AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargo, SetDeployZones ) - local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetHelicopter, SetCargo, SetDeployZones ) ) -- #AI_CARGO_DISPATCHER_HELICOPTER + local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithZones( SetHelicopter, SetCargo, SetDeployZones ) ) -- #AI_CARGO_DISPATCHER_HELICOPTER self:SetDeploySpeed( 200, 150 ) self:SetPickupSpeed( 200, 150 ) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index e81beb007..7183623e0 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -4010,6 +4010,45 @@ function SET_AIRBASE:FindAirbase( AirbaseName ) end +--- Finds an Airbase in range of a coordinate. +-- @param #SET_AIRBASE self +-- @param Core.Point#COORDINATE Coordinate +-- @param #number Range +-- @return Wrapper.Airbase#AIRBASE The found Airbase. +function SET_AIRBASE:FindAirbaseInRange( Coordinate, Range ) + + local AirbaseFound = nil + + for AirbaseName, AirbaseObject in pairs( self.Set ) do + + local AirbaseCoordinate = AirbaseObject:GetCoordinate() + local Distance = Coordinate:Get2DDistance( AirbaseCoordinate ) + + self:F({Distance=Distance}) + + if Distance <= Range then + AirbaseFound = AirbaseObject + break + end + + end + + return AirbaseFound +end + + +--- Finds a random Airbase in the set. +-- @param #SET_AIRBASE self +-- @return Wrapper.Airbase#AIRBASE The found Airbase. +function SET_AIRBASE:GetRandomAirbase() + + local RandomAirbase = self:GetRandom() + self:F( { RandomAirbase = RandomAirbase:GetName() } ) + + return RandomAirbase +end + + --- Builds a set of airbases of coalitions. -- Possible current coalitions are red, blue and neutral. From 1ce55f3d3d638a9fe3cf6d251bc848814da695cc Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sat, 4 Aug 2018 18:27:44 +0200 Subject: [PATCH 234/420] WAREHOUSE --- .../Moose/Functional/Warehouse.lua | 55 ++++++++++--------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 48d365019..0eea6eeaf 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -89,31 +89,31 @@ WAREHOUSE.Descriptor = { --- Warehouse unit categories. These are used for -- @type WAREHOUSE.Attribute WAREHOUSE.Attribute = { - TRANSPORT_PLANE="transportplane", - TRANSPORT_HELO="transporthelo", - TRANSPORT_APC="transportapc", - FIGHTER="fighter", - TANKER="tanker", - AWACS="awacs", - ARTILLERY="artillery", - ATTACKHELICOPTER="attackhelicopter", - INFANTRY="infantry", - BOMBER="bomber", - TANK="tank", - TRUCK="truck", - OTHER="other", + TRANSPORT_PLANE="Transport_Plane", + TRANSPORT_HELO="Transport_Helo", + TRANSPORT_APC="Transport_APC", + FIGHTER="Fighter", + TANKER="Tanker", + AWACS="AWACS", + ARTILLERY="Artillery", + ATTACKHELICOPTER="Attackhelicopter", + INFANTRY="Infantry", + BOMBER="Bomber", + TANK="Tank", + TRUCK="Truck", + SHIP="Ship", + OTHER="Other", } --- Cargo transport type. -- @type WAREHOUSE.TransportType --- @field #string AIRPLANE plane blabla WAREHOUSE.TransportType = { - AIRPLANE = "transportplane", - HELICOPTER = "transporthelo", - APC = "transportapc", - SHIP = "ship", - TRAIN = "train", - SELFPROPELLED = "selfporpelled", + AIRPLANE = "Transport_Plane", + HELICOPTER = "Transport_Helo", + APC = "Transport_APC", + SHIP = "Ship", + TRAIN = "Train", + SELFPROPELLED = "Selfporpelled", } --- Warehouse class version. @@ -131,6 +131,7 @@ WAREHOUSE.version="0.1.0" -- TODO: Put active groups into the warehouse. -- TODO: Spawn warehouse assets as uncontrolled or AI off and activate them when requested. -- TODO: Handle cases with immobile units. +-- TODO: Add queue. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor(s) @@ -209,9 +210,7 @@ function WAREHOUSE:NewAirbase(airbase) --- Triggers the FSM event "Request" after a delay. -- @function [parent=#WAREHOUSE] __Request -- @param #WAREHOUSE self - -- @param #string From From state. - -- @param #string Event Event. - -- @param #string To To state. + -- @param #number Delay Delay in seconds. -- @param Wrapper.Airbase#AIRBASE Airbase Airbase requesting supply. -- @param #WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. -- @param AssetDescriptorValue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. @@ -225,8 +224,8 @@ function WAREHOUSE:NewAirbase(airbase) --- Triggers the FSM event "Delivered" after a delay. -- @function [parent=#WAREHOUSE] __Delivered - -- @param #number delay Delay in seconds. -- @param #WAREHOUSE self + -- @param #number delay Delay in seconds. -- @param Wrapper.Group#GROUP group Group that was delivered. return self @@ -423,9 +422,13 @@ function WAREHOUSE:onafterRequest(From, Event, To, Airbase, AssetDescriptor, Ass -- Add spawned groups to cargo group object. for _i,_spawngroup in pairs(_spawngroups) do --TODO: check near and load radius. - local cargogroup = CARGO_GROUP:New(_spawngroup, AssetDescriptorValue, string.format("%s %d",AssetDescriptorValue, _i), _loadradius, _nearradius) + local _name=string.format("%s %d",AssetDescriptorValue, _i) + env.info(string.format("FF cargo group %d: %s",_i,_name)) + local cargogroup = CARGO_GROUP:New(_spawngroup, AssetDescriptorValue, _name, _loadradius, _nearradius) CargoGroups:AddCargo(cargogroup) - end + end + + env.info(string.format("FF cargo set object names %s", CargoGroups:GetObjectNames())) From d53c444c621df010d61999e2a4c89613e1c7577b Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 6 Aug 2018 00:21:22 +0200 Subject: [PATCH 235/420] Warehouse and other things --- .../Moose/AI/AI_Cargo_Airplane.lua | 328 +++++++++--------- .../Moose/AI/AI_Cargo_Dispatcher_Airplane.lua | 10 +- .../Moose/AI/AI_G2G_Dispatcher.lua | 139 -------- Moose Development/Moose/Cargo/Cargo.lua | 8 + Moose Development/Moose/Cargo/CargoGroup.lua | 30 +- Moose Development/Moose/Cargo/CargoUnit.lua | 12 +- Moose Development/Moose/Core/Point.lua | 31 ++ Moose Development/Moose/Functional/JTAC.lua | 172 --------- .../Moose/Functional/Warehouse.lua | 45 +-- Moose Development/Moose/Wrapper/Group.lua | 132 +++---- Moose Setup/Moose.files | 1 - 11 files changed, 308 insertions(+), 600 deletions(-) delete mode 100644 Moose Development/Moose/AI/AI_G2G_Dispatcher.lua delete mode 100644 Moose Development/Moose/Functional/JTAC.lua diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 37ae630b9..c591acace 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -51,34 +51,41 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) --- Pickup Handler OnBefore for AI_CARGO_AIRPLANE -- @function [parent=#AI_CARGO_AIRPLANE] OnBeforePickup -- @param #AI_CARGO_AIRPLANE self - -- @param #string From - -- @param #string Event - -- @param #string To + -- @param Wrapper.Group#GROUP Airplane Cargo transport plane. + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. -- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troops are picked up. + -- @param #number Speed in km/h for travelling to pickup base. -- @return #boolean --- Pickup Handler OnAfter for AI_CARGO_AIRPLANE -- @function [parent=#AI_CARGO_AIRPLANE] OnAfterPickup -- @param #AI_CARGO_AIRPLANE self + -- @param Wrapper.Group#GROUP Airplane Cargo plane. -- @param #string From -- @param #string Event -- @param #string To -- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troops are picked up. + -- @param #number Speed in km/h for travelling to pickup base. --- Pickup Trigger for AI_CARGO_AIRPLANE -- @function [parent=#AI_CARGO_AIRPLANE] Pickup -- @param #AI_CARGO_AIRPLANE self -- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troops are picked up. + -- @param #number Speed in km/h for travelling to pickup base. --- Pickup Asynchronous Trigger for AI_CARGO_AIRPLANE -- @function [parent=#AI_CARGO_AIRPLANE] __Pickup -- @param #AI_CARGO_AIRPLANE self -- @param #number Delay Delay in seconds. -- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troops are picked up. + -- @param #number Speed in km/h for travelling to pickup base. --- Deploy Handler OnBefore for AI_CARGO_AIRPLANE -- @function [parent=#AI_CARGO_AIRPLANE] OnBeforeDeploy -- @param #AI_CARGO_AIRPLANE self + -- @param Wrapper.Group#GROUP Airplane Cargo plane. -- @param #string From -- @param #string Event -- @param #string To @@ -89,6 +96,7 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) --- Deploy Handler OnAfter for AI_CARGO_AIRPLANE -- @function [parent=#AI_CARGO_AIRPLANE] OnAfterDeploy -- @param #AI_CARGO_AIRPLANE self + -- @param Wrapper.Group#GROUP Airplane Cargo plane. -- @param #string From -- @param #string Event -- @param #string To @@ -107,7 +115,16 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) -- @param #number Delay Delay in seconds. -- @param Wrapper.Airbase#AIRBASE Airbase Destination airbase where troops are deployed. -- @param #number Speed Speed in km/h for travelling to deploy base. + + --- On after Loaded event, i.e. triggered when the cargo is inside the carrier. + -- @function [parent=#AI_CARGO_AIRPLANE] OnAfterLoaded + -- @param #AI_CARGO_AIRPLANE self + -- @param Wrapper.Group#GROUP Airplane Cargo plane. + -- @param From + -- @param Event + -- @param To + -- Set carrier. self:SetCarrier( Airplane ) return self @@ -126,10 +143,10 @@ end ---- Set the Carrier. +--- Set the Carrier (controllable). Also initializes events for carrier and defines the coalition. -- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane --- @return #AI_CARGO_AIRPLANE +-- @param Wrapper.Group#GROUP Airplane Transport plane. +-- @return #AI_CARGO_AIRPLANE self function AI_CARGO_AIRPLANE:SetCarrier( Airplane ) local AICargo = self @@ -184,7 +201,7 @@ end --- Find a free Carrier within a range. -- @param #AI_CARGO_AIRPLANE self - -- @param Wrapper.Airbase#AIRBASE Airbase +-- @param Wrapper.Airbase#AIRBASE Airbase -- @param #number Radius -- @return Wrapper.Group#GROUP NewCarrier function AI_CARGO_AIRPLANE:FindCarrier( Coordinate, Radius ) @@ -206,9 +223,9 @@ function AI_CARGO_AIRPLANE:FindCarrier( Coordinate, Radius ) end ---- On after "Landed" event. Called on engine shutdown. +--- On after "Landed" event. Called on engine shutdown and initiates the pickup mission or unloading event. -- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane Transport plane. +-- @param Wrapper.Group#GROUP Airplane Cargo transport plane. -- @param From -- @param Event -- @param To @@ -220,11 +237,13 @@ function AI_CARGO_AIRPLANE:onafterLanded( Airplane, From, Event, To ) if Airplane and Airplane:IsAlive() then + -- Aircraft was sent to this airbase to pickup troops. Initiate loadling. if self.RoutePickup == true then self:Load( Airplane:GetPointVec2() ) self.RoutePickup = false end + -- Aircraft was send to this airbase to deploy troops. Initiate unloading. if self.RouteDeploy == true then self:Unload() self.RouteDeploy = false @@ -237,48 +256,66 @@ end --- On after "Pickup" event. Routes transport to pickup airbase. -- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane --- @param From --- @param Event --- @param To --- @param Wrapper.Airbase#AIRBASE Airbase --- @param #number Speed +-- @param Wrapper.Group#GROUP Airplane Cargo transport plane. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Wrapper.Airbase#AIRBASE Airbase Airbase where the troops as picked up. +-- @param #number Speed in km/h for travelling to pickup base. function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Airbase, Speed ) if Airplane and Airplane:IsAlive() then - -- Aircraft might be on the ground of the pickup airbase already. + -- Two cases. Aircraft spawned in air or at an airbase. if Airplane:InAir() then - self:Route( Airplane, Airbase, Speed ) + self.Airbase=nil + else + self.Airbase=Airplane:GetCoordinate():GetClosestAirbase() end - -- TODO: Improve :Route() so that the aircraft can be routed from another airbase to the pickup airbase. - - self.RoutePickup = true + -- Route aircraft to pickup airbase. + self:Route( Airplane, Airbase, Speed ) + -- Set airbase as starting point in the next Route() call. self.Airbase = Airbase + + -- Aircraft is on a pickup mission. + self.RoutePickup = true + + -- Unclear!? self.Transporting = true self.Relocating = false end end ---- On after Depoly event. Routes plane to deploy airbase. +--- On after Depoly event. Routes plane to the airbase where the troops are deployed. -- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane --- @param From --- @param Event --- @param To +-- @param Wrapper.Group#GROUP Airplane Cargo transport plane. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. -- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troups should be deployed. -- @param #number Speed Speed in km/h for travelling to deploy base. function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Airbase, Speed ) - if Airplane and Airplane:IsAlive() then - - -- Route to + if Airplane and Airplane:IsAlive()~=nil then + + -- Activate uncontrolled airplane. + if Airplane:IsAlive()==false then + Airplane:SetCommand({id = 'Start', params = {}}) + end + + -- Route to destination airbase. self:Route( Airplane, Airbase, Speed ) + + -- Aircraft is on a depoly mission. self.RouteDeploy = true + + -- Set destination airbase for next :Route() command. self.Airbase = Airbase + + -- Unclear?! self.Transporting = false self.Relocating = false end @@ -286,17 +323,16 @@ function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Airbase, Sp end ---- On after Load event. +--- On after Load event. Checks if cargo is inside the load radius and if so starts the boarding process. -- @param #AI_CARGO_AIRPLANE self -- @param Wrapper.Group#GROUP Airplane Transport plane. --- @param From --- @param Event --- @param To --- @param Wrapper.Point#COORDINATE Coordinate +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Wrapper.Point#COORDINATE Coordinate Place where the cargo is guided to if it is inside the load radius. function AI_CARGO_AIRPLANE:onafterLoad( Airplane, From, Event, To, Coordinate ) - if Airplane and Airplane:IsAlive()==true or Airplane:IsAlive()==false then - --if Airplane then + if Airplane and Airplane:IsAlive()~=nil then for _, Cargo in pairs( self.CargoSet:GetSet() ) do self:F({Cargo:GetName()}) @@ -313,12 +349,12 @@ function AI_CARGO_AIRPLANE:onafterLoad( Airplane, From, Event, To, Coordinate ) end ---- On after Board event. +--- On after Board event. Cargo is inside the load radius and boarding is performed. -- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane Cargo plane. --- @param From --- @param Event --- @param To +-- @param Wrapper.Group#GROUP Airplane Cargo transport plane. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. function AI_CARGO_AIRPLANE:onafterBoard( Airplane, From, Event, To ) if Airplane and Airplane:IsAlive() then @@ -332,26 +368,28 @@ function AI_CARGO_AIRPLANE:onafterBoard( Airplane, From, Event, To ) end ---- On after Loaded event. +--- On after Loaded event. Cargo is inside the carrier and ready to be transported. -- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane Cargo plane. --- @param From --- @param Event --- @param To +-- @param Wrapper.Group#GROUP Airplane Cargo transport plane. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. function AI_CARGO_AIRPLANE:onafterLoaded( Airplane, From, Event, To ) + env.info("FF troops loaded into cargo plane") + if Airplane and Airplane:IsAlive() then end end ---- On after Unload event. +--- On after Unload event. Cargo is beeing unloaded, i.e. the unboarding process is started. -- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane Cargo plane. --- @param From --- @param Event --- @param To +-- @param Wrapper.Group#GROUP Airplane Cargo transport plane. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To ) if Airplane and Airplane:IsAlive() then @@ -361,12 +399,12 @@ function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To ) end ---- On after Unboard event. +--- On after Unboard event. Checks if unboarding process is finished. -- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane Cargo plane. --- @param From --- @param Event --- @param To +-- @param Wrapper.Group#GROUP Airplane Cargo transport plane. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. function AI_CARGO_AIRPLANE:onafterUnboard( Airplane, From, Event, To ) if Airplane and Airplane:IsAlive() then @@ -379,12 +417,12 @@ function AI_CARGO_AIRPLANE:onafterUnboard( Airplane, From, Event, To ) end ---- On after Unloaded event. +--- On after Unloaded event. Cargo has been unloaded, i.e. the unboarding process is finished. -- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane Cargo plane. --- @param From --- @param Event --- @param To +-- @param Wrapper.Group#GROUP Airplane Cargo transport plane. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. function AI_CARGO_AIRPLANE:onafterUnloaded( Airplane, From, Event, To ) if Airplane and Airplane:IsAlive() then @@ -393,119 +431,75 @@ function AI_CARGO_AIRPLANE:onafterUnloaded( Airplane, From, Event, To ) end +--- Route the airplane from one airport or it's current position to another airbase. +-- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Group#GROUP Airplane Airplane group to be routed. +-- @param Wrapper.Airbase#AIRBASE Airbase Destination airbase. +-- @param #number Speed Speed in km/h. Default is 80% of max possible speed the group can do. +-- @param #boolean Uncontrolled If true, spawn group in uncontrolled state. +function AI_CARGO_AIRPLANE:Route( Airplane, Airbase, Speed, Uncontrolled ) ---- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane --- @param Wrapper.Airbase#AIRBASE Airbase --- @param #number Speed -function AI_CARGO_AIRPLANE:Route( Airplane, Airbase, Speed ) + if Airplane and Airplane:IsAlive()~=nil then - if Airplane and Airplane:IsAlive() then - - local PointVec3 = Airplane:GetPointVec3() - - local Takeoff = SPAWN.Takeoff.Hot + -- Set takeoff type. + local Takeoff = SPAWN.Takeoff.Cold + -- Get template of group. local Template = Airplane:GetTemplate() - - if Template then - - local Points = {} - - if self.Airbase then - - local FromWaypoint = Template.route.points[1] - -- These are only for ships. - FromWaypoint.linkUnit = nil - FromWaypoint.helipadId = nil - FromWaypoint.airdromeId = nil - - local ParkingSpots = self.Airbase:FindFreeParkingSpotForAircraft( Airplane, AIRBASE.TerminalType.OpenBig ) - local AirbaseID = self.Airbase:GetID() - local AirbaseCategory = self.Airbase:GetDesc().category - - FromWaypoint.airdromeId = AirbaseID - - FromWaypoint.alt = 0 - - FromWaypoint.type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type - FromWaypoint.action = GROUPTEMPLATE.Takeoff[Takeoff][2] -- action - - - -- Translate the position of the Group Template to the Vec3. - for UnitID = 1, #Template.units do - self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. Template.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. Template.units[UnitID].y ) - - -- These cause a lot of confusion. - local UnitTemplate = Template.units[UnitID] - - UnitTemplate.parking = nil - UnitTemplate.parking_id = nil - UnitTemplate.alt = 0 - - local SX = UnitTemplate.x - local SY = UnitTemplate.y - local BX = FromWaypoint.x - local BY = FromWaypoint.y - local TX = PointVec3.x + ( SX - BX ) - local TY = PointVec3.z + ( SY - BY ) - - UnitTemplate.x = TX - UnitTemplate.y = TY - - self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. UnitTemplate.x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. UnitTemplate.y ) - end - - FromWaypoint.x = PointVec3.x - FromWaypoint.y = PointVec3.z - - Points[#Points+1] = FromWaypoint - else - - local GroupPoint = Airplane:GetVec2() - local GroupVelocity = Airplane:GetUnit(1):GetDesc().speedMax - - local FromWaypoint = {} - FromWaypoint.x = GroupPoint.x - FromWaypoint.y = GroupPoint.y - FromWaypoint.type = "Turning Point" - FromWaypoint.action = "Turning Point" - FromWaypoint.speed = GroupVelocity - - Points[#Points+1] = FromWaypoint - end - - local AirbasePointVec2 = Airbase:GetPointVec2() - local ToWaypoint = AirbasePointVec2:WaypointAir( - POINT_VEC3.RoutePointAltType.BARO, - "Land", - "Landing", - Speed or Airplane:GetUnit(1):GetDesc().speedMax - ) - - ToWaypoint["airdromeId"] = Airbase:GetID() - ToWaypoint["speed_locked"] = true, - - self:F( ToWaypoint ) - - Points[#Points+1] = ToWaypoint - - Template.x = PointVec3.x - Template.y = PointVec3.z - - self:T3( Points ) - Template.route.points = Points - - Template.uncontrolled=false - - --self:Respawn( Template ) - - local GroupSpawned = Airplane:Respawn( Template ) - - return GroupSpawned + -- Nil check + if Template==nil then + return end - end + -- Waypoints of the route. + local Points={} + + -- To point. + local AirbasePointVec2 = Airbase:GetPointVec2() + local ToWaypoint = AirbasePointVec2:WaypointAir( + POINT_VEC3.RoutePointAltType.BARO, + "Land", + "Landing", + Speed or Airplane:GetSpeedMax()*0.8 + ) + ToWaypoint["airdromeId"] = Airbase:GetID() + ToWaypoint["speed_locked"] = true + + + -- If self.Airbase~=nil then group is currently at an airbase, where it should be respawned. + if self.Airbase then + + -- Second point of the route. First point is done in RespawnAtCurrentAirbase() routine. + Template.route.points[2] = ToWaypoint + + -- Respawn group at the current airbase. + Airplane:RespawnAtCurrentAirbase(Template, Takeoff, Uncontrolled) + + else + -- From point. + local GroupPoint = Airplane:GetVec2() + local FromWaypoint = {} + FromWaypoint.x = GroupPoint.x + FromWaypoint.y = GroupPoint.y + FromWaypoint.type = "Turning Point" + FromWaypoint.action = "Turning Point" + FromWaypoint.speed = Airplane:GetSpeedMax()*0.8 + + -- The two route points. + Points[1] = FromWaypoint + Points[2] = ToWaypoint + + local PointVec3 = Airplane:GetPointVec3() + Template.x = PointVec3.x + Template.y = PointVec3.z + + Template.route.points = Points + + local GroupSpawned = Airplane:Respawn(Template) + + end + end end + diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua index 52dd0a712..300fef9ae 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua @@ -29,11 +29,11 @@ AI_CARGO_DISPATCHER_AIRPLANE = { --- Creates a new AI_CARGO_DISPATCHER_AIRPLANE object. -- @param #AI_CARGO_DISPATCHER_AIRPLANE self --- @param Core.Set#SET_GROUP SetAirplanes --- @param Core.Set#SET_CARGO SetCargos --- @param Core.Set#SET_AIRBASE PickupAirbasesSet --- @param Core.Set#SET_AIRBASE DeployAirbasesSet --- @return #AI_CARGO_DISPATCHER_AIRPLANE +-- @param Core.Set#SET_GROUP SetAirplanes Set of cargo transport airplanes. +-- @param Core.Set#SET_CARGO SetCargos Set of cargo, which is supposed to be transported. +-- @param Core.Set#SET_AIRBASE PickupAirbasesSet Set of airbases where the cargo has to be picked up. +-- @param Core.Set#SET_AIRBASE DeployAirbasesSet Set of airbases where the cargo is deployed. Choice for each cargo is random. +-- @return #AI_CARGO_DISPATCHER_AIRPLANE self -- @usage -- -- -- Create a new cargo dispatcher diff --git a/Moose Development/Moose/AI/AI_G2G_Dispatcher.lua b/Moose Development/Moose/AI/AI_G2G_Dispatcher.lua deleted file mode 100644 index 734c4ef4d..000000000 --- a/Moose Development/Moose/AI/AI_G2G_Dispatcher.lua +++ /dev/null @@ -1,139 +0,0 @@ ---- **AI** - (R2.4) - Manages automatic ground troups dispatching to the battle field. --- --- --- Features: --- --- * Some nice stuff. --- --- # QUICK START GUIDE --- --- === --- --- ### Authors: **funkyfranky** --- --- @module AI.AI_G2G_Dispatcher --- @image AI_Air_To_Air_Dispatching.JPG - -do -- AI_G2G_DISPATCHER - - --- AI_G2G_DISPATCHER class. - -- @type AI_G2G_DISPATCHER - -- @field #string ClassName Name of the class. - -- @field Functional.Detection#DETECTION_AREAS Detection Detection object responsible for identifying enemies. - -- @extends Tasking.DetectionManager#DETECTION_MANAGER - - --- Create an automatic ground . - -- - -- === - -- - -- # Demo Missions - -- - -- ### [AI\_A2A\_DISPATCHER Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/AID%20-%20AI%20Dispatching) - -- - -- === - -- - -- # YouTube Channel - -- - -- ### [DCS WORLD - MOOSE - A2A GCICAP - Build an automatic A2A Defense System](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl0S4KMNUUJpaUs6zZHjLKNx) - -- - -- === - -- - -- ![Banner Image](..\Presentations\AI_A2A_DISPATCHER\Dia3.JPG) - -- - -- Blabla. - -- - -- === - -- - -- # USAGE GUIDE - -- - -- - -- - -- @field #AI_G2G_DISPATCHER - AI_G2G_DISPATCHER = { - ClassName = "AI_G2G_DISPATCHER", - Detection = nil, - Homebase = {}, - } - - - - --- AI_G2G_DISPATCHER constructor. Creates a new AI_G2G_DISPATCHER object. - -- @param #AI_G2G_DISPATCHER self - -- @param Functional.Detection#DETECTION_BASE Detection The DETECTION object that will detects targets using the the Early Warning Radar network. - -- @return #AI_G2G_DISPATCHER self - function AI_G2G_DISPATCHER:New(Detection) - - -- Inherits from DETECTION_MANAGER - local self = BASE:Inherit(self, DETECTION_MANAGER:New(nil, Detection)) -- #AI_G2G_DISPATCHER - - self.Detection = Detection -- Functional.Detection#DETECTION_AREAS - - self:AddTransition( "Started", "Assign", "Started" ) - - self:__Start(5) - - return self - end - - --- Adds an APC group to transport troops to the front line. - -- @param #AI_G2G_DISPATCHER self - -- @param Wrapper.Group#GROUP group APC group. - -- @return #AI_G2G_DISPATCHER self - function AI_G2G_DISPATCHER:AddTransportAPC(group, homebase, resources) - self.TransportAPC[group]={} - self.TransportAPC[group].group=group - self.TransportAPC[group].homebase=homebase - self.TransportAPC[group].resources=resources - - -- Add homebase - if not self:GetHomebase(homebase) then - self:AddHomebase(homebase) - end - end - - --- Adds an APC group to transport troops to the front line. - -- @param #AI_G2G_DISPATCHER self - -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem The detected item. - function AI_G2G_DISPATCHER:EvaluateDetectedItem(DetectedItem) - local _coord=DetectedItem.Coordinate - _coord:MarkToAll("detected") - - - local _id=DetectedItem.ID - - - - end - - --- Adds an APC group to transport troops to the front line. - -- @param #AI_G2G_DISPATCHER self - -- @param Functional.Detection#DETECTION_BASE Detection The detection created by the @{Functional.Detection#DETECTION_BASE} derived object. - -- @return #boolean True if you want the task assigning to continue while false will cancel the loop. - function AI_G2G_DISPATCHER:ProcessDetected(Detection) - - - -- Now that all obsolete tasks are removed, loop through the detected targets. - for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do - - local DetectedItem = DetectedItem -- Functional.Detection#DETECTION_BASE.DetectedItem - local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT - local DetectedCount = DetectedSet:Count() - local DetectedZone = DetectedItem.Zone - - self:F( { "Target ID", DetectedItem.ItemID } ) - DetectedSet:Flush( self ) - - local DetectedID = DetectedItem.ID - local DetectionIndex = DetectedItem.Index - local DetectedItemChanged = DetectedItem.Changed - - env.info(string.format("FF detected item id %d, index = %d, changed = %s", DetectedID, DetectedItem.Index, tostring(DetectedItem.Changed))) - - - - end - - end - -end - diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 42d309c6a..5c2526919 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -1203,6 +1203,10 @@ end -- @param #string From -- @param #string To -- @param Wrapper.Unit#UNIT CargoCarrier +-- @param #number Speed +-- @param #number BoardDistance +-- @param #number LoadDistance +-- @param #number Angle function CARGO_PACKAGE:onafterOnBoarded( From, Event, To, CargoCarrier, Speed, BoardDistance, LoadDistance, Angle ) self:F() @@ -1218,6 +1222,7 @@ end -- @param #string Event -- @param #string From -- @param #string To +-- @param Wrapper.Unit#UNIT CargoCarrier -- @param #number Speed -- @param #number UnLoadDistance -- @param #number UnBoardDistance @@ -1261,6 +1266,7 @@ end -- @param #string From -- @param #string To -- @param Wrapper.Unit#UNIT CargoCarrier +-- @param #number Speed function CARGO_PACKAGE:onafterUnBoarded( From, Event, To, CargoCarrier, Speed ) self:F() @@ -1304,6 +1310,8 @@ end -- @param #string Event -- @param #string From -- @param #string To +-- @param Wrapper.Unit#UNIT CargoCarrier +-- @param #number Speed -- @param #number Distance -- @param #number Angle function CARGO_PACKAGE:onafterUnLoad( From, Event, To, CargoCarrier, Speed, Distance, Angle ) diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 459da2a0f..c2d45f142 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -61,8 +61,8 @@ do -- CARGO_GROUP -- @param #number LoadRadius (optional) Distance in meters until which a cargo is loaded into the carrier. Cargo outside this radius has to be routed by other means to within the radius to be loaded. -- @param #number NearRadius (optional) Once the units are within this radius of the carrier, they are actually loaded, i.e. disappear from the scene. -- @return #CARGO_GROUP Cargo group object. - function CARGO_GROUP:New( CargoGroup, Type, Name, LoadRadius ) - local self = BASE:Inherit( self, CARGO_REPORTABLE:New( Type, Name, 0, LoadRadius ) ) -- #CARGO_GROUP + function CARGO_GROUP:New( CargoGroup, Type, Name, LoadRadius, NearRadius ) + local self = BASE:Inherit( self, CARGO_REPORTABLE:New( Type, Name, 0, LoadRadius, NearRadius ) ) -- #CARGO_GROUP self:F( { Type, Name, LoadRadius } ) self.CargoSet = SET_CARGO:New() @@ -98,7 +98,7 @@ do -- CARGO_GROUP local Unit = UNIT:Register( CargoUnitName ) --local WeightUnit = Unit:GetDesc().massEmpty --WeightGroup = WeightGroup + WeightUnit - local CargoUnit = CARGO_UNIT:New( Unit, Type, CargoUnitName, 10 ) + local CargoUnit = CARGO_UNIT:New( Unit, Type, CargoUnitName, 10, LoadRadius, NearRadius ) self.CargoSet:Add( CargoUnitName, CargoUnit ) end @@ -257,10 +257,11 @@ do -- CARGO_GROUP --- Enter Boarding State. -- @param #CARGO_GROUP self - -- @param Wrapper.Unit#UNIT CargoCarrier -- @param #string Event -- @param #string From -- @param #string To + -- @param Wrapper.Unit#UNIT CargoCarrier + -- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier. function CARGO_GROUP:onenterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) --self:F( { CargoCarrier.UnitName, From, Event, To } ) @@ -303,11 +304,12 @@ do -- CARGO_GROUP end --- Leave Boarding State. - -- @param #CARGO_GROUP self - -- @param Wrapper.Unit#UNIT CargoCarrier + -- @param #CARGO_GROUP self -- @param #string Event -- @param #string From -- @param #string To + -- @param Wrapper.Unit#UNIT CargoCarrier + -- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier. function CARGO_GROUP:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) --self:F( { CargoCarrier.UnitName, From, Event, To } ) @@ -359,10 +361,11 @@ do -- CARGO_GROUP --- Enter UnBoarding State. -- @param #CARGO_GROUP self - -- @param Core.Point#POINT_VEC2 ToPointVec2 -- @param #string Event -- @param #string From -- @param #string To + -- @param Core.Point#POINT_VEC2 ToPointVec2 + -- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier. function CARGO_GROUP:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) self:F( {From, Event, To, ToPointVec2, NearRadius } ) @@ -401,10 +404,11 @@ do -- CARGO_GROUP --- Leave UnBoarding State. -- @param #CARGO_GROUP self - -- @param Core.Point#POINT_VEC2 ToPointVec2 -- @param #string Event -- @param #string From -- @param #string To + -- @param Core.Point#POINT_VEC2 ToPointVec2 + -- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier. function CARGO_GROUP:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) --self:F( { From, Event, To, ToPointVec2, NearRadius } ) @@ -438,10 +442,11 @@ do -- CARGO_GROUP --- UnBoard Event. -- @param #CARGO_GROUP self - -- @param Core.Point#POINT_VEC2 ToPointVec2 -- @param #string Event -- @param #string From -- @param #string To + -- @param Core.Point#POINT_VEC2 ToPointVec2 + -- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier. function CARGO_GROUP:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius, ... ) --self:F( { From, Event, To, ToPointVec2, NearRadius } ) @@ -454,10 +459,10 @@ do -- CARGO_GROUP --- Enter UnLoaded State. -- @param #CARGO_GROUP self - -- @param Core.Point#POINT_VEC2 -- @param #string Event -- @param #string From -- @param #string To + -- @param Core.Point#POINT_VEC2 function CARGO_GROUP:onenterUnLoaded( From, Event, To, ToPointVec2, ... ) --self:F( { From, Event, To, ToPointVec2 } ) @@ -467,7 +472,7 @@ do -- CARGO_GROUP self.CargoSet:ForEach( function( Cargo ) --Cargo:UnLoad( ToPointVec2 ) - local RandomVec2=ToPointVec2:GetRandomPointVec2InRadius(10) + local RandomVec2=ToPointVec2:GetRandomPointVec2InRadius(20, 10) Cargo:UnLoad( RandomVec2 ) end ) @@ -592,8 +597,7 @@ do -- CARGO_GROUP -- @param #CARGO_GROUP self -- @param Wrapper.Group#GROUP CargoCarrier -- @param #number NearRadius - -- @return #boolean The Cargo is near to the Carrier. - -- @return #nil The Cargo is not near to the Carrier. + -- @return #boolean The Cargo is near to the Carrier or #nil if the Cargo is not near to the Carrier. function CARGO_GROUP:IsNear( CargoCarrier, NearRadius ) self:F( {NearRadius = NearRadius } ) diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index 2a5f009ac..c7992256c 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -42,9 +42,9 @@ do -- CARGO_UNIT -- @param #number LoadRadius (optional) -- @param #number NearRadius (optional) -- @return #CARGO_UNIT - function CARGO_UNIT:New( CargoUnit, Type, Name, Weight, NearRadius ) - local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, Weight, NearRadius ) ) -- #CARGO_UNIT - self:I( { Type, Name, Weight, NearRadius } ) + function CARGO_UNIT:New( CargoUnit, Type, Name, Weight, LoadRadius, NearRadius ) + local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, Weight, LoadRadius, NearRadius ) ) -- #CARGO_UNIT + self:I( { Type, Name, Weight, LoadRadius, NearRadius } ) self:T( CargoUnit ) self.CargoObject = CargoUnit @@ -62,6 +62,7 @@ do -- CARGO_UNIT -- @param #string From -- @param #string To -- @param Core.Point#POINT_VEC2 ToPointVec2 + -- @param #number NearRadius (optional) Defaut 25 m. function CARGO_UNIT:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius ) self:F( { From, Event, To, ToPointVec2, NearRadius } ) @@ -131,6 +132,7 @@ do -- CARGO_UNIT -- @param #string From -- @param #string To -- @param Core.Point#POINT_VEC2 ToPointVec2 + -- @param #number NearRadius (optional) Defaut 100 m. function CARGO_UNIT:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius ) self:F( { From, Event, To, ToPointVec2, NearRadius } ) @@ -158,6 +160,7 @@ do -- CARGO_UNIT -- @param #string From -- @param #string To -- @param Core.Point#POINT_VEC2 ToPointVec2 + -- @param #number NearRadius (optional) Defaut 100 m. function CARGO_UNIT:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius ) self:F( { From, Event, To, ToPointVec2, NearRadius } ) @@ -281,7 +284,7 @@ do -- CARGO_UNIT -- @param #string From -- @param #string To -- @param Wrapper.Client#CLIENT CargoCarrier - -- @param #number NearRadius + -- @param #number NearRadius Default 25 m. function CARGO_UNIT:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) --self:F( { From, Event, To, CargoCarrier.UnitName, NearRadius } ) @@ -338,6 +341,7 @@ do -- CARGO_UNIT -- @param #string From -- @param #string To -- @param Wrapper.Unit#UNIT CargoCarrier + -- @param #number NearRadius Default 25 m. function CARGO_UNIT:onenterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) --self:F( { From, Event, To, CargoCarrier.UnitName, NearRadius } ) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index e3c478b14..311d94b22 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1095,6 +1095,37 @@ do -- COORDINATE return RoutePoint end + + --- Gets the nearest airbase with respect to the current coordinates. + -- @param #COORDINATE self + -- @param #number AirbaseCategory Category of the airbase. + -- @return Wrapper.Airbase#AIRBASE Closest Airbase to the given coordinate. + -- @return #number Distance to the closest airbase in meters. + function COORDINATE:GetClosestAirbase(AirbaseCategory) + local airbases=AIRBASE.GetAllAirbases() + + local closest=nil + local distmin=nil + -- Loop over all airbases. + for _,_airbase in pairs(airbases) do + local airbase=_airbase --Wrapper.Airbase#AIRBASE + local category=airbase:GetDesc().category + if AirbaseCategory and AirbaseCategory==category or AirbaseCategory==nil then + local dist=self:Get2DDistance(airbase:GetCoordinate()) + if closest==nil then + distmin=dist + closest=airbase + else + if dist Date: Wed, 8 Aug 2018 21:36:23 +0200 Subject: [PATCH 242/420] WAREHOUSE --- .../Moose/AI/AI_Cargo_Helicopter.lua | 3 +- .../Moose/Functional/Warehouse.lua | 109 +++++++++++------- 2 files changed, 67 insertions(+), 45 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index b774acf95..1e3141649 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -224,7 +224,8 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To ) if self.RoutePickup == true then if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 10 then - self:Load( Helicopter:GetPointVec2() ) + --self:Load( Helicopter:GetPointVec2() ) + self:Load() self.RoutePickup = false self.Relocating = true end diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 9ccef42e3..cf134349b 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -252,6 +252,15 @@ function WAREHOUSE:NewAirbase(airbase) return self end +--- Set a zone where the (ground) assets of the warehouse are spawned once requested. +-- @param #WAREHOUSE self +-- @param Core.Zone#ZONE zone The spawn zone. +-- @return #WAREHOUSE self +function WAREHOUSE:SetSpawnZone(zone) + self.spawnzone=zone + return self +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- FSM states ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -262,7 +271,7 @@ end -- @param #string Event Event. -- @param #string To To state. function WAREHOUSE:onafterStart(From, Event, To) - self:E(self.wid..string.format("Starting warehouse at airbase %s.", self.homebase:GetName())) + self:E(self.wid..string.format("Starting warehouse at airbase %s, category %d, coalition %d.", self.homebase:GetName(), self.category, self.coalition)) -- handle events -- event takeoff @@ -307,13 +316,7 @@ function WAREHOUSE:onafterStatus(From, Event, To) end -- Print queue. - env.info(self.wid.."Queue:") - for _,_qitem in ipairs(self.queue) do - local qitem=_qitem --#WAREHOUSE.Queueitem - local text=string.format("uid=%d, prio=%d, airbase=%s, descriptor: %s=%s, nasssets=%d, transport=%s, ntransport=%d", - qitem.uid, qitem.prio, qitem.airbase:GetName(), qitem.assetdesc,tostring(qitem.assetdescval),qitem.nasset,qitem.transporttype,qitem.ntransport) - env.info(text) - end + self:_PrintQueue() -- Check queue and handle requests if possible. local request=self:_CheckQueue() @@ -325,7 +328,7 @@ function WAREHOUSE:onafterStatus(From, Event, To) end -- Call status again in 30 sec. - self:__Status(30) + self:__Status(10) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -384,12 +387,15 @@ function WAREHOUSE:_CheckQueue() local okay=true -- Check if number of requested assets is in stock. local _instock=#self:_FilterStock(self.stock, qitem.assetdesc, qitem.assetdescval) + env.info(string.format("FF desc = %s val=%s number=%d", qitem.assetdesc, tostring(qitem.assetdescval),_instock)) if qitem.nasset > _instock then + env.info("FF check queue nasset > instock okay=false") okay=false end -- Check if enough transport units are in stock. _instock=#self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, qitem.transporttype) if qitem.ntransport > _instock then + env.info("FF check queue ntransport > instock okay=false") okay=false end return okay @@ -468,6 +474,13 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) ---------------------------------------------------------------- + -- New empty cargo set in case we need it. + local CargoGroups = SET_CARGO:New() + + --TODO: make nearradius depended on transport type and asset type. + local _loadradius=5000 + local _nearradius=35 + -- Filter the requested assets. local _assetstock=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval) @@ -482,17 +495,20 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) local _assetitem=_assetstock[i] --#WAREHOUSE.Stockitem -- Find a random point within the spawn zone. - local spawncoord=self.spawnzone:GetRandomCoordinate() - - spawncoord:MarkToAll(string.format("spawnpoint %d",i)) + local spawncoord=self.spawnzone:GetRandomCoordinate() -- Alias of the group. Spawn with ALIAS here or DCS crashes! - local _alias=string.format("%s_WHID%04d", _assetitem.templatename,_assetitem.id) + local _alias=string.format("%s_AssetID-%04d_RequestID-%04d", _assetitem.templatename,_assetitem.id,Request.uid) local _spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias) - local _group + local _group=nil --Wrapper.Group#GROUP + + -- Set a marker for the spawned group. + spawncoord:MarkToAll(string.format("Spawnpoint %s",_alias)) + if _assetitem.category==Group.Category.GROUND then -- Spawn ground troops. - _group=_spawn:SpawnFromCoordinate(spawncoord) + _group=_spawn:SpawnFromCoordinate(spawncoord) + env.info(string.format("FF spawning group %s", _alias)) elseif _assetitem.category==Group.Category.AIRPLANE or _assetitem.category==Group.Category.HELICOPTER then -- Spawn air units. local _takeoff=SPAWN.Takeoff.Cold @@ -512,6 +528,12 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) _cargotype=_assetitem.attribute _cargocategory=_assetitem.category table.insert(_delid,_assetitem.id) + + if Request.transporttype ~= WAREHOUSE.TransportType.SELFPROPELLED then + local cargogroup = CARGO_GROUP:New(_group, _alias, _alias, _loadradius, _nearradius) + CargoGroups:AddCargo(cargogroup) + end + end end @@ -551,22 +573,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) return end - ---------------------------------------------------------------- - -- New empty cargo set. - local CargoGroups = SET_CARGO:New() - - --TODO: make nearradius depended on transport type and asset type. - local _loadradius=5000 - local _nearradius=35 - - -- Add spawned groups to cargo group object. - for _i,_spawngroup in pairs(_spawngroups) do - local _name=string.format("%s %d", Request.assetdescval, _i) - env.info(string.format("FF cargo group %d: %s",_i,_name)) - local cargogroup = CARGO_GROUP:New(_spawngroup, Request.assetdescval, _name, _loadradius, _nearradius) - CargoGroups:AddCargo(cargogroup) - end - + env.info("FF cargo set name(s) = "..CargoGroups:GetObjectNames()) ---------------------------------------------------------------- local TransportSet = SET_GROUP:New() --:AddGroupsByName(Plane:GetName()) @@ -574,7 +581,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Pickup and depoly locations. local PickupAirbaseSet = SET_AIRBASE:New():AddAirbase(self.homebase) local DeployAirbaseSet = SET_AIRBASE:New():AddAirbase(Request.airbase) - local DeployZoneSet = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() + local DeployZoneSet = SET_ZONE:New():FilterPrefixes("Deploy"):FilterStart() --local bla=SET_ZONE:New():AddZonesByName(AddZoneNames) local CargoTransport --AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER @@ -764,9 +771,8 @@ end -- @param #WAREHOUSE self -- @param #string templategroupname Name of the late activated template group as defined in the mission editor. -- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1. --- @param #boolean istransport If true, this group will act as transport unit to transport other assets to another airbase. If false, this unit will not be used as transport unit. By default the behavior is determined for the group's attributes. -- @return #WAREHOUSE self -function WAREHOUSE:AddAsset(templategroupname, ngroups, istransport) +function WAREHOUSE:AddAsset(templategroupname, ngroups) -- Set default. local n=ngroups or 1 @@ -1069,10 +1075,11 @@ end -- @param #WAREHOUSE self -- @param #number _uid The unique id of the item to be deleted. function WAREHOUSE:_DeleteStockItem(_uid) - for _i,_item in pairs(self.stock) do - local item=_item --#WAREHOUSE.Stockitem + for i=1,#self.stock do + local item=self.stock[i] --#WAREHOUSE.Stockitem if item.id==_uid then - self.stock[_i]=nil + table.remove(self.stock,i) + break end end end @@ -1081,27 +1088,41 @@ end -- @param #WAREHOUSE self -- @param #number _uid The id of the item to be deleted. function WAREHOUSE:_DeleteQueueItem(_uid) - for _i,_item in pairs(self.queue) do - local item=_item --#WAREHOUSE.Queueitem + env.info("FF BEFORE delete queue") + self:_PrintQueue() + for i=1,#self.queue do + local item=self.queue[i] --#WAREHOUSE.Queueitem if item.uid==_uid then - self.queue[_i]=nil + table.remove(self.queue,i) + break end end + env.info("FF AFTER delete queue") + self:_PrintQueue() end --- Sort requests queue wrt prio and request uid. -- @param #WAREHOUSE self function WAREHOUSE:_SortQueue() - self:F2() - - -- Sort results table wrt times they have already been engaged. + self:F3() + -- Sort. local function _sort(a, b) return (a.prio < b.prio) or (a.prio==b.prio and a.uid < b.uid) end table.sort(self.queue, _sort) - end +--- Prints the queue to DCS.log file. +-- @param #WAREHOUSE self +function WAREHOUSE:_PrintQueue() + env.info(self.wid.."Queue:") + for _,_qitem in ipairs(self.queue) do + local qitem=_qitem --#WAREHOUSE.Queueitem + local text=string.format("uid=%d, prio=%d, airbase=%s (category=%d), descriptor: %s=%s, nasssets=%d, transport=%s, ntransport=%d", + qitem.uid, qitem.prio, qitem.airbase:GetName(),qitem.category, qitem.assetdesc,tostring(qitem.assetdescval),qitem.nasset,qitem.transporttype,qitem.ntransport) + env.info(text) + end +end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- From a14c2ef58977d9dab3460dd4df37133675bf750d Mon Sep 17 00:00:00 2001 From: FlightControl Date: Wed, 8 Aug 2018 22:41:02 +0200 Subject: [PATCH 243/420] Work in progress. Limit functions for POSITIONABLE. --- .../Moose/AI/AI_Cargo_Airplane.lua | 4 +- .../Moose/AI/AI_Cargo_Dispatcher.lua | 1 + .../Moose/Wrapper/Positionable.lua | 139 ++++++++++++------ 3 files changed, 93 insertions(+), 51 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 003100967..54f8b0cdd 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -188,7 +188,6 @@ function AI_CARGO_AIRPLANE:SetCarrier( Airplane ) function Airplane:OnEventEngineShutdown( EventData ) - self:F("Calling") AICargo:Landed( self.Airplane ) end @@ -233,8 +232,6 @@ end function AI_CARGO_AIRPLANE:onafterLanded( Airplane, From, Event, To ) self:F({Airplane, From, Event, To}) - self:F({IsAlive=Airplane:IsAlive()}) - self:F({RoutePickup=self.RoutePickup}) if Airplane and Airplane:IsAlive()~=nil then @@ -406,6 +403,7 @@ function AI_CARGO_AIRPLANE:onafterLoaded( Airplane, From, Event, To ) env.info("FF troops loaded into cargo plane") if Airplane and Airplane:IsAlive() then + -- Check if another cargo can be loaded into the airplane. end end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 7ac009283..ea76ec694 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -403,6 +403,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() -- The Pickup sequence ... -- Check if this Carrier need to go and Pickup something... + -- So, if the cargo bay is not full yet with cargo to be loaded ... self:I( { IsTransporting = AI_Cargo:IsTransporting() } ) if AI_Cargo:IsTransporting() == false then -- ok, so there is a free Carrier diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 23b85806a..888cf3232 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -798,56 +798,99 @@ function POSITIONABLE:GetLaserCode() --R2.1 return self.LaserCode end ---- Add cargo. --- @param #POSITIONABLE self --- @param Core.Cargo#CARGO Cargo --- @return #POSITIONABLE -function POSITIONABLE:AddCargo( Cargo ) - self.__.Cargo[Cargo] = Cargo - return self -end +do -- Cargo ---- Get all contained cargo. --- @param #POSITIONABLE self --- @return #POSITIONABLE -function POSITIONABLE:GetCargo() - return self.__.Cargo -end - - - ---- Remove cargo. --- @param #POSITIONABLE self --- @param Core.Cargo#CARGO Cargo --- @return #POSITIONABLE -function POSITIONABLE:RemoveCargo( Cargo ) - self.__.Cargo[Cargo] = nil - return self -end - ---- Returns if carrier has given cargo. --- @param #POSITIONABLE self --- @return Core.Cargo#CARGO Cargo -function POSITIONABLE:HasCargo( Cargo ) - return self.__.Cargo[Cargo] -end - ---- Clear all cargo. --- @param #POSITIONABLE self -function POSITIONABLE:ClearCargo() - self.__.Cargo = {} -end - ---- Get cargo item count. --- @param #POSITIONABLE self --- @return Core.Cargo#CARGO Cargo -function POSITIONABLE:CargoItemCount() - local ItemCount = 0 - for CargoName, Cargo in pairs( self.__.Cargo ) do - ItemCount = ItemCount + Cargo:GetCount() + --- Add cargo. + -- @param #POSITIONABLE self + -- @param Core.Cargo#CARGO Cargo + -- @return #POSITIONABLE + function POSITIONABLE:AddCargo( Cargo ) + self.__.Cargo[Cargo] = Cargo + return self end - return ItemCount -end + + --- Get all contained cargo. + -- @param #POSITIONABLE self + -- @return #POSITIONABLE + function POSITIONABLE:GetCargo() + return self.__.Cargo + end + + + + --- Remove cargo. + -- @param #POSITIONABLE self + -- @param Core.Cargo#CARGO Cargo + -- @return #POSITIONABLE + function POSITIONABLE:RemoveCargo( Cargo ) + self.__.Cargo[Cargo] = nil + return self + end + + --- Returns if carrier has given cargo. + -- @param #POSITIONABLE self + -- @return Core.Cargo#CARGO Cargo + function POSITIONABLE:HasCargo( Cargo ) + return self.__.Cargo[Cargo] + end + + --- Clear all cargo. + -- @param #POSITIONABLE self + function POSITIONABLE:ClearCargo() + self.__.Cargo = {} + end + + --- Get cargo item count. + -- @param #POSITIONABLE self + -- @return Core.Cargo#CARGO Cargo + function POSITIONABLE:CargoItemCount() + local ItemCount = 0 + for CargoName, Cargo in pairs( self.__.Cargo ) do + ItemCount = ItemCount + Cargo:GetCount() + end + return ItemCount + end + + --- Get Cargo Bay Free Volume in m3. + -- @param #POSITIONABLE self + -- @return #number CargoBayFreeVolume + function POSITIONABLE:GetCargoBayFreeVolume() + local CargoVolume = 0 + for CargoName, Cargo in pairs( self.__.Cargo ) do + CargoVolume = CargoVolume + Cargo:GetVolume() + end + return self.__.CargoBayVolumeLimit - CargoVolume + end + + --- Get Cargo Bay Free Weight in kg. + -- @param #POSITIONABLE self + -- @return #number CargoBayFreeWeight + function POSITIONABLE:GetCargoBayFreeWeight() + local CargoWeight = 0 + for CargoName, Cargo in pairs( self.__.Cargo ) do + CargoWeight = CargoWeight + Cargo:GetWeight() + end + return self.__.CargoBayWeightLimit - CargoWeight + end + + --- Get Cargo Bay Volume Limit in m3. + -- @param #POSITIONABLE self + -- @param #number VolumeLimit + function POSITIONABLE:SetCargoBayVolumeLimit( VolumeLimit ) + self.__.CargoBayVolumeLimit = VolumeLimit + end + + --- Get Cargo Bay Weight Limit in kg. + -- @param #POSITIONABLE self + -- @param #number WeightLimit + function POSITIONABLE:GetCargoBayFreeWeightLimit( WeightLimit ) + self.__.CargoBayWeightLimit = WeightLimit + end + + + + +end --- Cargo --- Signal a flare at the position of the POSITIONABLE. -- @param #POSITIONABLE self From 498ec4f86bc5f9c8dd16794b4d5fc3c19079b603 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 9 Aug 2018 00:56:25 +0200 Subject: [PATCH 244/420] WAREHOUSE etc --- Moose Development/Moose/Core/Point.lua | 15 +++-- Moose Development/Moose/Core/Set.lua | 18 ++++-- Moose Development/Moose/Core/Zone.lua | 3 +- .../Moose/Functional/Warehouse.lua | 61 ++++++++++++++++--- .../Moose/Wrapper/Controllable.lua | 55 +++++++++++++++++ 5 files changed, 133 insertions(+), 19 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 8c456137d..55ba4ffda 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1205,13 +1205,18 @@ do -- COORDINATE return self:GetClosestParkingSpot(airbase, terminaltype, false) end - --- Gets the nearest coordinate to a road. + --- Gets the nearest coordinate to a road (or railroad). -- @param #COORDINATE self + -- @param #boolean Railroad (Optional) If true, closest point to railroad is returned rather than closest point to conventional road. Default false. -- @return #COORDINATE Coordinate of the nearest road. - function COORDINATE:GetClosestPointToRoad() - local x,y = land.getClosestPointOnRoads("roads", self.x, self.z) - local vec2={ x = x, y = y } - return COORDINATE:NewFromVec2(vec2) + function COORDINATE:GetClosestPointToRoad(Railroad) + local roadtype="roads" + if Railroad==true then + roadtype="railroads" + end + local x,y = land.getClosestPointOnRoads(roadtype, self.x, self.z) + local vec2={ x = x, y = y } + return COORDINATE:NewFromVec2(vec2) end diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 12a611250..6a284ab73 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -4735,10 +4735,20 @@ function SET_ZONE:New() return self end +--- Add ZONE to SET_ZONE. +-- @param Core.Set#SET_ZONE self +-- @param Core.Zone#ZONE Zone Zone to add to the set. +-- @return #SET_ZONE self +function SET_ZONE:AddZone(Zone) + self:Add(Zone:GetName(), Zone) + return self +end + + --- Add ZONEs to SET_ZONE. -- @param Core.Set#SET_ZONE self -- @param #string AddZoneNames A single name or an array of ZONE_BASE names. --- @return self +-- @return #SET_ZONE self function SET_ZONE:AddZonesByName( AddZoneNames ) local AddZoneNamesArray = ( type( AddZoneNames ) == "table" ) and AddZoneNames or { AddZoneNames } @@ -4753,7 +4763,7 @@ end --- Remove ZONEs from SET_ZONE. -- @param Core.Set#SET_ZONE self -- @param Core.Zone#ZONE_BASE RemoveZoneNames A single name or an array of ZONE_BASE names. --- @return self +-- @return #SET_ZONE self function SET_ZONE:RemoveZonesByName( RemoveZoneNames ) local RemoveZoneNamesArray = ( type( RemoveZoneNames ) == "table" ) and RemoveZoneNames or { RemoveZoneNames } @@ -4779,8 +4789,7 @@ end --- Get a random zone from the set. -- @param #SET_ZONE self --- @return Core.Zone#ZONE_BASE The random Zone. --- @return #nil if no zone in the collection. +-- @return Core.Zone#ZONE_BASE The random Zone or #nil if no zone in the collection. function SET_ZONE:GetRandomZone() if self:Count() ~= 0 then @@ -4806,6 +4815,7 @@ end --- Set a zone probability. -- @param #SET_ZONE self -- @param #string ZoneName The name of the zone. +-- @param #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability. function SET_ZONE:SetZoneProbability( ZoneName, ZoneProbability ) local Zone = self:FindZone( ZoneName ) Zone:SetZoneProbability( ZoneProbability ) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 82c7a0be7..d0e368d3a 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -317,7 +317,8 @@ end --- Set the randomization probability of a zone to be selected. -- @param #ZONE_BASE self --- @param ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability. +-- @param #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability. +-- @return #ZONE_BASE self function ZONE_BASE:SetZoneProbability( ZoneProbability ) self:F( { self:GetName(), ZoneProbability = ZoneProbability } ) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index cf134349b..54c75cc50 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -117,8 +117,10 @@ WAREHOUSE.Descriptor = { ATTRIBUTE="attribute", } ---- Warehouse unit categories. These are used for +--- Warehouse generalited categories. -- @type WAREHOUSE.Attribute +-- @field #string TRANSPORT_PLANE Airplane with transport capability. Usually bigger. +-- @field #string TRANSPORT_HELO Helicopter with transport capability. WAREHOUSE.Attribute = { TRANSPORT_PLANE="Transport_Plane", TRANSPORT_HELO="Transport_Helo", @@ -132,6 +134,7 @@ WAREHOUSE.Attribute = { BOMBER="Bomber", TANK="Tank", TRUCK="Truck", + TRAIN="Train", SHIP="Ship", OTHER="Other", } @@ -504,23 +507,30 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Set a marker for the spawned group. spawncoord:MarkToAll(string.format("Spawnpoint %s",_alias)) + + local _attribute=_assetitem.attribute if _assetitem.category==Group.Category.GROUND then -- Spawn ground troops. _group=_spawn:SpawnFromCoordinate(spawncoord) - env.info(string.format("FF spawning group %s", _alias)) + env.info(string.format("FF spawning group %s", _alias)) elseif _assetitem.category==Group.Category.AIRPLANE or _assetitem.category==Group.Category.HELICOPTER then -- Spawn air units. local _takeoff=SPAWN.Takeoff.Cold local _terminal=AIRBASE.TerminalType.OpenBig - if _assetitem.attribute==WAREHOUSE.Attribute.FIGHTER then + if _attribute==WAREHOUSE.Attribute.FIGHTER then _terminal=AIRBASE.TerminalType.FighterAircraft - elseif _assetitem.attribute==WAREHOUSE.Attribute.BOMBER then + elseif _attribute==WAREHOUSE.Attribute.BOMBER or _attribute==WAREHOUSE.Attribute.TRANSPORT_PLANE or _attribute==WAREHOUSE.Attribute.TANKER or _attribute==WAREHOUSE.Attribute.AWACS then _terminal=AIRBASE.TerminalType.OpenBig + elseif _attribute==WAREHOUSE.Attribute.TRANSPORT_HELO or _attribute==WAREHOUSE.Attribute.ATTACKHELICOPTER then + _terminal=AIRBASE.TerminalType.HelicopterUsable end _group=_spawn:InitUnControlled(true):SpawnAtAirbase(self.homebase,_takeoff, nil,_terminal, true) elseif _assetitem.category==Group.Category.TRAIN then - + local _railroad=self.coordinate:GetClosestPointToRoad(true) + if _railroad then + _group=_spawn:SpawnFromCoordinate(_railroad) + end end if _group then @@ -561,7 +571,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) elseif _cargocategory==Group.Category.SHIP then elseif _cargocategory==Group.Category.TRAIN then - + self:_RouteTrain(group, ToCoordinate) end end @@ -581,8 +591,10 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Pickup and depoly locations. local PickupAirbaseSet = SET_AIRBASE:New():AddAirbase(self.homebase) local DeployAirbaseSet = SET_AIRBASE:New():AddAirbase(Request.airbase) - local DeployZoneSet = SET_ZONE:New():FilterPrefixes("Deploy"):FilterStart() - --local bla=SET_ZONE:New():AddZonesByName(AddZoneNames) + --local DeployZoneSet = SET_ZONE:New():FilterPrefixes("Deploy"):FilterStart() + --local DeployZoneSet = SET_ZONE:New():AddZonesByName(Request.airbase:GetZone():GetName()) + local DeployZoneSet = SET_ZONE:New():AddZone(Request.airbase:GetZone()) + local CargoTransport --AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER -- Filter the requested transport assets. @@ -651,6 +663,8 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) TransportSet:AddGroup(spawngroup) table.insert(_delid,_assetitem.id) + else + env.info("FF error spawngroup helo transport does not exist!") end end @@ -918,6 +932,32 @@ function WAREHOUSE:_RouteAir(Aircraft, ToAirbase, Speed) end end +--- Route trains to their destination - or at least to the closest point on rail of the desired final destination. +-- @param #WAREHOUSE self +-- @param Wrapper.Group#GROUP Group The train group. +-- @param Core.Point#COORDINATE Coordinate of the destination. Tail will be routed to the closest point +-- @param #number Speed Speed in km/h to drive to the destination coordinate. Default is 60% of max possible speed the unit can go. +function WAREHOUSE:_RouteTrain(Group, Coordinate, Speed) + + if Group and Group:IsAlive() then + + local _speed=Speed or Group:GetSpeedMax()*0.6 + + -- Create a + local Waypoints = Group:TaskGroundOnRailRoads(Coordinate, Speed) + + -- Task function triggering the arrived event. + local TaskFunction = Group:TaskFunction("WAREHOUSE._Arrived", self) + + -- Put task function on last waypoint. + local Waypoint = Waypoints[#Waypoints] + Group:SetTaskWaypoint( Waypoint, TaskFunction ) + + -- Route group to destination. + Group:Route(Waypoints, 1) + end +end + --- Filter stock assets by table entry. -- @param #WAREHOUSE self -- @param #table stock Table holding all assets in stock of the warehouse. Each entry is of type @{#WAREHOUSE.Stockitem}. @@ -997,7 +1037,8 @@ function WAREHOUSE:_GetAttribute(groupname) local attackhelicopter=group:HasAttribute("Attack helicopters") local bomber=group:HasAttribute("Bombers") local tank=group:HasAttribute("Old Tanks") or group:HasAttribute("Modern Tanks") - local truck=group:HasAttribute("Trucks") + local truck=group:HasAttribute("Trucks") and not group:GetCategory()==Group.Category.TRAIN + local train=group:GetCategory()==Group.Category.TRAIN -- Debug output. --[[ @@ -1039,6 +1080,8 @@ function WAREHOUSE:_GetAttribute(groupname) attribute=WAREHOUSE.Attribute.TANK elseif truck then attribute=WAREHOUSE.Attribute.TRUCK + elseif train then + attribute=WAREHOUSE.Attribute.TRAIN else attribute=WAREHOUSE.Attribute.OTHER end diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 84c25a1f4..767845e21 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1996,6 +1996,28 @@ do -- Route methods return self end + + --- Make the TRAIN Controllable to drive towards a specific point using railroads. + -- @param #CONTROLLABLE self + -- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to. + -- @param #number Speed (Optional) Speed in km/h. The default speed is 20 km/h. + -- @param #number DelaySeconds (Optional) Wait for the specified seconds before executing the Route. Default is one second. + -- @return #CONTROLLABLE The CONTROLLABLE. + function CONTROLLABLE:RouteGroundOnRailRoads( ToCoordinate, Speed, DelaySeconds) + + -- Defaults. + Speed=Speed or 20 + DelaySeconds=DelaySeconds or 1 + + -- Get the route task. + local route=self:TaskGroundOnRailRoads(ToCoordinate, Speed) + + -- Route controllable to destination. + self:Route( route, DelaySeconds ) + + return self + end + --- Make a task for a GROUND Controllable to drive towards a specific point using (mostly) roads. @@ -2077,7 +2099,40 @@ do -- Route methods return route end + --- Make a task for a TRAIN Controllable to drive towards a specific point using railroad. + -- @param #CONTROLLABLE self + -- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to. + -- @param #number Speed (Optional) Speed in km/h. The default speed is 20 km/h. + -- @return Task + function CONTROLLABLE:TaskGroundOnRailRoads(ToCoordinate, Speed) + self:F2({ToCoordinate=ToCoordinate, Speed=Speed}) + + -- Defaults. + Speed=Speed or 20 + -- Current coordinate. + local FromCoordinate = self:GetCoordinate() + + -- Get path and path length on railroad. + local PathOnRail, LengthOnRail=FromCoordinate:GetPathOnRoad(ToCoordinate, false, true) + + -- Debug info. + self:T(string.format("Length on railroad = %.3f km", LengthOnRail/1000)) + + -- Route, ground waypoints along road. + local route={} + + -- Check if a valid path on railroad could be found. + if PathOnRail then + + table.insert(route, PathOnRail[1]:WaypointGround(Speed, "On Railroad")) + table.insert(route, PathOnRail[2]:WaypointGround(Speed, "On Railroad")) + + end + + return route + end + --- Make the AIR Controllable fly towards a specific point. -- @param #CONTROLLABLE self -- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to. From a2aa482bc1899471107d66341e1a4f55836b6708 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 9 Aug 2018 16:23:35 +0200 Subject: [PATCH 245/420] WH --- Moose Development/Moose/Core/Spawn.lua | 9 +- .../Moose/Functional/Warehouse.lua | 170 +++++++++++++----- Moose Development/Moose/Wrapper/Airbase.lua | 5 +- 3 files changed, 140 insertions(+), 44 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index a9817109e..586ac8396 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1299,6 +1299,7 @@ end -- @param #number TakeoffAltitude (optional) The altitude above the ground. -- @param Wrapper.Airbase#AIRBASE.TerminalType TerminalType (optional) The terminal type the aircraft should be spawned at. See @{Wrapper.Airbase#AIRBASE.TerminalType}. -- @param #boolean EmergencyAirSpawn (optional) If true (default), groups are spawned in air if there is no parking spot at the airbase. If false, nothing is spawned if no parking spot is available. +-- @param #table Parkingdata (optional) Table holding the coordinates and terminal ids for all units of the group. Spawning will be forced to happen at exactily these spots! -- @return Wrapper.Group#GROUP that was spawned or nil when nothing was spawned. -- @usage -- Spawn_Plane = SPAWN:New( "Plane" ) @@ -1319,7 +1320,7 @@ end -- -- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Cold, nil, AIRBASE.TerminalType.OpenBig ) -- -function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalType, EmergencyAirSpawn ) -- R2.2, R2.4 +function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalType, EmergencyAirSpawn, Parkingdata ) -- R2.2, R2.4 self:F( { self.SpawnTemplatePrefix, SpawnAirbase, Takeoff, TakeoffAltitude, TerminalType } ) -- Get position of airbase. @@ -1434,6 +1435,10 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT self:T(string.format("Group %s is spawned on farp/ship/runway %s.", self.SpawnTemplatePrefix, SpawnAirbase:GetName())) nfree=SpawnAirbase:GetFreeParkingSpotsNumber(termtype, true) spots=SpawnAirbase:GetFreeParkingSpotsTable(termtype, true) + elseif Parkingdata~=nil then + -- Parking data explicitly set by user as input parameter. + nfree=#Parkingdata + spots=Parkingdata else if ishelo then if termtype==nil then @@ -1511,7 +1516,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT PointVec3=spots[1].Coordinate else - -- If there is absolutely not spot ==> air start! + -- If there is absolutely no spot ==> air start! _notenough=true end diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 54c75cc50..01d96b243 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -152,22 +152,24 @@ WAREHOUSE.TransportType = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.1.0" +WAREHOUSE.version="0.1.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehuse todo list. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Add event handlers. --- TODO: Add AI_APC --- TODO: Add AI_HELICOPTER +-- DONE: Add AI_CARGO_AIRPLANE +-- DONE: Add AI_CARGO_APC +-- DONE: Add AI_CARGO_HELICOPTER -- TODO: Write documentation. --- TODO: Put active groups into the warehouse. +-- TODO: Put active groups into the warehouse, e.g. when they were transported to this warehouse. -- TODO: Spawn warehouse assets as uncontrolled or AI off and activate them when requested. -- TODO: Handle cases with immobile units. --- TODO: Add queue. +-- DONE: Add queue. -- TODO: How to handle multiple units in a transport group? --- TODO: Switch to AI_XXX_DISPATCHER +-- DONE: Switch to AI_CARGO_XXX_DISPATCHER +-- TODO: Add phyical object, if destroyed asssets are gone. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor(s) @@ -178,7 +180,7 @@ WAREHOUSE.version="0.1.0" -- @param Wrapper.Airbase#AIRBASE airbase The airbase at which the warehouse is constructed. -- @return #WAREHOUSE self function WAREHOUSE:NewAirbase(airbase) - BASE:E({airbase=airbase}) + BASE:E({airbase=airbase:GetName()}) -- Print version. env.info(string.format("Adding warehouse v%s for airbase %s", WAREHOUSE.version, airbase:GetName())) @@ -200,8 +202,6 @@ function WAREHOUSE:NewAirbase(airbase) -- Define the default spawn zone. self.spawnzone=ZONE_RADIUS:New("Spawnzone",_road, 200) - self.spawnzone:BoundZone(60,country.id.GERMANY) - self.spawnzone:GetCoordinate():MarkToAll("Spawnzone") -- Add FSM transitions. self:AddTransition("*", "Start", "Running") @@ -275,6 +275,10 @@ end -- @param #string To To state. function WAREHOUSE:onafterStart(From, Event, To) self:E(self.wid..string.format("Starting warehouse at airbase %s, category %d, coalition %d.", self.homebase:GetName(), self.category, self.coalition)) + + -- Debug mark spawn zone. + self.spawnzone:BoundZone(60,country.id.GERMANY) + self.spawnzone:GetCoordinate():MarkToAll("Spawnzone of warehouse "..self.homebase:GetName()) -- handle events -- event takeoff @@ -326,7 +330,6 @@ function WAREHOUSE:onafterStatus(From, Event, To) -- Execute the request. If the request is really executed, it is also deleted from the queue. if request then - --self:Request(request.airbase, request.assetdesc, request.assetdescval, request.nasset, request.transporttype, request.ntransport) self:Request(request) end @@ -390,15 +393,12 @@ function WAREHOUSE:_CheckQueue() local okay=true -- Check if number of requested assets is in stock. local _instock=#self:_FilterStock(self.stock, qitem.assetdesc, qitem.assetdescval) - env.info(string.format("FF desc = %s val=%s number=%d", qitem.assetdesc, tostring(qitem.assetdescval),_instock)) if qitem.nasset > _instock then - env.info("FF check queue nasset > instock okay=false") okay=false end -- Check if enough transport units are in stock. _instock=#self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, qitem.transporttype) if qitem.ntransport > _instock then - env.info("FF check queue ntransport > instock okay=false") okay=false end return okay @@ -449,7 +449,6 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request) local _assetattribute=self:_GetAttribute(_stockitem.templatename) - -- Check that a transport unit is available. if Request.transporttype~=WAREHOUSE.TransportType.SELFPROPELLED then local _instock=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, Request.transporttype) @@ -466,6 +465,25 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request) return true end +--- Get the proper terminal type based on generalized attribute of the group. +--@param #WAREHOUSE self +--@param #WAREHOUSE.Attribute _attribute Generlized attibute of unit. +function WAREHOUSE:_GetTerminal(_attribute) + + local _terminal=AIRBASE.TerminalType.OpenBig + if _attribute==WAREHOUSE.Attribute.FIGHTER then + -- Fighter ==> small. + _terminal=AIRBASE.TerminalType.FighterAircraft + elseif _attribute==WAREHOUSE.Attribute.BOMBER or _attribute==WAREHOUSE.Attribute.TRANSPORT_PLANE or _attribute==WAREHOUSE.Attribute.TANKER or _attribute==WAREHOUSE.Attribute.AWACS then + -- Bigger aircraft. + _terminal=AIRBASE.TerminalType.OpenBig + elseif _attribute==WAREHOUSE.Attribute.TRANSPORT_HELO or _attribute==WAREHOUSE.Attribute.ATTACKHELICOPTER then + -- Helicopter. + _terminal=AIRBASE.TerminalType.HelicopterUsable + end + +end + --- On after "Request" event. Initiates the transport of the assets to the requesting airbase. -- @param #WAREHOUSE self -- @param #string From From state. @@ -475,7 +493,9 @@ end function WAREHOUSE:onafterRequest(From, Event, To, Request) --env.info(self.wid..string.format("Airbase %s requesting asset %s = %s.", Airbase:GetName(), tostring(AssetDescriptor), tostring(AssetDescriptorValue))) - ---------------------------------------------------------------- + ------------------------------------------------------------------------------------------------------------------------------------ + -- Cargo assets. + ------------------------------------------------------------------------------------------------------------------------------------ -- New empty cargo set in case we need it. local CargoGroups = SET_CARGO:New() @@ -487,11 +507,56 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Filter the requested assets. local _assetstock=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval) + -- General type and category (GROUND,...) + local _cargotype=_assetstock[1].attribute --#WAREHOUSE.Attribute + local _cargocategory=_assetstock[1].category --DCS#Group.Category + + --_cargotype. + -- Spawn the assets. local _delid={} local _spawngroups={} local _cargotype local _cargocategory + + + -- Now we try to find all parking spots for all cargo groups in advance. Due to the for loop, the parking spots do not get updated while spawning. + local Parking={} + + if _cargocategory==Group.Category.AIRPLANE or _cargocategory==Group.Category.HELICOPTER then + + -- Count total number of units that will be spawned. + local ntotal=0 + for i=1,Request.ntransport do + local _assetitem=_assetstock[i] --#WAREHOUSE.Stockitem + local group=GROUP:FindByName(_assetitem.templatename) + ntotal=ntotal + #group:GetUnits() + end + + -- All spots are based on the same template group which must not be the case. + -- But I cant think of a better way to fetch all parking spots in advance. + local group=GROUP:FindByName(_assetstock[1].templatename) + local terminaltype=self:_GetTerminal(_assetstock[1].attribute) + local allspots=self.homebase:FindFreeParkingSpotForAircraft(group, terminaltype, 50, true, true, false, false, ntotal) + + env.info("FF allspots = "..#allspots) + env.info("FF notal = "..ntotal) + + -- No rearrange them again for each asset + local k=1 + for i=1,Request.ntransport do + local _assetitem=_assetstock[i] --#WAREHOUSE.Stockitem + local spots={} + local nunits=#GROUP:FindByName(_assetitem.templatename):GetUnits() + for j=1,nunits do + table.insert(spots,allspots[k]) + k=k+1 + end + table.insert(Parking, spots) + end + end + + -- Loop over cargo requests. for i=1,Request.nasset do -- Get stock item. @@ -503,6 +568,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Alias of the group. Spawn with ALIAS here or DCS crashes! local _alias=string.format("%s_AssetID-%04d_RequestID-%04d", _assetitem.templatename,_assetitem.id,Request.uid) local _spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias) + local _group=nil --Wrapper.Group#GROUP -- Set a marker for the spawned group. @@ -511,39 +577,39 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) local _attribute=_assetitem.attribute if _assetitem.category==Group.Category.GROUND then + -- Spawn ground troops. _group=_spawn:SpawnFromCoordinate(spawncoord) env.info(string.format("FF spawning group %s", _alias)) + elseif _assetitem.category==Group.Category.AIRPLANE or _assetitem.category==Group.Category.HELICOPTER then + -- Spawn air units. local _takeoff=SPAWN.Takeoff.Cold - local _terminal=AIRBASE.TerminalType.OpenBig - if _attribute==WAREHOUSE.Attribute.FIGHTER then - _terminal=AIRBASE.TerminalType.FighterAircraft - elseif _attribute==WAREHOUSE.Attribute.BOMBER or _attribute==WAREHOUSE.Attribute.TRANSPORT_PLANE or _attribute==WAREHOUSE.Attribute.TANKER or _attribute==WAREHOUSE.Attribute.AWACS then - _terminal=AIRBASE.TerminalType.OpenBig - elseif _attribute==WAREHOUSE.Attribute.TRANSPORT_HELO or _attribute==WAREHOUSE.Attribute.ATTACKHELICOPTER then - _terminal=AIRBASE.TerminalType.HelicopterUsable - end - _group=_spawn:InitUnControlled(true):SpawnAtAirbase(self.homebase,_takeoff, nil,_terminal, true) + + _group=_spawn:InitUnControlled(true):SpawnAtAirbase(self.homebase,_takeoff, nil, nil, true, Parking[i]) + elseif _assetitem.category==Group.Category.TRAIN then + + -- Spawn train. local _railroad=self.coordinate:GetClosestPointToRoad(true) if _railroad then _group=_spawn:SpawnFromCoordinate(_railroad) end + end if _group then _spawngroups[i]=_group - _cargotype=_assetitem.attribute - _cargocategory=_assetitem.category table.insert(_delid,_assetitem.id) + -- Add groups to cargo. if Request.transporttype ~= WAREHOUSE.TransportType.SELFPROPELLED then local cargogroup = CARGO_GROUP:New(_group, _alias, _alias, _loadradius, _nearradius) CargoGroups:AddCargo(cargogroup) end - + else + self:E(self.wid.."ERROR: cargo asset could not be spawned!") end end @@ -552,7 +618,9 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) self:_DeleteStockItem(_id) end - ---------------------------------------------------------------- + ------------------------------------------------------------------------------------------------------------------------------------ + -- Self propelled assets. + ------------------------------------------------------------------------------------------------------------------------------------ -- No transport unit requested. Assets go by themselfes. if Request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then @@ -562,6 +630,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) local group=_spawngroup --Wrapper.Group#GROUP local ToCoordinate=Request.airbase:GetZone():GetRandomCoordinate() + -- Route cargo to their destination. if _cargocategory==Group.Category.GROUND then self:_RouteGround(group, ToCoordinate) elseif _cargocategory==Group.Category.AIRPLANE then @@ -569,7 +638,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) elseif _cargocategory==Group.Category.HELICOPTER then self:_RouteAir(group, Request.airbase) elseif _cargocategory==Group.Category.SHIP then - + self:E("ERROR: self propelled ship not implemented yet!") elseif _cargocategory==Group.Category.TRAIN then self:_RouteTrain(group, ToCoordinate) end @@ -582,19 +651,22 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- No cargo transport necessary. return end + + ------------------------------------------------------------------------------------------------------------------------------------ + -- Transport assets and dispachers + ------------------------------------------------------------------------------------------------------------------------------------ env.info("FF cargo set name(s) = "..CargoGroups:GetObjectNames()) ---------------------------------------------------------------- - local TransportSet = SET_GROUP:New() --:AddGroupsByName(Plane:GetName()) + local TransportSet = SET_GROUP:New() - -- Pickup and depoly locations. + -- Pickup and deploy zones/bases. local PickupAirbaseSet = SET_AIRBASE:New():AddAirbase(self.homebase) local DeployAirbaseSet = SET_AIRBASE:New():AddAirbase(Request.airbase) - --local DeployZoneSet = SET_ZONE:New():FilterPrefixes("Deploy"):FilterStart() - --local DeployZoneSet = SET_ZONE:New():AddZonesByName(Request.airbase:GetZone():GetName()) local DeployZoneSet = SET_ZONE:New():AddZone(Request.airbase:GetZone()) + -- Cargo dispatcher. local CargoTransport --AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER -- Filter the requested transport assets. @@ -602,6 +674,15 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Dependent on transport type, spawn the transports and set up the dispatchers. if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then + + -- Now we try to find all parking spots for all transport groups in advance. Due to the for loop, the parking spots do not get updated while spawning. + local Parking={} + for i=1,Request.ntransport do + local _assetitem=_assetstock[i] --#WAREHOUSE.Stockitem + local group=GROUP:FindByName(_assetitem.templatename) + local spots=self.homebase:FindFreeParkingSpotForAircraft(group, AIRBASE.TerminalType.OpenBig) + table.insert(Parking, spots) + end -- Spawn the transport groups. local _delid={} @@ -609,14 +690,14 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Get stock item. local _assetitem=_assetstock[i] --#WAREHOUSE.Stockitem + local _parking=Parking[i] -- Spawn with ALIAS here or DCS crashes! local _alias=string.format("%s_%d", _assetitem.templatename,_assetitem.id) -- Spawn plane at airport in uncontrolled state. local _takeoff=SPAWN.Takeoff.Cold - local _terminal=AIRBASE.TerminalType.OpenBig - local spawngroup=SPAWN:NewWithAlias(_assetitem.templatename,_alias):InitUnControlled(true):SpawnAtAirbase(self.homebase,_takeoff, nil,_terminal, false) + local spawngroup=SPAWN:NewWithAlias(_assetitem.templatename,_alias):InitUnControlled(true):SpawnAtAirbase(self.homebase,_takeoff, nil, nil, false, _parking) if spawngroup then -- Set state of warehouse so we can retrieve it later. @@ -638,6 +719,16 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) CargoTransport = AI_CARGO_DISPATCHER_AIRPLANE:New(TransportSet, CargoGroups, PickupAirbaseSet, DeployAirbaseSet) elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then + + -- Now we try to find all parking spots for all transport groups in advance. Due to the for loop, the parking spots do not get updated while spawning. + -- Note that not all assest need to be of the same type. Therefore, do + local Parking={} + for i=1,Request.ntransport do + local _assetitem=_assetstock[i] --#WAREHOUSE.Stockitem + local group=GROUP:FindByName(_assetitem.templatename) + local spots=self.homebase:FindFreeParkingSpotForAircraft(group, AIRBASE.TerminalType.HelicopterUsable) + table.insert(Parking, spots) + end -- Spawn the transport groups. local _delid={} @@ -645,15 +736,14 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Get stock item. local _assetitem=_assetstock[i] --#WAREHOUSE.Stockitem - + local _parking=Parking[i] + -- Spawn with ALIAS here or DCS crashes! local _alias=string.format("%s_%d", _assetitem.templatename,_assetitem.id) - -- Spawn plane at airport in uncontrolled state. - -- TODO: check terminal type. + -- Spawn helo at airport. local _takeoff=SPAWN.Takeoff.Hot - local _terminal=AIRBASE.TerminalType.HelicopterUsable - local spawngroup=SPAWN:NewWithAlias(_assetitem.templatename,_alias):InitUnControlled(false):SpawnAtAirbase(self.homebase,_takeoff, nil,_terminal, false) + local spawngroup=SPAWN:NewWithAlias(_assetitem.templatename,_alias):InitUnControlled(false):SpawnAtAirbase(self.homebase,_takeoff, nil, nil, false, _parking) if spawngroup then -- Set state of warehouse so we can retrieve it later. diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 70169576e..1af9548c2 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -594,8 +594,9 @@ end -- @param #boolean scanscenery (Optional) Scan for scenery as obstacles. Default false. Can cause problems with e.g. shelters. -- @param #boolean verysafe (Optional) If true, wait until an aircraft has taken off until the parking spot is considered to be free. Defaul false. -- @param #number nspots (Optional) Number of freeparking spots requested. Default is the number of aircraft in the group. +-- @param #table parkingdata (Optional) Parking spots data table. If not given it is automatically derived from the GetParkingSpotsTable() function. -- @return #table Table of coordinates and terminal IDs of free parking spots. Each table entry has the elements .Coordinate and .TerminalID. -function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nspots) +function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, scanunits, scanstatics, scanscenery, verysafe, nspots, parkingdata) -- Init default scanradius=scanradius or 50 @@ -647,7 +648,7 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, -- 1. A spot is considered as NOT free until an aircraft that is present has finally taken off. This might be a bit long especiall at smaller airports. -- 2. A "free" spot does not take the aircraft size into accound. So if two big aircraft are spawned on spots next to each other, they might overlap and get destroyed. -- 3. The routine return a free spot, if there a static objects placed on the spot. - local parkingdata=self:GetParkingSpotsTable(terminaltype) + parkingdata=parkingdata or self:GetParkingSpotsTable(terminaltype) -- Get the aircraft size, i.e. it's longest side of x,z. local aircraft=group:GetUnit(1) From 06688366c696cfa9a6a09188b811a0220697de81 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 9 Aug 2018 23:40:56 +0200 Subject: [PATCH 246/420] Warehouse 0.1.2 --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 26 ++ .../Moose/AI/AI_Cargo_Dispatcher.lua | 14 +- .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 2 +- .../Moose/AI/AI_Cargo_Helicopter.lua | 115 ++++++- Moose Development/Moose/Core/Zone.lua | 4 +- .../Moose/Functional/Warehouse.lua | 315 +++++++++++------- 6 files changed, 334 insertions(+), 142 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 13f88324e..d231dd881 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -106,6 +106,7 @@ function AI_CARGO_APC:New( APC, CargoSet, CombatRadius ) self:AddTransition( "*", "Follow", "Following" ) self:AddTransition( "*", "Guard", "Unloaded" ) self:AddTransition( "*", "Home", "*" ) + self:AddTransition( "*", "BackHome" , "*" ) self:AddTransition( "*", "Destroyed", "Destroyed" ) @@ -725,9 +726,34 @@ function AI_CARGO_APC:onafterHome( APC, From, Event, To, Coordinate, Speed ) self:F({Waypoints = Waypoints}) local Waypoint = Waypoints[#Waypoints] + + -- Task function triggering the arrived event. + local TaskFunction = APC:TaskFunction("AI_CARGO_APC._BackHome", self) + + -- Put task function on last waypoint. + APC:SetTaskWaypoint( Waypoint, TaskFunction ) APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. end end + +--- Function called when transport is back home and nothing more to do. Triggering the event BackHome. +-- @param Wrapper.Group#GROUP APC Cargo carrier. +-- @param #AI_CARGO_APC self +function AI_CARGO_APC._BackHome(APC, self) + --Trigger BackHome event. + APC:SmokeGreen() + self:__BackHome(1) +end + +--- On after BackHome event. +-- @param #AI_CARGO_APC self +-- @param Wrapper.Group#GROUP APC +-- @param From +-- @param Event +-- @param To +function AI_CARGO_APC:onafterBackHome( APC, From, Event, To ) + APC:SmokeRed() +end \ No newline at end of file diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 7ac009283..cfb2da656 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -144,8 +144,8 @@ end --- Creates a new AI_CARGO_DISPATCHER object. -- @param #AI_CARGO_DISPATCHER self --- @param Core.Set#SET_GROUP SetCarrier --- @param Core.Set#SET_CARGO SetCargo +-- @param Core.Set#SET_GROUP SetCarriers +-- @param Core.Set#SET_CARGO SetCargos -- @param Core.Set#SET_ZONE DeployZonesSet -- @return #AI_CARGO_DISPATCHER -- @usage @@ -454,6 +454,11 @@ function AI_CARGO_DISPATCHER:onafterMonitor() self.CarrierHome[Carrier] = true AI_Cargo:__Home( 60, self.HomeZone:GetRandomPointVec2() ) end + elseif self.HomeBase then + if not self.CarrierHome[Carrier] then + self.CarrierHome[Carrier] = true + AI_Cargo:__RTB( 60, self.HomeBase ) + end end end end @@ -548,11 +553,12 @@ end -- @param Cargo.Cargo#CARGO Cargo -- @return #AI_CARGO_DISPATCHER function AI_CARGO_DISPATCHER:OnAfterLoaded( From, Event, To, Carrier, Cargo ) + if self.DeployZonesSet then - + local DeployZone = self.DeployZonesSet:GetRandomZone() - + local DeployCoordinate = DeployZone:GetCoordinate():GetRandomCoordinateInRadius( self.DeployOuterRadius, self.DeployInnerRadius ) self.AI_Cargo[Carrier]:Deploy( DeployCoordinate, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ) ) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index 413ca7c26..69947e082 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -103,7 +103,7 @@ AI_CARGO_DISPATCHER_APC = { -- function AI_CARGO_DISPATCHER_APC:NewWithZones( SetAPC, SetCargo, SetDeployZone, CombatRadius ) - local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone ) ) -- #AI_CARGO_DISPATCHER_APC + local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithZones( SetAPC, SetCargo, SetDeployZone ) ) -- #AI_CARGO_DISPATCHER_APC self.CombatRadius = CombatRadius or 500 diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 1e3141649..846289e03 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -53,7 +53,9 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) self:AddTransition( "*", "Landed", "*" ) self:AddTransition( "*", "Queue", "*" ) self:AddTransition( "*", "Orbit" , "*" ) - self:AddTransition( "*", "Home" , "*" ) + self:AddTransition( "*", "Home" , "*" ) + self:AddTransition( "*", "RTB" , "*" ) + self:AddTransition( "*", "BackHome" , "*" ) self:AddTransition( "*", "Destroyed", "Destroyed" ) @@ -714,7 +716,7 @@ end -- @param Event -- @param To -- @param Core.Point#COORDINATE Coordinate Home place. --- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. +-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 80% of max possible speed the unit can go. function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinate, Speed ) if Helicopter and Helicopter:IsAlive() ~= nil then @@ -725,9 +727,9 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat --- Calculate the target route point. - Coordinate.y = math.random( 50, 200 ) + Coordinate.y = math.random( 100, 500 ) - local _speed=Speed or Helicopter:GetSpeedMax()*0.5 + local _speed=Speed or Helicopter:GetSpeedMax()*0.8 --- Create a route point of type air. local CoordinateFrom = Helicopter:GetCoordinate() @@ -756,11 +758,14 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat Helicopter:WayPointInitialize( Route ) local Tasks = {} - + Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() ) + Tasks[#Tasks+1] = Helicopter:TaskFunction("AI_CARGO_HELICOPTER._BackHome", self) + Route[#Route].task = Helicopter:TaskCombo( Tasks ) Route[#Route+1] = WaypointTo + -- Now route the helicopter Helicopter:Route( Route, 0 ) @@ -769,3 +774,103 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat end + +--- On after RTB event. Route the helicopter from one airport or it's current position to another airbase. +-- @param #AI_CARGO_HELICOPTER self +-- @param Wrapper.Group#GROUP Helicopter Cargo helicopter. +-- @param From +-- @param Event +-- @param To +-- @param Wrapper.Airbase#AIRBASE Airbase Destination airbase. +-- @param #number Speed Speed in km/h. Default is 80% of max possible speed the group can do. +function AI_CARGO_HELICOPTER:onafterRTB( Helicopter, From, Event, To, Airbase, Speed) + + if Helicopter and Helicopter:IsAlive() then + + -- Set takeoff type. + local Takeoff = SPAWN.Takeoff.Hot + + -- Get template of group. + local Template = Helicopter:GetTemplate() + + -- Nil check + if Template==nil then + return + end + + -- Waypoints of the route. + local Points={} + + -- To point. + local AirbasePointVec2 = Airbase:GetPointVec2() + local ToWaypoint = AirbasePointVec2:WaypointAir( + POINT_VEC3.RoutePointAltType.BARO, + "Land", + "Landing", + Speed or Helicopter:GetSpeedMax()*0.8 + ) + ToWaypoint["airdromeId"] = Airbase:GetID() + ToWaypoint["speed_locked"] = true + + -- Task function triggering the arrived event. + local TaskFunction = Helicopter:TaskFunction("AI_CARGO_HELICOPTER._BackHome", self) + + -- Put task function on last waypoint. + Helicopter:SetTaskWaypoint( ToWaypoint, TaskFunction ) + + + -- If self.Airbase~=nil then group is currently at an airbase, where it should be respawned. + if self.Airbase then + + -- Second point of the route. First point is done in RespawnAtCurrentAirbase() routine. + Template.route.points[2] = ToWaypoint + + -- Respawn group at the current airbase. + Helicopter:RespawnAtCurrentAirbase(Template, Takeoff, false) + + else + + -- From point. + local GroupPoint = Helicopter:GetVec2() + local FromWaypoint = {} + FromWaypoint.x = GroupPoint.x + FromWaypoint.y = GroupPoint.y + FromWaypoint.type = "Turning Point" + FromWaypoint.action = "Turning Point" + FromWaypoint.speed = Helicopter:GetSpeedMax()*0.8 + + -- The two route points. + Points[1] = FromWaypoint + Points[2] = ToWaypoint + + local PointVec3 = Helicopter:GetPointVec3() + Template.x = PointVec3.x + Template.y = PointVec3.z + + Template.route.points = Points + + local GroupSpawned = Helicopter:Respawn(Template) + + end + end +end + +--- Function called when transport is back home and nothing more to do. Triggering the event BackHome. +-- @param Wrapper.Group#GROUP Helicopter Cargo helicopter. +-- @param #AI_CARGO_HELICOPTER self +function AI_CARGO_HELICOPTER._BackHome(Group, self) + --Trigger BackHome event. + Group:SmokeRed() + self:__BackHome(1) +end + + +--- On after BackHome event. +-- @param #AI_CARGO_HELICOPTER self +-- @param Wrapper.Group#GROUP Helicopter Cargo helo. +-- @param From +-- @param Event +-- @param To +function AI_CARGO_HELICOPTER:onafterBackHome( Helicopter, From, Event, To ) + Helicopter:SmokeRed() +end \ No newline at end of file diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index d0e368d3a..c417ad6a6 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -312,7 +312,6 @@ end -- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color. function ZONE_BASE:SmokeZone( SmokeColor ) self:F2( SmokeColor ) - end --- Set the randomization probability of a zone to be selected. @@ -330,8 +329,7 @@ end -- @param #ZONE_BASE self -- @return #number A value between 0 and 1. 0 = 0% and 1 = 100% probability. function ZONE_BASE:GetZoneProbability() - self:F2() - + self:F2() return self.ZoneProbability end diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 01d96b243..83c703062 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -152,7 +152,7 @@ WAREHOUSE.TransportType = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.1.1" +WAREHOUSE.version="0.1.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehuse todo list. @@ -379,6 +379,83 @@ function WAREHOUSE:AddRequest(airbase, AssetDescriptor, AssetDescriptorValue, nA table.insert(self.queue, request) end +---Checks if the request can be fullfilled. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE.Queueitem qitem The request to be checked. +-- @return #boolean If true, request can be executed. If false, something is not right. +function WAREHOUSE:_CheckRequest(request) + + local okay=true + + -- Check if number of requested assets is in stock. + local _assets=self:_FilterStock(self.stock, request.assetdesc, request.assetdescval, request.nasset) + + -- Get the attibute of the requested asset. + local _assetattribute=_assets[1].attribute + local _assetcategory=_assets[1].category + + -- Check if enough assets are in stock. + if request.nasset > #_assets then + local text=string.format("Request denied! Not enough assets currently available.") + MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:E(self.wid..text) + okay=false + end + + -- Check available parking for asset units. + local Parkingdata=self.homebase:GetParkingSpotsTable() + env.info("FF number parking data before "..#Parkingdata) + local Parking + if _assetcategory==Group.Category.AIRPLANE or _assetcategory==Group.Category.HELICOPTER then + Parking, Parkingdata=self:_GetParkingForAssets(_assets, Parkingdata) + env.info("FF number parking data after assets "..#Parkingdata) + if Parking==nil then + local text=string.format("Request denied! Not enough free parking spots for all assets at the moment.") + MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:E(self.wid..text) + okay=false + end + end + + + -- Check that a transport units. + if request.transporttype~=WAREHOUSE.TransportType.SELFPROPELLED then + + -- Transports in stock. + local _transports=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, request.transporttype, request.ntransport) + + -- Get the attibute of the transport units. + local _transportattribute=_transports[1].attribute + local _transportcategory=_transports[1].category + + -- Check if enough transport units are available. + if request.ntransport > #_transports then + local text=string.format("Request denied! Not enough transport units currently available.") + MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:E(self.wid..text) + okay=false + end + + -- Check available parking for transport units. + if _transportcategory==Group.Category.AIRPLANE or _transportcategory==Group.Category.HELICOPTER then + Parking, Parkingdata=self:_GetParkingForAssets(_transports, Parkingdata) + env.info("FF number parking data after transport "..#Parkingdata) + if Parking==nil then + local text=string.format("Request denied! Not enough free parking spots for all transports at the moment.") + MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:E(self.wid..text) + okay=false + end + end + + else + -- self propelled case. + + end + + return okay +end + ---Sorts the queue and checks if the request can be fullfilled. -- @param #WAREHOUSE self -- @return #WAREHOUSE.Queueitem Chosen request. @@ -387,28 +464,11 @@ function WAREHOUSE:_CheckQueue() -- Sort queue wrt to first prio and then qid. self:_SortQueue() - ---@param #WAREHOUSE.Queueitem qitem - --@return #boolean True if request is okay. - local function checkrequest(qitem) - local okay=true - -- Check if number of requested assets is in stock. - local _instock=#self:_FilterStock(self.stock, qitem.assetdesc, qitem.assetdescval) - if qitem.nasset > _instock then - okay=false - end - -- Check if enough transport units are in stock. - _instock=#self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, qitem.transporttype) - if qitem.ntransport > _instock then - okay=false - end - return okay - end - -- Search for a request we can execute. local request=nil --#WAREHOUSE.Queueitem for _,_qitem in ipairs(self.queue) do local qitem=_qitem --#WAREHOUSE.Queueitem - local okay=checkrequest(qitem) + local okay=self:_CheckRequest(qitem) if okay==true then request=qitem break @@ -428,61 +488,26 @@ end -- @param #WAREHOUSE.Queueitem Request Information table of the request. -- @return #boolean If true, request is granted. function WAREHOUSE:onbeforeRequest(From, Event, To, Request) - --env.info(self.wid..string.format("Airbase %s requesting asset %s = %s.", Airbase:GetName(), tostring(AssetDescriptor), tostring(AssetDescriptorValue))) + env.info(self.wid..string.format("Airbase %s requesting %d assets of %s=%s by transport %s", + Request.airbase:GetName(), Request.nasset, tostring(Request.assetdesc), tostring(Request.assetdescval), tostring(Request.transporttype))) -- Distance from warehouse to requesting airbase. local distance=self.coordinate:Get2DDistance(Request.airbase:GetCoordinate()) -- Filter the requested assets. - local _stockrequest=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval) + local _assets=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval, Request.nasset) -- Asset is not in stock ==> request denied. - if #_stockrequest < Request.nasset then - local text=string.format("Request denied! Not enough assets currently in stock. Requested %d < %d in stock.", Request.nasset, #_stockrequest) + if #_assets < Request.nasset then + local text=string.format("Request denied! Not enough assets currently in stock. Requested %d < %d in stock.", Request.nasset, #_assets) MESSAGE:New(text, 10):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:E(self.wid..text) return false end - -- Get the attibute of the requested asset. - local _stockitem=_stockrequest[1] --#WAREHOUSE.Stockitem - local _assetattribute=self:_GetAttribute(_stockitem.templatename) - - - -- Check that a transport unit is available. - if Request.transporttype~=WAREHOUSE.TransportType.SELFPROPELLED then - local _instock=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, Request.transporttype) - if #_instock==0 then - local text=string.format("Request denied! No transport unit currently available.") - MESSAGE:New(text, 10):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:E(self.wid..text) - return false - end - end - - -- TODO: For aircraft check that a parking spot is available. - return true end ---- Get the proper terminal type based on generalized attribute of the group. ---@param #WAREHOUSE self ---@param #WAREHOUSE.Attribute _attribute Generlized attibute of unit. -function WAREHOUSE:_GetTerminal(_attribute) - - local _terminal=AIRBASE.TerminalType.OpenBig - if _attribute==WAREHOUSE.Attribute.FIGHTER then - -- Fighter ==> small. - _terminal=AIRBASE.TerminalType.FighterAircraft - elseif _attribute==WAREHOUSE.Attribute.BOMBER or _attribute==WAREHOUSE.Attribute.TRANSPORT_PLANE or _attribute==WAREHOUSE.Attribute.TANKER or _attribute==WAREHOUSE.Attribute.AWACS then - -- Bigger aircraft. - _terminal=AIRBASE.TerminalType.OpenBig - elseif _attribute==WAREHOUSE.Attribute.TRANSPORT_HELO or _attribute==WAREHOUSE.Attribute.ATTACKHELICOPTER then - -- Helicopter. - _terminal=AIRBASE.TerminalType.HelicopterUsable - end - -end --- On after "Request" event. Initiates the transport of the assets to the requesting airbase. -- @param #WAREHOUSE self @@ -504,57 +529,22 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) local _loadradius=5000 local _nearradius=35 - -- Filter the requested assets. - local _assetstock=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval) + -- Filter the requested cargo assets. + local _assetstock=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval, Request.nasset) - -- General type and category (GROUND,...) + -- General type and category. local _cargotype=_assetstock[1].attribute --#WAREHOUSE.Attribute local _cargocategory=_assetstock[1].category --DCS#Group.Category - --_cargotype. + -- Now we try to find all parking spots for all cargo groups in advance. Due to the for loop, the parking spots do not get updated while spawning. + local Parking={} + if _cargocategory==Group.Category.AIRPLANE or _cargocategory==Group.Category.HELICOPTER then + Parking=self:_GetParkingForAssets(_assetstock) + end -- Spawn the assets. local _delid={} local _spawngroups={} - local _cargotype - local _cargocategory - - - -- Now we try to find all parking spots for all cargo groups in advance. Due to the for loop, the parking spots do not get updated while spawning. - local Parking={} - - if _cargocategory==Group.Category.AIRPLANE or _cargocategory==Group.Category.HELICOPTER then - - -- Count total number of units that will be spawned. - local ntotal=0 - for i=1,Request.ntransport do - local _assetitem=_assetstock[i] --#WAREHOUSE.Stockitem - local group=GROUP:FindByName(_assetitem.templatename) - ntotal=ntotal + #group:GetUnits() - end - - -- All spots are based on the same template group which must not be the case. - -- But I cant think of a better way to fetch all parking spots in advance. - local group=GROUP:FindByName(_assetstock[1].templatename) - local terminaltype=self:_GetTerminal(_assetstock[1].attribute) - local allspots=self.homebase:FindFreeParkingSpotForAircraft(group, terminaltype, 50, true, true, false, false, ntotal) - - env.info("FF allspots = "..#allspots) - env.info("FF notal = "..ntotal) - - -- No rearrange them again for each asset - local k=1 - for i=1,Request.ntransport do - local _assetitem=_assetstock[i] --#WAREHOUSE.Stockitem - local spots={} - local nunits=#GROUP:FindByName(_assetitem.templatename):GetUnits() - for j=1,nunits do - table.insert(spots,allspots[k]) - k=k+1 - end - table.insert(Parking, spots) - end - end -- Loop over cargo requests. for i=1,Request.nasset do @@ -670,20 +660,24 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) local CargoTransport --AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER -- Filter the requested transport assets. - local _assetstock=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, Request.transporttype) + local _assetstock=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, Request.transporttype, Request.ntransport) + + -- General type and category. + local _transporttype=_assetstock[1].attribute --#WAREHOUSE.Attribute + local _transportcategory=_assetstock[1].category --DCS#Group.Category + + -- Now we try to find all parking spots for all transport groups in advance. Due to the for loop, the parking spots do not get updated while spawning. + local Parking={} + + -- Now we try to find all parking spots for all cargo groups in advance. Due to the for loop, the parking spots do not get updated while spawning. + local Parking={} + if _transportcategory==Group.Category.AIRPLANE or _transportcategory==Group.Category.HELICOPTER then + Parking=self:_GetParkingForAssets(_assetstock) + end -- Dependent on transport type, spawn the transports and set up the dispatchers. if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then - -- Now we try to find all parking spots for all transport groups in advance. Due to the for loop, the parking spots do not get updated while spawning. - local Parking={} - for i=1,Request.ntransport do - local _assetitem=_assetstock[i] --#WAREHOUSE.Stockitem - local group=GROUP:FindByName(_assetitem.templatename) - local spots=self.homebase:FindFreeParkingSpotForAircraft(group, AIRBASE.TerminalType.OpenBig) - table.insert(Parking, spots) - end - -- Spawn the transport groups. local _delid={} for i=1,Request.ntransport do @@ -719,16 +713,6 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) CargoTransport = AI_CARGO_DISPATCHER_AIRPLANE:New(TransportSet, CargoGroups, PickupAirbaseSet, DeployAirbaseSet) elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then - - -- Now we try to find all parking spots for all transport groups in advance. Due to the for loop, the parking spots do not get updated while spawning. - -- Note that not all assest need to be of the same type. Therefore, do - local Parking={} - for i=1,Request.ntransport do - local _assetitem=_assetstock[i] --#WAREHOUSE.Stockitem - local group=GROUP:FindByName(_assetitem.templatename) - local spots=self.homebase:FindFreeParkingSpotForAircraft(group, AIRBASE.TerminalType.HelicopterUsable) - table.insert(Parking, spots) - end -- Spawn the transport groups. local _delid={} @@ -754,7 +738,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) table.insert(_delid,_assetitem.id) else - env.info("FF error spawngroup helo transport does not exist!") + self:E(self.wid.."ERROR: spawngroup helo transport does not exist!") end end @@ -767,7 +751,8 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) CargoTransport = AI_CARGO_DISPATCHER_HELICOPTER:New(TransportSet, CargoGroups, DeployZoneSet) -- Home zone. - CargoTransport:SetHomeZone(self.spawnzone) + CargoTransport:SetHomeBase(self.homebase) + --CargoTransport:SetHomeZone(self.spawnzone) elseif Request.transporttype==WAREHOUSE.TransportType.APC then @@ -803,6 +788,9 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Define dispatcher for this task. CargoTransport = AI_CARGO_DISPATCHER_APC:NewWithZones(TransportSet, CargoGroups, DeployZoneSet, 0) + -- Set home zone. + CargoTransport:SetHomeZone(self.spawnzone) + elseif Request.transporttype==WAREHOUSE.TransportType.TRAIN then self:E(self.wid.."ERROR: transport by train not supported yet!") @@ -1048,13 +1036,83 @@ function WAREHOUSE:_RouteTrain(Group, Coordinate, Speed) end end +--- Get the proper terminal type based on generalized attribute of the group. +--@param #WAREHOUSE self +--@param #WAREHOUSE.Attribute _attribute Generlized attibute of unit. +function WAREHOUSE:_GetTerminal(_attribute) + + local _terminal=AIRBASE.TerminalType.OpenBig + if _attribute==WAREHOUSE.Attribute.FIGHTER then + -- Fighter ==> small. + _terminal=AIRBASE.TerminalType.FighterAircraft + elseif _attribute==WAREHOUSE.Attribute.BOMBER or _attribute==WAREHOUSE.Attribute.TRANSPORT_PLANE or _attribute==WAREHOUSE.Attribute.TANKER or _attribute==WAREHOUSE.Attribute.AWACS then + -- Bigger aircraft. + _terminal=AIRBASE.TerminalType.OpenBig + elseif _attribute==WAREHOUSE.Attribute.TRANSPORT_HELO or _attribute==WAREHOUSE.Attribute.ATTACKHELICOPTER then + -- Helicopter. + _terminal=AIRBASE.TerminalType.HelicopterUsable + end + +end + +--- Get parking data for all air assets that need to be spawned at an airbase. +--@param #WAREHOUSE self +--@param #table assetlist A list of assets for which parking spots are required. +--@param #table parkingdata Table of the complete parking data to check. Default is to take it from the @{Wrapper.Airbase#AIRBASE.GetParkingSpotsTable}() function. +--@return #table A table with parking spots for each asset group. +--@return #table The reduced parking data table of the spots that have not been assigned. +function WAREHOUSE:_GetParkingForAssets(assetlist, parkingdata) + + --- Remove selected spots from parking data table. + local function removeparking(parkingdata,spots) + for j=1,#spots do + for i=1,#parkingdata do + if parkingdata[i].TerminalID==spots[j].TerminalID then + table.remove(parkingdata,j) + break + end + end + end + end + + -- Get complete parking data of the airbase. + parkingdata=parkingdata or self.homebase:GetParkingSpotsTable() + + local assetparking={} + for i=1,#assetlist do + + -- Asset specifics. + local asset=assetlist[i] --#WAREHOUSE.Stockitem + local group=GROUP:FindByName(asset.templatename) + local nunits=#group:GetUnits() + local terminal=self:_GetTerminal(asset.attribute) + + -- Find appropiate parking spots for this group. + local spots=self.homebase:FindFreeParkingSpotForAircraft(group, terminal, nil, nil, nil, nil, nil, nil, parkingdata) + + -- Not enough parking spots for this group. + if #spots=nmax then + return filtered + end end end @@ -1221,8 +1282,6 @@ end -- @param #WAREHOUSE self -- @param #number _uid The id of the item to be deleted. function WAREHOUSE:_DeleteQueueItem(_uid) - env.info("FF BEFORE delete queue") - self:_PrintQueue() for i=1,#self.queue do local item=self.queue[i] --#WAREHOUSE.Queueitem if item.uid==_uid then @@ -1230,8 +1289,6 @@ function WAREHOUSE:_DeleteQueueItem(_uid) break end end - env.info("FF AFTER delete queue") - self:_PrintQueue() end --- Sort requests queue wrt prio and request uid. From 195459d6d83dc2fe220bf4f0ee0de238746c3895 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Fri, 10 Aug 2018 16:26:39 +0200 Subject: [PATCH 247/420] Warehouse v0.1.3 --- .../Moose/AI/AI_Cargo_Dispatcher.lua | 2 + .../Moose/Functional/Warehouse.lua | 505 ++++++++++++------ 2 files changed, 331 insertions(+), 176 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index cfb2da656..bb39d63ce 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -123,6 +123,8 @@ function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo ) self:AddTransition( "*", "Unloaded", "*" ) self:AddTransition( "*", "Home", "*" ) + self:AddTransition( "*", "RTB", "*" ) --FF + self:AddTransition( "*", "BackHome", "*" ) --FF self.MonitorTimeInterval = 30 self.DeployRadiusInner = 200 diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 83c703062..49bd8d8aa 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -22,16 +22,19 @@ -- @field #string ClassName Name of the class. -- @field #boolean Debug If true, send debug messages to all. -- @field #boolean Report If true, send status messages to coalition. --- @field DCS#Coalition coalition Coalition the warehouse belongs to. +-- @field DCS#coalition.side coalition Coalition ID the warehouse belongs to. +-- @field DCS#country.id country Country ID the warehouse belongs to. -- @field Wrapper.Airbase#AIRBASE homebase Airbase the warehouse belongs to. -- @field DCS#Airbase.Category category Category of the home airbase, i.e. airdrome, helipad/farp or ship. -- @field Core.Point#COORDINATE coordinate Coordinate of the warehouse. -- @field Core.Zone#ZONE spawnzone Zone in which assets are spawned. -- @field #string wid Identifier of the warehouse printed before other output to DCS.log file. +-- @field #number uid Unit identifier of the warehouse. Derived from the associated airbase. -- @field #number markerid ID of the warehouse marker at the airbase. -- @field #number assetid Unique id of asset items in stock. Essentially a running number starting at one and incremented when a new asset is added. -- @field #table stock Table holding all assets in stock. Table entries are of type @{#WAREHOUSE.Stockitem}. -- @field #table queue Table holding all queued requests. Table entries are of type @{#WAREHOUSE.Queueitem}. +-- @field #table pending Table holding all pending requests, i.e. those that are currently in progress. Table entries are of type @{#WAREHOUSE.Queueitem}. -- @extends Core.Fsm#FSM --- Manages ground assets of an airbase and offers the possibility to transport them to another airbase or warehouse. @@ -74,12 +77,15 @@ WAREHOUSE = { Debug = false, Report = true, coalition = nil, + country = nil, homebase = nil, category = nil, coordinate = nil, spawnzone = nil, wid = nil, + uid = nil, markerid = nil, + warehouse = nil, assetid = 0, queueid = 0, stock = {}, @@ -90,6 +96,7 @@ WAREHOUSE = { -- @type WAREHOUSE.Stockitem -- @field #number id Unique id of the asset. -- @field #string templatename Name of the template group. +-- @field #table template The spawn template of the group. -- @field DCS#Group.Category category Category of the group. -- @field #string unittype Type of the first unit of the group as obtained by the Object.getTypeName() DCS API function. -- @field #WAREHOUSE.Attribute attribute Generalized attribute of the group. @@ -147,12 +154,12 @@ WAREHOUSE.TransportType = { APC = "Transport_APC", SHIP = "Ship", TRAIN = "Train", - SELFPROPELLED = "Selfporpelled", + SELFPROPELLED = "Selfpropelled", } --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.1.2" +WAREHOUSE.version="0.1.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehuse todo list. @@ -195,8 +202,10 @@ function WAREHOUSE:NewAirbase(airbase) self.homebase=airbase self.coordinate=airbase:GetCoordinate() self.coalition=airbase:GetCoalition() + self.country=airbase:GetCountry() self.category=airbase:GetDesc().category - + self.uid=airbase:GetID() + -- Get the closest point on road. local _road=self.coordinate:GetClosestPointToRoad():GetVec2() @@ -255,29 +264,21 @@ function WAREHOUSE:NewAirbase(airbase) return self end ---- Set a zone where the (ground) assets of the warehouse are spawned once requested. --- @param #WAREHOUSE self --- @param Core.Zone#ZONE zone The spawn zone. --- @return #WAREHOUSE self -function WAREHOUSE:SetSpawnZone(zone) - self.spawnzone=zone - return self -end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- FSM states ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Warehouse +--- On after Start event. Starts the warehouse. Addes event handlers and schedules status updates of reqests and queue. -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. function WAREHOUSE:onafterStart(From, Event, To) - self:E(self.wid..string.format("Starting warehouse at airbase %s, category %d, coalition %d.", self.homebase:GetName(), self.category, self.coalition)) + self:E(self.wid..string.format("Starting warehouse at airbase %s, category %d, coalition %d, country %d", self.homebase:GetName(), self.category, self.coalition, self.country)) -- Debug mark spawn zone. - self.spawnzone:BoundZone(60,country.id.GERMANY) + --self.spawnzone:BoundZone(60, country.id.GERMANY) + self.spawnzone:BoundZone(60, self.country) self.spawnzone:GetCoordinate():MarkToAll("Spawnzone of warehouse "..self.homebase:GetName()) -- handle events @@ -291,7 +292,7 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Warehouse +--- On after Status event. Checks the queue and handles requests. -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. @@ -339,147 +340,6 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- On before "Request" event. Checks if the request can be fullfilled. --- @param #WAREHOUSE self --- @param Wrapper.Airbase#AIRBASE Airbase airbase requesting supply. --- @param #WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. --- @param AssetDescriptorValue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. --- @param #number nAsset Number of groups requested that match the asset specification. --- @param #WAREHOUSE.TransportType TransportType Type of transport. --- @param #number nTransport Number of transport units requested. --- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. -function WAREHOUSE:AddRequest(airbase, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType, nTransport, Prio) - - nAsset=nAsset or 1 - TransportType=TransportType or WAREHOUSE.TransportType.SELFPROPELLED - nTransport=nTransport or 1 - Prio=Prio or 50 - - --TODO: check that - -- if warehouse or requestor is a FARP, plane asset and transport not possible - -- if requestor or warehouse is a SHIP, APC transport not possible, SELFPROPELLED only for AIR/SHIP - -- etc. etc... - - local request_category=airbase:GetDesc().category - - if self.category==Airbase.Category.HELIPAD or request_category==Airbase.Category.HELIPAD then - if TransportType==WAREHOUSE.TransportType.AIRPLANE then - self:E("ERROR: incorrect request. Warehouse or requestor is FARP. No transport by plane possible!") - return - end - end - - -- Increase id. - self.queueid=self.queueid+1 - - -- Request queue table item. - local request={uid=self.queueid, prio=Prio, airbase=airbase, category=request_category, assetdesc=AssetDescriptor, assetdescval=AssetDescriptorValue, nasset=nAsset, transporttype=TransportType, ntransport=nTransport} - - -- Add request to queue. - table.insert(self.queue, request) -end - ----Checks if the request can be fullfilled. --- @param #WAREHOUSE self --- @param #WAREHOUSE.Queueitem qitem The request to be checked. --- @return #boolean If true, request can be executed. If false, something is not right. -function WAREHOUSE:_CheckRequest(request) - - local okay=true - - -- Check if number of requested assets is in stock. - local _assets=self:_FilterStock(self.stock, request.assetdesc, request.assetdescval, request.nasset) - - -- Get the attibute of the requested asset. - local _assetattribute=_assets[1].attribute - local _assetcategory=_assets[1].category - - -- Check if enough assets are in stock. - if request.nasset > #_assets then - local text=string.format("Request denied! Not enough assets currently available.") - MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:E(self.wid..text) - okay=false - end - - -- Check available parking for asset units. - local Parkingdata=self.homebase:GetParkingSpotsTable() - env.info("FF number parking data before "..#Parkingdata) - local Parking - if _assetcategory==Group.Category.AIRPLANE or _assetcategory==Group.Category.HELICOPTER then - Parking, Parkingdata=self:_GetParkingForAssets(_assets, Parkingdata) - env.info("FF number parking data after assets "..#Parkingdata) - if Parking==nil then - local text=string.format("Request denied! Not enough free parking spots for all assets at the moment.") - MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:E(self.wid..text) - okay=false - end - end - - - -- Check that a transport units. - if request.transporttype~=WAREHOUSE.TransportType.SELFPROPELLED then - - -- Transports in stock. - local _transports=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, request.transporttype, request.ntransport) - - -- Get the attibute of the transport units. - local _transportattribute=_transports[1].attribute - local _transportcategory=_transports[1].category - - -- Check if enough transport units are available. - if request.ntransport > #_transports then - local text=string.format("Request denied! Not enough transport units currently available.") - MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:E(self.wid..text) - okay=false - end - - -- Check available parking for transport units. - if _transportcategory==Group.Category.AIRPLANE or _transportcategory==Group.Category.HELICOPTER then - Parking, Parkingdata=self:_GetParkingForAssets(_transports, Parkingdata) - env.info("FF number parking data after transport "..#Parkingdata) - if Parking==nil then - local text=string.format("Request denied! Not enough free parking spots for all transports at the moment.") - MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:E(self.wid..text) - okay=false - end - end - - else - -- self propelled case. - - end - - return okay -end - ----Sorts the queue and checks if the request can be fullfilled. --- @param #WAREHOUSE self --- @return #WAREHOUSE.Queueitem Chosen request. -function WAREHOUSE:_CheckQueue() - - -- Sort queue wrt to first prio and then qid. - self:_SortQueue() - - -- Search for a request we can execute. - local request=nil --#WAREHOUSE.Queueitem - for _,_qitem in ipairs(self.queue) do - local qitem=_qitem --#WAREHOUSE.Queueitem - local okay=self:_CheckRequest(qitem) - if okay==true then - request=qitem - break - end - end - - -- Execute request. - return request -end - - --- On before "Request" event. Checks if the request can be fullfilled. -- @param #WAREHOUSE self -- @param #string From From state. @@ -556,8 +416,10 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) local spawncoord=self.spawnzone:GetRandomCoordinate() -- Alias of the group. Spawn with ALIAS here or DCS crashes! - local _alias=string.format("%s_AssetID-%04d_RequestID-%04d", _assetitem.templatename,_assetitem.id,Request.uid) - local _spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias) + --local _alias=string.format("%s_WID-%02d_AID-%04d_RID-%04d", _assetitem.unittype, self.uid,_assetitem.id,Request.uid) + local _alias=self:_Alias(_assetitem,Request) + --local _spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias) + local _spawn=SPAWN:NewFromTemplate(_assetitem.template,_assetitem.templatename,_alias) local _group=nil --Wrapper.Group#GROUP @@ -636,6 +498,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) end -- Delete request from queue. + table.insert(self.pending, Request) self:_DeleteQueueItem(Request.uid) -- No cargo transport necessary. @@ -687,11 +550,14 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) local _parking=Parking[i] -- Spawn with ALIAS here or DCS crashes! - local _alias=string.format("%s_%d", _assetitem.templatename,_assetitem.id) + --local _alias=string.format("%s_%d", _assetitem.templatename,_assetitem.id) + local _alias=self:_Alias(_assetitem, Request) -- Spawn plane at airport in uncontrolled state. local _takeoff=SPAWN.Takeoff.Cold - local spawngroup=SPAWN:NewWithAlias(_assetitem.templatename,_alias):InitUnControlled(true):SpawnAtAirbase(self.homebase,_takeoff, nil, nil, false, _parking) + --local spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias) + local spawn=SPAWN:NewFromTemplate(_assetitem.template,_assetitem.templatename,_alias) + local spawngroup=spawn:InitUnControlled(true):SpawnAtAirbase(self.homebase,_takeoff, nil, nil, false, _parking) if spawngroup then -- Set state of warehouse so we can retrieve it later. @@ -723,11 +589,14 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) local _parking=Parking[i] -- Spawn with ALIAS here or DCS crashes! - local _alias=string.format("%s_%d", _assetitem.templatename,_assetitem.id) + --local _alias=string.format("%s_%d", _assetitem.templatename,_assetitem.id) + local _alias=self:_Alias(_assetitem, Request) -- Spawn helo at airport. local _takeoff=SPAWN.Takeoff.Hot - local spawngroup=SPAWN:NewWithAlias(_assetitem.templatename,_alias):InitUnControlled(false):SpawnAtAirbase(self.homebase,_takeoff, nil, nil, false, _parking) + --local spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias) + local spawn=SPAWN:NewFromTemplate(_assetitem.template,_assetitem.templatename,_alias) + local spawngroup=spawn:InitUnControlled(false):SpawnAtAirbase(self.homebase,_takeoff, nil, nil, false, _parking) if spawngroup then -- Set state of warehouse so we can retrieve it later. @@ -764,10 +633,13 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) local _assetitem=_assetstock[i] --#WAREHOUSE.Stockitem -- Spawn with ALIAS here or DCS crashes! - local _alias=string.format("%s_%d", _assetitem.templatename,_assetitem.id) + --local _alias=string.format("%s_%d", _assetitem.templatename,_assetitem.id) + local _alias=self:_Alias(_assetitem, Request) -- Spawn plane at airport in uncontrolled state. - local spawngroup=SPAWN:NewWithAlias(_assetitem.templatename,_alias):SpawnFromCoordinate(self.spawnzone:GetRandomCoordinate()) + local spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias) + local spawn=SPAWN:NewFromTemplate(_assetitem.template,_assetitem.templatename,_alias) + local spawngroup=spawn:SpawnFromCoordinate(self.spawnzone:GetRandomCoordinate()) if spawngroup then -- Set state of warehouse so we can retrieve it later. @@ -831,28 +703,46 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Trigger Delivered event. warehouse:__Delivered(1, group) end + + --- On after BackHome event. + function CargoTransport:OnAfterBackHome(From, Event, To, Carrier) + + -- Get warehouse state. + local warehouse=Carrier:GetState(Carrier, "WAREHOUSE") --#WAREHOUSE + Carrier:SmokeRed() + + -- Add carrier back to warehouse stock. Actual unit is destroyed. + warehouse:AddAsset(Carrier:GetName(), 1) + + end -- Start dispatcher. CargoTransport:__Start(5) -- Delete request from queue. + table.insert(self.pending, Request) self:_DeleteQueueItem(Request.uid) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Warehouse +--- On after "Delivered" event. -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param Wrapper.Group#GROUP Group The group that was delivered. -function WAREHOUSE:onafterDelivered(From, Event, To, Group) - env.info("FF warehouse cargo delivered! Croutine to closest point on road") - local road=Group:GetCoordinate():GetClosestPointToRoad() - local speed=Group:GetSpeedMax()*0.6 - Group:RouteGroundTo(road, speed, "Off Road") +-- @param Wrapper.Group#GROUP group The group that was delivered. +function WAREHOUSE:onafterDelivered(From, Event, To, group) + self:E(self.wid..string.format("Cargo %s delivered!", group:GetName())) + + -- Route a ground group to the closest point on road. + if group:IsGround() and group:GetSpeedMax()>0 then + local road=group:GetCoordinate():GetClosestPointToRoad() + local speed=group:GetSpeedMax()*0.6 + group:RouteGroundTo(road, speed, "Off Road") + end + end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -894,11 +784,22 @@ function WAREHOUSE:AddAsset(templategroupname, ngroups) self.assetid=self.assetid+1 stockitem.id=self.assetid stockitem.templatename=templategroupname + stockitem.template=UTILS.DeepCopy(_DATABASE.Templates.Groups[templategroupname].Template) stockitem.category=DCScategory stockitem.unittype=DCStype stockitem.attribute=attribute + + -- Modify the template so that the group is spawned with the right coalition. + stockitem.template.CoalitionID=self.coalition + stockitem.template.CountryID=self.country + table.insert(self.stock, stockitem) end + + -- Destroy group if it is alive. + if group:IsAlive()==true then + group:Destroy() + end else -- Group name does not exist! @@ -908,10 +809,262 @@ function WAREHOUSE:AddAsset(templategroupname, ngroups) return self end +--- Set a zone where the (ground) assets of the warehouse are spawned once requested. +-- @param #WAREHOUSE self +-- @param Core.Zone#ZONE zone The spawn zone. +-- @return #WAREHOUSE self +function WAREHOUSE:SetSpawnZone(zone) + self.spawnzone=zone + return self +end + +--- Add a request for the warehouse. +-- @param #WAREHOUSE self +-- @param Wrapper.Airbase#AIRBASE Airbase airbase requesting supply. +-- @param #WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. +-- @param AssetDescriptorValue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. +-- @param #number nAsset Number of groups requested that match the asset specification. +-- @param #WAREHOUSE.TransportType TransportType Type of transport. +-- @param #number nTransport Number of transport units requested. +-- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. +function WAREHOUSE:AddRequest(airbase, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType, nTransport, Prio) + + nAsset=nAsset or 1 + TransportType=TransportType or WAREHOUSE.TransportType.SELFPROPELLED + nTransport=nTransport or 1 + Prio=Prio or 50 + + --TODO: check that + -- if warehouse or requestor is a FARP, plane asset and transport not possible. + -- if requestor or warehouse is a SHIP, APC transport not possible, SELFPROPELLED only for AIR/SHIP + -- etc. etc... + + local request_category=airbase:GetDesc().category --DCS#Airbase.Category + + -- No airplanes from to to FARPS. + if self.category==Airbase.Category.HELIPAD or request_category==Airbase.Category.HELIPAD then + if TransportType==WAREHOUSE.TransportType.AIRPLANE then + self:E("ERROR: incorrect request. Warehouse or requestor is FARP. No transport by plane possible!") + return + end + end + + local request_air=false + local request_plane=false + local request_helo=false + local request_ground=false + local request_naval=false + + -- Check if category is matching + if AssetDescriptor==WAREHOUSE.Descriptor.CATEGORY then + if AssetDescriptorValue==Group.Category.AIRPLANE then + request_plane=true + elseif AssetDescriptorValue==Group.Category.HELICOPTER then + request_helo=true + elseif AssetDescriptorValue==Group.Category.GROUND then + request_ground=true + elseif AssetDescriptorValue==Group.Category.SHIP then + request_naval=true + elseif AssetDescriptorValue==Group.Category.TRAIN then + request_ground=true + else + self:E("ERROR: incorrect request. Asset Descriptor missmatch! Has to be Group.Cagetory.AIRPLANE, ...") + return + end + end + + -- Check attribute is matching + if AssetDescriptor==WAREHOUSE.Descriptor.ATTRIBUTE then + if AssetDescriptorValue==WAREHOUSE.Attribute.ARTILLERY then + request_ground=true + elseif AssetDescriptorValue==WAREHOUSE.Attribute.ATTACKHELICOPTER then + request_helo=true + elseif AssetDescriptorValue==WAREHOUSE.Attribute.AWACS then + request_plane=true + elseif AssetDescriptorValue==WAREHOUSE.Attribute.BOMBER then + request_plane=true + elseif AssetDescriptorValue==WAREHOUSE.Attribute.FIGHTER then + request_plane=true + elseif AssetDescriptorValue==WAREHOUSE.Attribute.INFANTRY then + request_ground=true + elseif AssetDescriptorValue==WAREHOUSE.Attribute.OTHER then + self:E("ERROR: incorrect request. Asset attribute WAREHOUSE.Attribute.OTHER is not valid!") + return + elseif AssetDescriptorValue==WAREHOUSE.Attribute.SHIP then + request_naval=true + elseif AssetDescriptorValue==WAREHOUSE.Attribute.TANK then + request_ground=true + elseif AssetDescriptorValue==WAREHOUSE.Attribute.TANKER then + request_plane=true + elseif AssetDescriptorValue==WAREHOUSE.Attribute.TRAIN then + request_ground=true + elseif AssetDescriptorValue==WAREHOUSE.Attribute.TRANSPORT_APC then + request_ground=true + elseif AssetDescriptorValue==WAREHOUSE.Attribute.TRANSPORT_HELO then + request_helo=true + elseif AssetDescriptorValue==WAREHOUSE.Attribute.TRANSPORT_PLANE then + request_plane=true + elseif AssetDescriptorValue==WAREHOUSE.Attribute.TRUCK then + request_ground=true + else + self:E("ERROR: incorrect request. Unknown asset attribute!") + return + end + end + + request_air=request_helo or request_plane + + if request_air then + + if request_plane then + -- No airplane to or from FARPS. + if request_category==Airbase.Category.HELIPAD or self.category==Airbase.Category.HELIPAD then + self:E("ERROR: incorrect request. Asset aircraft requestst but warehouse or requestor is HELIPAD/FARP!") + return + end + elseif request_helo then + + end + + elseif request_ground then + + -- No ground assets directly to ships. + if (request_category==Airbase.Category.SHIP or self.category==Airbase.Category.SHIP) + and not (TransportType==WAREHOUSE.TransportType.AIRPLANE or TransportType==WAREHOUSE.TransportType.HELICOPTER) then + self:E("ERROR: incorrect request. Ground asset requested but warehouse or requestor is SHIP!") + return + end + + elseif request_naval then + + end + + -- Increase id. + self.queueid=self.queueid+1 + + -- Request queue table item. + local request={uid=self.queueid, prio=Prio, airbase=airbase, category=request_category, assetdesc=AssetDescriptor, assetdescval=AssetDescriptorValue, nasset=nAsset, transporttype=TransportType, ntransport=nTransport} + + -- Add request to queue. + table.insert(self.queue, request) +end + + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Helper functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Checks if the request can be fullfilled. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE.Queueitem qitem The request to be checked. +-- @return #boolean If true, request can be executed. If false, something is not right. +function WAREHOUSE:_CheckRequest(request) + + local okay=true + + -- Check if number of requested assets is in stock. + local _assets=self:_FilterStock(self.stock, request.assetdesc, request.assetdescval, request.nasset) + + -- Get the attibute of the requested asset. + local _assetattribute=_assets[1].attribute + local _assetcategory=_assets[1].category + + -- Check if enough assets are in stock. + if request.nasset > #_assets then + local text=string.format("Request denied! Not enough assets currently available.") + MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:E(self.wid..text) + okay=false + end + + -- Check available parking for asset units. + local Parkingdata=self.homebase:GetParkingSpotsTable() + local Parking + if _assetcategory==Group.Category.AIRPLANE or _assetcategory==Group.Category.HELICOPTER then + Parking, Parkingdata=self:_GetParkingForAssets(_assets, Parkingdata) + if Parking==nil then + local text=string.format("Request denied! Not enough free parking spots for all assets at the moment.") + MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:E(self.wid..text) + okay=false + end + end + + + -- Check that a transport units. + if request.transporttype~=WAREHOUSE.TransportType.SELFPROPELLED then + + -- Transports in stock. + local _transports=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, request.transporttype, request.ntransport) + + -- Get the attibute of the transport units. + local _transportattribute=_transports[1].attribute + local _transportcategory=_transports[1].category + + -- Check if enough transport units are available. + if request.ntransport > #_transports then + local text=string.format("Request denied! Not enough transport units currently available.") + MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:E(self.wid..text) + okay=false + end + + -- Check available parking for transport units. + if _transportcategory==Group.Category.AIRPLANE or _transportcategory==Group.Category.HELICOPTER then + Parking, Parkingdata=self:_GetParkingForAssets(_transports, Parkingdata) + if Parking==nil then + local text=string.format("Request denied! Not enough free parking spots for all transports at the moment.") + MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:E(self.wid..text) + okay=false + end + end + + else + -- self propelled case. + + end + + return okay +end + + +--- Creates a unique name for spawned assets. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE.Stockitem _assetitem Asset for which the name is created. +-- @param #WAREHOUSE.Queueitem _queueitem (Optional) Request specific name. +-- @return #string Alias name "UnitType\_WID-%02d\_AID-%04d" +function WAREHOUSE:_Alias(_assetitem,_queueitem) + local _alias=string.format("%s_WID-%02d_AID-%04d", _assetitem.unittype, self.uid,_assetitem.id) + if _queueitem then + _alias=_alias..string.format("_RID-%04d",_queueitem.uid) + end + return _alias +end + +---Sorts the queue and checks if the request can be fullfilled. +-- @param #WAREHOUSE self +-- @return #WAREHOUSE.Queueitem Chosen request. +function WAREHOUSE:_CheckQueue() + + -- Sort queue wrt to first prio and then qid. + self:_SortQueue() + + -- Search for a request we can execute. + local request=nil --#WAREHOUSE.Queueitem + for _,_qitem in ipairs(self.queue) do + local qitem=_qitem --#WAREHOUSE.Queueitem + local okay=self:_CheckRequest(qitem) + if okay==true then + request=qitem + break + end + end + + -- Execute request. + return request +end + --- Route ground units to destination. -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP Group The ground group. @@ -997,9 +1150,9 @@ function WAREHOUSE:_RouteAir(Aircraft, ToAirbase, Speed) -- Task function triggering the arrived event. local Task = Aircraft:TaskFunction("WAREHOUSE._Arrived", self) - -- or - --ToWaypoint.task=Aircraft:TaskCombo({Task}) - ToWaypoint.task={Task} + --or + ToWaypoint.task=Aircraft:TaskCombo({Task}) + --ToWaypoint.task={Task} -- Second point of the route. First point is done in RespawnAtCurrentAirbase() routine. Template.route.points[2] = ToWaypoint From 7f9f9b33fd4b1f64040c911e810ade946a28493d Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sat, 11 Aug 2018 11:33:43 +0200 Subject: [PATCH 248/420] - Have added cargo bay limits based on volume and weight. - AI Cargo Dispatcher works with cargo bay limits for carrier. (i still need to add options to set the parameters for this). - Carriers load now multiple cargo. - Added weight and volume attached to cargo objects for units. I reworked the respawning of cargo units, so that these volumes and weights are collected from the unit Descriptor properties! - There are still bugs, which I need to resolve, but it is going in the right direction. --- .../Moose/AI/AI_Cargo_Airplane.lua | 89 ++++++++++++++----- .../Moose/AI/AI_Cargo_Dispatcher.lua | 12 +-- Moose Development/Moose/Cargo/Cargo.lua | 23 +++++ Moose Development/Moose/Cargo/CargoGroup.lua | 31 ++++++- .../Moose/Wrapper/Identifiable.lua | 2 +- .../Moose/Wrapper/Positionable.lua | 3 +- 6 files changed, 129 insertions(+), 31 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 54f8b0cdd..28a71989f 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -34,10 +34,10 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) self:SetStartState( "Unloaded" ) - self:AddTransition( "Unloaded", "Pickup", "*" ) + self:AddTransition( { "Unloaded", "Loaded" }, "Pickup", "*" ) self:AddTransition( "Loaded", "Deploy", "*" ) - self:AddTransition( "Unloaded", "Load", "Boarding" ) + self:AddTransition( { "Unloaded", "Loaded" }, "Load", "Boarding" ) self:AddTransition( "Boarding", "Board", "Boarding" ) self:AddTransition( "Boarding", "Loaded", "Loaded" ) self:AddTransition( "Loaded", "Unload", "Unboarding" ) @@ -128,6 +128,11 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) -- Set carrier. self:SetCarrier( Airplane ) + Airplane:SetCargoBayWeightLimit( 5000 ) + Airplane:SetCargoBayVolumeLimit( 15 ) + + self.Relocating = true + return self end @@ -188,6 +193,7 @@ function AI_CARGO_AIRPLANE:SetCarrier( Airplane ) function Airplane:OnEventEngineShutdown( EventData ) + AICargo.Relocating = false AICargo:Landed( self.Airplane ) end @@ -355,17 +361,21 @@ end -- @param Wrapper.Point#COORDINATE Coordinate Place where the cargo is guided to if it is inside the load radius. function AI_CARGO_AIRPLANE:onafterLoad( Airplane, From, Event, To, Coordinate ) - if Airplane and Airplane:IsAlive()~=nil then + if Airplane and Airplane:IsAlive() ~= nil then for _,_Cargo in pairs( self.CargoSet:GetSet() ) do self:F({_Cargo:GetName()}) local Cargo=_Cargo --Cargo.Cargo#CARGO local InRadius = Cargo:IsInLoadRadius( Coordinate ) if InRadius then - self:__Board( 5 ) - Cargo:Board( Airplane, 25 ) - self.Cargo = Cargo - break + + -- Is there a cargo still unloaded? + if Cargo:IsUnLoaded() == true then + + self:__Board( 5, Cargo ) + Cargo:Board( Airplane, 25 ) + break + end end end @@ -379,14 +389,16 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -function AI_CARGO_AIRPLANE:onafterBoard( Airplane, From, Event, To ) +function AI_CARGO_AIRPLANE:onafterBoard( Airplane, From, Event, To, Cargo ) if Airplane and Airplane:IsAlive() then - self:F({ IsLoaded = self.Cargo:IsLoaded() } ) - if not self.Cargo:IsLoaded() then - self:__Board( 10 ) + + self:F({ IsLoaded = Cargo:IsLoaded() } ) + + if not Cargo:IsLoaded() then + self:__Board( 10, Cargo ) else - self:__Loaded( 1 ) + self:__Loaded( 1, Cargo ) end end @@ -398,12 +410,46 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -function AI_CARGO_AIRPLANE:onafterLoaded( Airplane, From, Event, To ) +function AI_CARGO_AIRPLANE:onafterLoaded( Airplane, From, Event, To, Cargo ) env.info("FF troops loaded into cargo plane") if Airplane and Airplane:IsAlive() then + -- Check if another cargo can be loaded into the airplane. + for _,_Cargo in pairs( self.CargoSet:GetSet() ) do + + self:F({_Cargo:GetName()}) + local Cargo =_Cargo --Cargo.Cargo#CARGO + + -- Is there a cargo still unloaded? + if Cargo:IsUnLoaded() == true then + + -- Only when the cargo is within load radius. + local InRadius = Cargo:IsInLoadRadius( Airplane:GetCoordinate() ) + if InRadius then + + local CargoBayFreeWeight = Airplane:GetCargoBayFreeWeight() + local CargoBayFreeVolume = Airplane:GetCargoBayFreeVolume() + + local CargoWeight = Cargo:GetWeight() + local CargoVolume = Cargo:GetVolume() + + -- Only when there is space within the bay to load the next cargo item! + if CargoBayFreeWeight > CargoWeight and CargoBayFreeVolume > CargoVolume then + + -- ok, board. + self:__Load( 5, Airplane:GetCoordinate() ) + + -- And start the boarding loop for the AI_CARGO_AIRPLANE object until the cargo is boarded. + --Cargo:Board( Airplane, 25 ) + return + end + end + end + end + self:F( { "Transporting" } ) + self.Transporting = true -- This will only be executed when there is no cargo boarded anymore. The dispatcher will then kick-off the deploy cycle! end end @@ -418,8 +464,11 @@ end function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To ) if Airplane and Airplane:IsAlive() then - self.Cargo:UnBoard() - self:__Unboard( 10 ) + local Cargos = Airplane:GetCargo() + for CargoID, Cargo in pairs( Cargos ) do + Cargo:UnBoard() + self:__Unboard( 10, Cargo ) + end end end @@ -430,13 +479,13 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -function AI_CARGO_AIRPLANE:onafterUnboard( Airplane, From, Event, To ) +function AI_CARGO_AIRPLANE:onafterUnboard( Airplane, From, Event, To, Cargo ) if Airplane and Airplane:IsAlive() then - if not self.Cargo:IsUnLoaded() then - self:__Unboard( 10 ) + if not Cargo:IsUnLoaded() then + self:__Unboard( 10, Cargo ) else - self:__Unloaded( 1 ) + self:__Unloaded( 1, Cargo ) end end @@ -448,7 +497,7 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -function AI_CARGO_AIRPLANE:onafterUnloaded( Airplane, From, Event, To ) +function AI_CARGO_AIRPLANE:onafterUnloaded( Airplane, From, Event, To, Cargo ) if Airplane and Airplane:IsAlive() then self.Airplane = Airplane diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index ea76ec694..9a5082f52 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -404,8 +404,8 @@ function AI_CARGO_DISPATCHER:onafterMonitor() -- The Pickup sequence ... -- Check if this Carrier need to go and Pickup something... -- So, if the cargo bay is not full yet with cargo to be loaded ... - self:I( { IsTransporting = AI_Cargo:IsTransporting() } ) - if AI_Cargo:IsTransporting() == false then + self:I( { IsRelocating = AI_Cargo:IsRelocating() } ) + if AI_Cargo:IsRelocating() == false then -- ok, so there is a free Carrier -- now find the first cargo that is Unloaded @@ -560,9 +560,11 @@ function AI_CARGO_DISPATCHER:OnAfterLoaded( From, Event, To, Carrier, Cargo ) end if self.DeployAirbasesSet then - - local DeployAirbase = self.DeployAirbasesSet:GetRandomAirbase() - self.AI_Cargo[Carrier]:Deploy( DeployAirbase, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ) ) + + if self.AI_Cargo[Carrier]:IsTransporting() == true then + local DeployAirbase = self.DeployAirbasesSet:GetRandomAirbase() + self.AI_Cargo[Carrier]:Deploy( DeployAirbase, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ) ) + end end self.PickupCargo[Carrier] = nil diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 5c2526919..e0b6f4d4a 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -875,6 +875,13 @@ do -- CARGO return self.CargoObject:GetCoordinate() end + --- Get the weight of the cargo. + -- @param #CARGO self + -- @return #number Weight The weight in kg. + function CARGO:GetWeight() + return self.Weight + end + --- Set the weight of the cargo. -- @param #CARGO self -- @param #number Weight The weight in kg. @@ -884,6 +891,22 @@ do -- CARGO return self end + --- Get the volume of the cargo. + -- @param #CARGO self + -- @return #number Volume The volume in kg. + function CARGO:GetVolume() + return self.Volume + end + + --- Set the volume of the cargo. + -- @param #CARGO self + -- @param #number Volume The volume in kg. + -- @return #CARGO + function CARGO:SetVolume( Volume ) + self.Volume = Volume + return self + end + --- Send a CC message to a @{Wrapper.Group}. -- @param #CARGO self -- @param #string Message diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index c2d45f142..b7c78a713 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -73,6 +73,7 @@ do -- CARGO_GROUP self:SetDeployed( false ) local WeightGroup = 0 + local VolumeGroup = 0 self.CargoGroup:Destroy() @@ -96,10 +97,7 @@ do -- CARGO_GROUP -- And we register the spawned unit as part of the CargoSet. local Unit = UNIT:Register( CargoUnitName ) - --local WeightUnit = Unit:GetDesc().massEmpty - --WeightGroup = WeightGroup + WeightUnit - local CargoUnit = CARGO_UNIT:New( Unit, Type, CargoUnitName, 10, LoadRadius, NearRadius ) - self.CargoSet:Add( CargoUnitName, CargoUnit ) + end -- Then we register the new group in the database @@ -107,6 +105,31 @@ do -- CARGO_GROUP -- Now we spawn the new group based on the template created. self.CargoObject = _DATABASE:Spawn( GroupTemplate ) + + for CargoUnitID, CargoUnit in pairs( self.CargoObject:GetUnits() ) do + local Desc = CargoUnit:GetDesc() + self:I( { Desc = Desc } ) + local WeightUnit = math.random( 80, 120 ) + if Desc then + WeightUnit = Desc.massEmpty + end + + local Box = CargoUnit:GetBoundingBox() + local VolumeUnit = ( Box.max.x - Box.min.x ) * ( Box.max.y - Box.min.y ) * ( Box.max.z - Box.min.z ) + self:I( { VolumeUnit = VolumeUnit, WeightUnit = WeightUnit } ) + + + local CargoUnitName = CargoUnit:GetName() + + local Cargo = CARGO_UNIT:New( CargoUnit, Type, CargoUnitName, 0, LoadRadius, NearRadius ) + Cargo:SetWeight( WeightUnit ) + Cargo:SetVolume( VolumeUnit ) + self.CargoSet:Add( CargoUnitName, Cargo ) + + WeightGroup = WeightGroup + WeightUnit + VolumeGroup = VolumeGroup + VolumeUnit + + end self:SetWeight( WeightGroup ) self.CargoLimit = 10 diff --git a/Moose Development/Moose/Wrapper/Identifiable.lua b/Moose Development/Moose/Wrapper/Identifiable.lua index 6e226a466..0f6e14f06 100644 --- a/Moose Development/Moose/Wrapper/Identifiable.lua +++ b/Moose Development/Moose/Wrapper/Identifiable.lua @@ -216,7 +216,7 @@ end function IDENTIFIABLE:GetDesc() self:F2( self.IdentifiableName ) - local DCSIdentifiable = self:GetDCSObject() + local DCSIdentifiable = self:GetDCSObject() -- DCS#Object if DCSIdentifiable then local IdentifiableDesc = DCSIdentifiable:getDesc() diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 888cf3232..44fa06d52 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -868,6 +868,7 @@ do -- Cargo function POSITIONABLE:GetCargoBayFreeWeight() local CargoWeight = 0 for CargoName, Cargo in pairs( self.__.Cargo ) do + self:F( { Cargo = Cargo } ) CargoWeight = CargoWeight + Cargo:GetWeight() end return self.__.CargoBayWeightLimit - CargoWeight @@ -883,7 +884,7 @@ do -- Cargo --- Get Cargo Bay Weight Limit in kg. -- @param #POSITIONABLE self -- @param #number WeightLimit - function POSITIONABLE:GetCargoBayFreeWeightLimit( WeightLimit ) + function POSITIONABLE:SetCargoBayWeightLimit( WeightLimit ) self.__.CargoBayWeightLimit = WeightLimit end From d91166c3c44cd079ab64a74a3c81b7c4398b2725 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 13 Aug 2018 00:36:34 +0200 Subject: [PATCH 249/420] Warehouse 0.1.4 zone: fixed GetRandomCoordinate inner, outer missing controllable: fixed path cannot be found group: added getrange unit: added getrange --- Moose Development/Moose/Core/Message.lua | 5 +- Moose Development/Moose/Core/Point.lua | 13 +- Moose Development/Moose/Core/Spawn.lua | 2 +- Moose Development/Moose/Core/Zone.lua | 2 +- .../Moose/Functional/Warehouse.lua | 995 ++++++++++++------ .../Moose/Wrapper/Controllable.lua | 40 +- Moose Development/Moose/Wrapper/Group.lua | 34 +- Moose Development/Moose/Wrapper/Unit.lua | 22 + 8 files changed, 782 insertions(+), 331 deletions(-) diff --git a/Moose Development/Moose/Core/Message.lua b/Moose Development/Moose/Core/Message.lua index 5ccfc3b65..1369f47be 100644 --- a/Moose Development/Moose/Core/Message.lua +++ b/Moose Development/Moose/Core/Message.lua @@ -294,8 +294,9 @@ end --- Sends a MESSAGE to a Coalition if the given Condition is true. -- @param #MESSAGE self --- @param CoalitionSide needs to be filled out by the defined structure of the standard scripting engine @{coalition.side}. --- @return #MESSAGE +-- @param CoalitionSide needs to be filled out by the defined structure of the standard scripting engine @{coalition.side}. +-- @param #boolean Condition Sends the message only if the condition is true. +-- @return #MESSAGE self function MESSAGE:ToCoalitionIf( CoalitionSide, Condition ) self:F( CoalitionSide ) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 55ba4ffda..0006d597f 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1098,11 +1098,14 @@ do -- COORDINATE --- Gets the nearest airbase with respect to the current coordinates. -- @param #COORDINATE self - -- @param #number AirbaseCategory Category of the airbase. + -- @param #number Category (Optional) Category of the airbase. Enumerator of @{Wrapper.Airbase#AIRBASE.Category}. + -- @param #number Coalition (Optional) Coalition of the airbase. -- @return Wrapper.Airbase#AIRBASE Closest Airbase to the given coordinate. -- @return #number Distance to the closest airbase in meters. - function COORDINATE:GetClosestAirbase(AirbaseCategory) - local airbases=AIRBASE.GetAllAirbases() + function COORDINATE:GetClosestAirbase(Category, Coalition) + + -- Get all airbases of the map. + local airbases=AIRBASE.GetAllAirbases(Coalition) local closest=nil local distmin=nil @@ -1110,7 +1113,7 @@ do -- COORDINATE for _,_airbase in pairs(airbases) do local airbase=_airbase --Wrapper.Airbase#AIRBASE local category=airbase:GetDesc().category - if AirbaseCategory and AirbaseCategory==category or AirbaseCategory==nil then + if Category and Category==category or Category==nil then local dist=self:Get2DDistance(airbase:GetCoordinate()) if closest==nil then distmin=dist @@ -1257,6 +1260,8 @@ do -- COORDINATE Path[#Path+1]=COORDINATE:NewFromVec2(_vec2) --COORDINATE:NewFromVec2(_vec2):SmokeGreen() end + --COORDINATE:NewFromVec2(path[1]):SmokeBlue() + --COORDINATE:NewFromVec2(path[#path]):SmokeBlue() else self:E("Path is nil. No valid path on road could be found.") diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 586ac8396..bd2f4653c 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -496,7 +496,7 @@ end --- Sets the coalition of the spawned group. Note that it might be necessary to also set the country explicitly! -- @param #SPAWN self --- @param #DCS.coalition Coaliton Coaliton of the group as number of enumerator, i.e. 0=coaliton.side.NEUTRAL, 1=coaliton.side.RED, 2=coalition.side.BLUE. +-- @param DCS#coalition.side Coalition Coalition of the group as number of enumerator, i.e. 0=coaliton.side.NEUTRAL, 1=coaliton.side.RED, 2=coalition.side.BLUE. -- @return #SPAWN self function SPAWN:InitCoalition( Coalition ) self:F({coalition=Coalition}) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index c417ad6a6..207587aee 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -933,7 +933,7 @@ end function ZONE_RADIUS:GetRandomCoordinate( inner, outer ) self:F( self.ZoneName, inner, outer ) - local Coordinate = COORDINATE:NewFromVec2( self:GetRandomVec2() ) + local Coordinate = COORDINATE:NewFromVec2( self:GetRandomVec2(inner, outer) ) self:T3( { Coordinate = Coordinate } ) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 49bd8d8aa..00c3714c0 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -22,11 +22,14 @@ -- @field #string ClassName Name of the class. -- @field #boolean Debug If true, send debug messages to all. -- @field #boolean Report If true, send status messages to coalition. +-- @field Wrapper.Static#STATIC warehouse The phyical warehouse structure. -- @field DCS#coalition.side coalition Coalition ID the warehouse belongs to. -- @field DCS#country.id country Country ID the warehouse belongs to. --- @field Wrapper.Airbase#AIRBASE homebase Airbase the warehouse belongs to. +-- @field Wrapper.Airbase#AIRBASE airbase Airbase the warehouse belongs to. -- @field DCS#Airbase.Category category Category of the home airbase, i.e. airdrome, helipad/farp or ship. -- @field Core.Point#COORDINATE coordinate Coordinate of the warehouse. +-- @field Core.Point#COORDINATE road Closest point to warehouse on road. +-- @field Core.Point#COORDINATE road Closest point to warehouse on rail. -- @field Core.Zone#ZONE spawnzone Zone in which assets are spawned. -- @field #string wid Identifier of the warehouse printed before other output to DCS.log file. -- @field #number uid Unit identifier of the warehouse. Derived from the associated airbase. @@ -76,43 +79,52 @@ WAREHOUSE = { ClassName = "WAREHOUSE", Debug = false, Report = true, + warehouse = nil, coalition = nil, country = nil, - homebase = nil, + airbase = nil, category = nil, coordinate = nil, + road = nil, + rail = nil, spawnzone = nil, wid = nil, uid = nil, markerid = nil, - warehouse = nil, assetid = 0, queueid = 0, stock = {}, queue = {}, + pending = {}, } --- Item of the warehouse stock table. -- @type WAREHOUSE.Stockitem --- @field #number id Unique id of the asset. +-- @field #number uid Unique id of the asset. -- @field #string templatename Name of the template group. -- @field #table template The spawn template of the group. -- @field DCS#Group.Category category Category of the group. -- @field #string unittype Type of the first unit of the group as obtained by the Object.getTypeName() DCS API function. +-- @field #number range Range of the unit in meters. +-- @field #number speedmax Maximum speed in km/h the unit can do. -- @field #WAREHOUSE.Attribute attribute Generalized attribute of the group. --- Item of the warehouse queue table. --- queueitem={uid=self.qid, prio=Prio, airbase=Airbase, assetdesc=AssetDescriptor, assetdescval=AssetDescriptorValue, nasset=nAsset, transporttype=TransportType, ntransport=nTransport} -- @type WAREHOUSE.Queueitem -- @field #number uid Unique id of the queue item. -- @field #number prio Priority of the request. --- @field Wrapper.Airbase#AIRBASE airbase Requesting airbase. +-- @field #WAREHOUSE warehouse Requesting warehouse. +-- @field Wrapper.Airbase#AIRBASE airbase Requesting airbase or airbase beloning to requesting warehouse. -- @field DCS#Airbase.Category category Category of the requesting airbase, i.e. airdrome, helipad/farp or ship. -- @field #WAREHOUSE.Descriptor assetdesc Descriptor of the requested asset. -- @field assetdescval Value of the asset descriptor. Type depends on descriptor. -- @field #number nasset Number of asset groups requested. -- @field #WAREHOUSE.TransportType transporttype Transport unit type. -- @field #number ntransport Number of transport units requested. +-- @field #number ndelivered Number of groups delivered to destination. Is managed automatically. +-- @field #number ntransporthome Number of transports back home. Is managed automatically. +-- @field Core.Set#SET_GROUP cargogroupset Set of cargo groups do be delivered. Is managed automatically. +-- @field Core.Set#SET_GROUP transportgroupset Set of cargo transport groups. Is managed automatically. --- Descriptors enumerator describing the type of the asset in stock. -- @type WAREHOUSE.Descriptor @@ -159,7 +171,7 @@ WAREHOUSE.TransportType = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.1.3" +WAREHOUSE.version="0.1.4" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehuse todo list. @@ -169,14 +181,14 @@ WAREHOUSE.version="0.1.3" -- DONE: Add AI_CARGO_AIRPLANE -- DONE: Add AI_CARGO_APC -- DONE: Add AI_CARGO_HELICOPTER +-- DONE: Switch to AI_CARGO_XXX_DISPATCHER +-- DONE: Add queue. -- TODO: Write documentation. -- TODO: Put active groups into the warehouse, e.g. when they were transported to this warehouse. -- TODO: Spawn warehouse assets as uncontrolled or AI off and activate them when requested. -- TODO: Handle cases with immobile units. --- DONE: Add queue. -- TODO: How to handle multiple units in a transport group? --- DONE: Switch to AI_CARGO_XXX_DISPATCHER --- TODO: Add phyical object, if destroyed asssets are gone. +-- DONE: Add phyical object, if destroyed asssets are gone. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor(s) @@ -184,38 +196,61 @@ WAREHOUSE.version="0.1.3" --- WAREHOUSE constructor. Creates a new WAREHOUSE object accociated with an airbase. -- @param #WAREHOUSE self --- @param Wrapper.Airbase#AIRBASE airbase The airbase at which the warehouse is constructed. +-- @param Wrapper.Static#STATIC warehouse The physical structure of the warehouse. -- @return #WAREHOUSE self -function WAREHOUSE:NewAirbase(airbase) - BASE:E({airbase=airbase:GetName()}) +function WAREHOUSE:New(warehouse) + BASE:E({warehouse=warehouse:GetName()}) + + if warehouse==nil then + BASE:E("ERROR: Warehouse does not exist!") + return nil + end -- Print version. - env.info(string.format("Adding warehouse v%s for airbase %s", WAREHOUSE.version, airbase:GetName())) + env.info(string.format("Adding warehouse v%s for structure %s", WAREHOUSE.version, warehouse:GetName())) -- Inherit everthing from FSM class. local self = BASE:Inherit( self, FSM:New() ) -- #WAREHOUSE -- Set some string id for output to DCS.log file. - self.wid=string.format("WAREHOUSE %s | ", airbase:GetName()) + self.wid=string.format("WAREHOUSE %s | ", warehouse:GetName()) -- Set some variables. - self.homebase=airbase - self.coordinate=airbase:GetCoordinate() - self.coalition=airbase:GetCoalition() - self.country=airbase:GetCountry() - self.category=airbase:GetDesc().category - self.uid=airbase:GetID() + self.warehouse=warehouse + self.uid=warehouse:GetID() + self.coalition=warehouse:GetCoalition() + self.country=warehouse:GetCountry() + self.coordinate=warehouse:GetCoordinate() - -- Get the closest point on road. - local _road=self.coordinate:GetClosestPointToRoad():GetVec2() + --TODO: Add max range parameter to check if airbase is close enough. Range should be? ~3 km maybe? + local _airbase=self.coordinate:GetClosestAirbase(nil, self.coalition) + + if _airbase and _airbase:GetCoordinate():Get2DDistance(self.coordinate) < 2500 then + self.airbase=_airbase + self.category=self.airbase:GetDesc().category + end + + + -- Get the closest point on road and rail. + local _road=self.coordinate:GetClosestPointToRoad() + local _rail=self.coordinate:GetClosestPointToRoad(true) + + if _road and _road:Get2DDistance(self.coordinate) < 2500 then + self.road=_road + end + if _rail and _rail:Get2DDistance(self.coordinate) < 2500 then + self.rail=_rail + end + -- Define the default spawn zone. - self.spawnzone=ZONE_RADIUS:New("Spawnzone",_road, 200) + self.spawnzone=ZONE_RADIUS:New("Spawnzone", warehouse:GetVec2(), 200) -- Add FSM transitions. self:AddTransition("*", "Start", "Running") self:AddTransition("*", "Status", "*") self:AddTransition("*", "Request", "*") + self:AddTransition("*", "Arrived", "*") self:AddTransition("*", "Delivered", "*") --- Triggers the FSM event "Start". Starts the warehouse. @@ -250,16 +285,28 @@ function WAREHOUSE:NewAirbase(airbase) -- @param #WAREHOUSE.Queueitem Request Information table of the request. + --- Triggers the FSM event "Arrived", i.e. when a group has arrived at the destination. + -- @function [parent=#WAREHOUSE] Arrived + -- @param #WAREHOUSE self + -- @param Wrapper.Group#GROUP group Group that has arrived. + + --- Triggers the FSM event "Arrived" after a delay, i.e. when a group has arrived at the destination. + -- @function [parent=#WAREHOUSE] __Arrived + -- @param #WAREHOUSE self + -- @param #number delay Delay in seconds. + -- @param Wrapper.Group#GROUP group Group that has arrived. + + --- Triggers the FSM event "Delivered". A group has been delivered from the warehouse to another airbase or warehouse. -- @function [parent=#WAREHOUSE] Delivered -- @param #WAREHOUSE self - -- @param Wrapper.Group#GROUP group Group that was delivered. + -- @param Core.Set#SET_GROUP groupset Set of groups that were delivered. --- Triggers the FSM event "Delivered" after a delay. A group has been delivered from the warehouse to another airbase or warehouse. -- @function [parent=#WAREHOUSE] __Delivered -- @param #WAREHOUSE self -- @param #number delay Delay in seconds. - -- @param Wrapper.Group#GROUP group Group that was delivered. + -- @param Core.Set#SET_GROUP groupset Set of groups that were delivered. return self end @@ -274,14 +321,22 @@ end -- @param #string Event Event. -- @param #string To To state. function WAREHOUSE:onafterStart(From, Event, To) - self:E(self.wid..string.format("Starting warehouse at airbase %s, category %d, coalition %d, country %d", self.homebase:GetName(), self.category, self.coalition, self.country)) + + -- Short info. + local text=string.format("Starting warehouse %s:\n",self.warehouse:GetName()) + text=text..string.format("Coaliton = %d\n", self.coalition) + text=text..string.format("Country = %d\n", self.country) + text=text..string.format("Airbase = %s (%s)\n", tostring(self.airbase:GetName()), tostring(self.category)) + env.info(text) + + -- Save self in static object. Easier to retrieve later. + self.warehouse:SetState(self.warehouse, "WAREHOUSE", self) -- Debug mark spawn zone. - --self.spawnzone:BoundZone(60, country.id.GERMANY) self.spawnzone:BoundZone(60, self.country) - self.spawnzone:GetCoordinate():MarkToAll("Spawnzone of warehouse "..self.homebase:GetName()) + self.spawnzone:GetCoordinate():MarkToAll("Spawnzone of warehouse "..self.warehouse:GetName()) - -- handle events + -- Handle events: -- event takeoff -- event landing -- event crash/dead @@ -298,7 +353,11 @@ end -- @param #string Event Event. -- @param #string To To state. function WAREHOUSE:onafterStatus(From, Event, To) - self:E(self.wid..string.format("Checking warehouse status of airbase %s", self.homebase:GetName())) + self:E(self.wid..string.format("Checking warehouse status of %s", self.warehouse:GetName())) + + -- Print queue. + self:_PrintQueue(self.queue, "Queue0:") + self:_PrintQueue(self.pending, "Pending0:") -- Create a mark with the current assets in stock. if self.markerid~=nil then @@ -324,8 +383,9 @@ function WAREHOUSE:onafterStatus(From, Event, To) end -- Print queue. - self:_PrintQueue() - + self:_PrintQueue(self.queue, "Queue:") + self:_PrintQueue(self.pending, "Pending:") + -- Check queue and handle requests if possible. local request=self:_CheckQueue() @@ -334,12 +394,107 @@ function WAREHOUSE:onafterStatus(From, Event, To) self:Request(request) end + -- Print queue. + self:_PrintQueue(self.queue, "Queue2:") + self:_PrintQueue(self.pending, "Pending2:") + -- Call status again in 30 sec. - self:__Status(10) + self:__Status(30) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Spawns requested asset at warehouse or associated airbase. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE.Queueitem Request Information table of the request. +-- @return Core.Set#SET_GROUP Set of groups that were spawned. +-- @return #WAREHOUSE.Attribute Generalized attribute of asset. +-- @return DCS#Group.Category Category of asset, i.e. ground, air, ship, ... +function WAREHOUSE:_SpawnAssetRequest(Request) + + -- Filter the requested cargo assets. + local _assetstock=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval, Request.nasset) + + if #_assetstock==0 then + return nil,nil,nil + end + + -- General type and category. + local _cargotype=_assetstock[1].attribute --#WAREHOUSE.Attribute + local _cargocategory=_assetstock[1].category --DCS#Group.Category + + -- Now we try to find all parking spots for all cargo groups in advance. Due to the for loop, the parking spots do not get updated while spawning. + local Parking={} + if _cargocategory==Group.Category.AIRPLANE or _cargocategory==Group.Category.HELICOPTER then + Parking=self:_GetParkingForAssets(_assetstock) + end + + -- Create an empty set. + local groupset=SET_GROUP:New():FilterDeads() + + -- Spawn the assets. + local _delid={} + local _spawngroups={} + + -- Loop over cargo requests. + for i=1,Request.nasset do + + -- Get stock item. + local _assetitem=_assetstock[i] --#WAREHOUSE.Stockitem + + -- Find a random point within the spawn zone. + local spawncoord=self.spawnzone:GetRandomCoordinate() + + -- Alias of the group. + local _alias=self:_Alias(_assetitem,Request) + + -- Spawn object. Spawn with ALIAS here or DCS crashes! + --local _spawn=SPAWN:NewFromTemplate(_assetitem.template,_assetitem.templatename,_alias):InitCoalition(self.coalition):InitCountry(self.country) + local _spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias):InitCoalition(self.coalition):InitCountry(self.country) + + local _group=nil --Wrapper.Group#GROUP + local _attribute=_assetitem.attribute + + if _assetitem.category==Group.Category.GROUND then + + -- Spawn ground troops. + _group=_spawn:SpawnFromCoordinate(spawncoord) + + elseif _assetitem.category==Group.Category.AIRPLANE or _assetitem.category==Group.Category.HELICOPTER then + + --TODO: spawn only so many groups as there are parking spots. Adjust request and create a new one with the reduced number! + + -- Spawn air units. + _group=_spawn:SpawnAtAirbase(self.airbase, SPAWN.Takeoff.Cold, nil, nil, true, Parking[i]) + + elseif _assetitem.category==Group.Category.TRAIN then + + -- Spawn train. + local _railroad=self.coordinate:GetClosestPointToRoad(true) + if _railroad then + _group=_spawn:SpawnFromCoordinate(_railroad) + end + + end + + if _group then + --_spawngroups[i]=_group + groupset:AddGroup(_group) + table.insert(_delid,_assetitem.uid) + else + self:E(self.wid.."ERROR: cargo asset could not be spawned!") + end + + end + + -- Delete spawned items from warehouse stock. + for _,_id in pairs(_delid) do + self:_DeleteStockItem(_id) + end + + return groupset,_cargotype,_cargocategory +end + --- On before "Request" event. Checks if the request can be fullfilled. -- @param #WAREHOUSE self -- @param #string From From state. @@ -348,14 +503,26 @@ end -- @param #WAREHOUSE.Queueitem Request Information table of the request. -- @return #boolean If true, request is granted. function WAREHOUSE:onbeforeRequest(From, Event, To, Request) - env.info(self.wid..string.format("Airbase %s requesting %d assets of %s=%s by transport %s", - Request.airbase:GetName(), Request.nasset, tostring(Request.assetdesc), tostring(Request.assetdescval), tostring(Request.transporttype))) + --env.info(self.wid..string.format("Warehouse %s requesting %d assets of %s=%s by transport %s", + --Request.warehouse:GetName(), Request.nasset, tostring(Request.assetdesc), tostring(Request.assetdescval), tostring(Request.transporttype))) -- Distance from warehouse to requesting airbase. local distance=self.coordinate:Get2DDistance(Request.airbase:GetCoordinate()) -- Filter the requested assets. local _assets=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval, Request.nasset) + + -- Check if destination is in range for all requested assets. + for _,_asset in pairs(_assets) do + local asset=_asset --#WAREHOUSE.Stockitem + if asset.range request denied. if #_assets < Request.nasset then @@ -376,99 +543,28 @@ end -- @param #string To To state. -- @param #WAREHOUSE.Queueitem Request Information table of the request. function WAREHOUSE:onafterRequest(From, Event, To, Request) - --env.info(self.wid..string.format("Airbase %s requesting asset %s = %s.", Airbase:GetName(), tostring(AssetDescriptor), tostring(AssetDescriptorValue))) ------------------------------------------------------------------------------------------------------------------------------------ -- Cargo assets. ------------------------------------------------------------------------------------------------------------------------------------ - -- New empty cargo set in case we need it. - local CargoGroups = SET_CARGO:New() - - --TODO: make nearradius depended on transport type and asset type. - local _loadradius=5000 - local _nearradius=35 - - -- Filter the requested cargo assets. - local _assetstock=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval, Request.nasset) - - -- General type and category. - local _cargotype=_assetstock[1].attribute --#WAREHOUSE.Attribute - local _cargocategory=_assetstock[1].category --DCS#Group.Category + -- Spawn assets. + local _spawngroups,_cargotype,_cargocategory=self:_SpawnAssetRequest(Request) --Core.Set#SET_GROUP - -- Now we try to find all parking spots for all cargo groups in advance. Due to the for loop, the parking spots do not get updated while spawning. - local Parking={} - if _cargocategory==Group.Category.AIRPLANE or _cargocategory==Group.Category.HELICOPTER then - Parking=self:_GetParkingForAssets(_assetstock) - end - - -- Spawn the assets. - local _delid={} - local _spawngroups={} + -- Add groups to cargo if they don't go by themselfs. + local CargoGroups --Core.Set#SET_CARGO + if Request.transporttype ~= WAREHOUSE.TransportType.SELFPROPELLED then - -- Loop over cargo requests. - for i=1,Request.nasset do - - -- Get stock item. - local _assetitem=_assetstock[i] --#WAREHOUSE.Stockitem - - -- Find a random point within the spawn zone. - local spawncoord=self.spawnzone:GetRandomCoordinate() - - -- Alias of the group. Spawn with ALIAS here or DCS crashes! - --local _alias=string.format("%s_WID-%02d_AID-%04d_RID-%04d", _assetitem.unittype, self.uid,_assetitem.id,Request.uid) - local _alias=self:_Alias(_assetitem,Request) - --local _spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias) - local _spawn=SPAWN:NewFromTemplate(_assetitem.template,_assetitem.templatename,_alias) - - local _group=nil --Wrapper.Group#GROUP - - -- Set a marker for the spawned group. - spawncoord:MarkToAll(string.format("Spawnpoint %s",_alias)) - - local _attribute=_assetitem.attribute - - if _assetitem.category==Group.Category.GROUND then - - -- Spawn ground troops. - _group=_spawn:SpawnFromCoordinate(spawncoord) - env.info(string.format("FF spawning group %s", _alias)) - - elseif _assetitem.category==Group.Category.AIRPLANE or _assetitem.category==Group.Category.HELICOPTER then - - -- Spawn air units. - local _takeoff=SPAWN.Takeoff.Cold - - _group=_spawn:InitUnControlled(true):SpawnAtAirbase(self.homebase,_takeoff, nil, nil, true, Parking[i]) - - elseif _assetitem.category==Group.Category.TRAIN then - - -- Spawn train. - local _railroad=self.coordinate:GetClosestPointToRoad(true) - if _railroad then - _group=_spawn:SpawnFromCoordinate(_railroad) - end - - end - - if _group then - _spawngroups[i]=_group - table.insert(_delid,_assetitem.id) - - -- Add groups to cargo. - if Request.transporttype ~= WAREHOUSE.TransportType.SELFPROPELLED then - local cargogroup = CARGO_GROUP:New(_group, _alias, _alias, _loadradius, _nearradius) - CargoGroups:AddCargo(cargogroup) - end - else - self:E(self.wid.."ERROR: cargo asset could not be spawned!") + --TODO: make nearradius depended on transport type and asset type. + local _loadradius=5000 + local _nearradius=35 + CargoGroups = SET_CARGO:New() + for _,_group in pairs(_spawngroups:GetSetObjects()) do + local cargogroup = CARGO_GROUP:New(_group, _cargotype, "Peter", _loadradius, _nearradius) + CargoGroups:AddCargo(cargogroup) end end - -- Delete spawned items from warehouse stock. - for _,_id in pairs(_delid) do - self:_DeleteStockItem(_id) - end ------------------------------------------------------------------------------------------------------------------------------------ -- Self propelled assets. @@ -476,31 +572,52 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- No transport unit requested. Assets go by themselfes. if Request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then - - for _i,_spawngroup in pairs(_spawngroups) do - + env.info("FF selfpropelled") + --for _i,_spawngroup in pairs(_spawngroups) do + for _,_spawngroup in pairs(_spawngroups:GetSetObjects()) do + local group=_spawngroup --Wrapper.Group#GROUP - local ToCoordinate=Request.airbase:GetZone():GetRandomCoordinate() + + local ToCoordinate=Request.warehouse.spawnzone:GetRandomCoordinate(50) + ToCoordinate:MarkToAll("Destination") + + local dist=Request.airbase:GetCoordinate():Get2DDistance(ToCoordinate) + + env.info("Dist to warehouse = "..dist) + env.info("FF group "..group:GetName()) -- Route cargo to their destination. - if _cargocategory==Group.Category.GROUND then + if _cargocategory==Group.Category.GROUND then + env.info("FF route ground "..group:GetName()) self:_RouteGround(group, ToCoordinate) elseif _cargocategory==Group.Category.AIRPLANE then + env.info("FF route plane "..group:GetName()) self:_RouteAir(group, Request.airbase) elseif _cargocategory==Group.Category.HELICOPTER then + env.info("FF route helo "..group:GetName()) self:_RouteAir(group, Request.airbase) elseif _cargocategory==Group.Category.SHIP then self:E("ERROR: self propelled ship not implemented yet!") elseif _cargocategory==Group.Category.TRAIN then + env.info("FF route train "..group:GetName()) self:_RouteTrain(group, ToCoordinate) + else + self:E(self.wid..string.format("ERROR: unknown category %s for self propelled cargo %s!",tostring(_cargocategory), tostring(group:GetName()))) end end - -- Delete request from queue. + -- Add cargo groups to request. + Request.cargogroupset=_spawngroups + Request.ndelivered=0 + + --local bla=UTILS.DeepCopy(Request) + + -- Add request to pending queue. table.insert(self.pending, Request) + -- Delete request from queue. self:_DeleteQueueItem(Request.uid) - + -- No cargo transport necessary. return end @@ -512,10 +629,10 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) env.info("FF cargo set name(s) = "..CargoGroups:GetObjectNames()) ---------------------------------------------------------------- - local TransportSet = SET_GROUP:New() + local TransportSet = SET_GROUP:New():FilterDeads() -- Pickup and deploy zones/bases. - local PickupAirbaseSet = SET_AIRBASE:New():AddAirbase(self.homebase) + local PickupAirbaseSet = SET_AIRBASE:New():AddAirbase(self.airbase) local DeployAirbaseSet = SET_AIRBASE:New():AddAirbase(Request.airbase) local DeployZoneSet = SET_ZONE:New():AddZone(Request.airbase:GetZone()) @@ -556,8 +673,8 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Spawn plane at airport in uncontrolled state. local _takeoff=SPAWN.Takeoff.Cold --local spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias) - local spawn=SPAWN:NewFromTemplate(_assetitem.template,_assetitem.templatename,_alias) - local spawngroup=spawn:InitUnControlled(true):SpawnAtAirbase(self.homebase,_takeoff, nil, nil, false, _parking) + local spawn=SPAWN:NewFromTemplate(_assetitem.template,_assetitem.templatename,_alias):InitCoalition(self.coalition):InitCountry(self.country) + local spawngroup=spawn:InitUnControlled(true):SpawnAtAirbase(self.airbase,_takeoff, nil, nil, false, _parking) if spawngroup then -- Set state of warehouse so we can retrieve it later. @@ -566,7 +683,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Add group to transportset. TransportSet:AddGroup(spawngroup) - table.insert(_delid,_assetitem.id) + table.insert(_delid,_assetitem.uid) end end @@ -595,8 +712,8 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Spawn helo at airport. local _takeoff=SPAWN.Takeoff.Hot --local spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias) - local spawn=SPAWN:NewFromTemplate(_assetitem.template,_assetitem.templatename,_alias) - local spawngroup=spawn:InitUnControlled(false):SpawnAtAirbase(self.homebase,_takeoff, nil, nil, false, _parking) + local spawn=SPAWN:NewFromTemplate(_assetitem.template,_assetitem.templatename,_alias):InitCoalition(self.coalition):InitCountry(self.country) + local spawngroup=spawn:InitUnControlled(false):SpawnAtAirbase(self.airbase,_takeoff, nil, nil, false, _parking) if spawngroup then -- Set state of warehouse so we can retrieve it later. @@ -605,7 +722,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Add group to transportset. TransportSet:AddGroup(spawngroup) - table.insert(_delid,_assetitem.id) + table.insert(_delid,_assetitem.uid) else self:E(self.wid.."ERROR: spawngroup helo transport does not exist!") end @@ -620,7 +737,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) CargoTransport = AI_CARGO_DISPATCHER_HELICOPTER:New(TransportSet, CargoGroups, DeployZoneSet) -- Home zone. - CargoTransport:SetHomeBase(self.homebase) + CargoTransport:Setairbase(self.airbase) --CargoTransport:SetHomeZone(self.spawnzone) elseif Request.transporttype==WAREHOUSE.TransportType.APC then @@ -637,8 +754,8 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) local _alias=self:_Alias(_assetitem, Request) -- Spawn plane at airport in uncontrolled state. - local spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias) - local spawn=SPAWN:NewFromTemplate(_assetitem.template,_assetitem.templatename,_alias) + --local spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias) + local spawn=SPAWN:NewFromTemplate(_assetitem.template,_assetitem.templatename,_alias):InitCoalition(self.coalition):InitCountry(self.country) local spawngroup=spawn:SpawnFromCoordinate(self.spawnzone:GetRandomCoordinate()) if spawngroup then @@ -648,7 +765,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Add group to transportset. TransportSet:AddGroup(spawngroup) - table.insert(_delid,_assetitem.id) + table.insert(_delid,_assetitem.uid) end end @@ -700,8 +817,8 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Get warehouse state. local warehouse=Carrier:GetState(Carrier, "WAREHOUSE") --#WAREHOUSE - -- Trigger Delivered event. - warehouse:__Delivered(1, group) + -- Trigger Arrived event. + warehouse:__Arrived(1, group) end --- On after BackHome event. @@ -727,21 +844,119 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- On after "Delivered" event. +--- On after "Arrived" event. Triggered when a group has arrived at its destination. -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Wrapper.Group#GROUP group The group that was delivered. -function WAREHOUSE:onafterDelivered(From, Event, To, group) - self:E(self.wid..string.format("Cargo %s delivered!", group:GetName())) +function WAREHOUSE:onafterArrived(From, Event, To, group) + + self:E(self.wid..string.format("Cargo %s arrived!", tostring(group:GetName()))) + group:SmokeOrange() + -- Update pending request. + self:_UpdatePending(group) + + -- Update pending + + --[[ -- Route a ground group to the closest point on road. if group:IsGround() and group:GetSpeedMax()>0 then local road=group:GetCoordinate():GetClosestPointToRoad() local speed=group:GetSpeedMax()*0.6 group:RouteGroundTo(road, speed, "Off Road") end + ]] +end + +--- Update the pending requests by removing assets that have arrived. +-- @param #WAREHOUSE self +-- @param Wrapper.Group#GROUP group The group that has arrived at its destination. +function WAREHOUSE:_UpdatePending(group) + + -- Get request from group name. + local request=self:_GetRequestOfGroup(group) + + -- Increase number of delivered assets. + request.ndelivered=request.ndelivered+1 + + -- TODO: Well this does not handle the case when a group that has been delivered was killed! + if request.ndelivered>=request.cargogroupset:Count() then + self:__Delivered(5, request.cargogroupset) + end +end + +--- Get the request from the group. +-- @param #WAREHOUSE self +-- @param Wrapper.Group#GROUP group The group from which the info is gathered. +-- @return #WAREHOUSE.Queueitem The request belonging to this group. +function WAREHOUSE:_GetRequestOfGroup(group) + + local wid,aid,rid=self:_GetInfoFromGroup(group) + + for _,_request in pairs(self.pending) do + local request=_request --#WAREHOUSE.Queueitem + if request.uid==rid then + return request + end + end + +end + +--- Get warehouse id, asset id and request id from group name. +-- @param #WAREHOUSE self +-- @param Wrapper.Group#GROUP group The group from which the info is gathered. +function WAREHOUSE:_GetInfoFromGroup(group) + + ---@param #string text The text to analyse. + local function analyse(text) + + -- Get rid of #0001 tail from spawn. + local unspawned=UTILS.Split(text, "#")[1] + + -- Split keywords. + local keywords=UTILS.Split(unspawned, "_") + + local _wid=nil + local _aid=nil + local _rid=nil + + -- loop + for _,keys in pairs(keywords) do + local str=UTILS.Split(keys,"-") + local key=str[1] + local val=str[2] + if key:find("WID") then + _wid=tonumber(val) + elseif key:find("AID") then + _aid=tonumber(val) + elseif key:find("RID") then + _rid=tonumber(val) + end + end + + return _wid,_aid,_rid + end + + local name=group:GetName() + env.info("FF Name = "..tostring(name)) + local wid,aid,rid=analyse(name) + env.info("FF warehouse id = %s"..tostring(wid)) + env.info("FF asset id = %s"..tostring(aid)) + env.info("FF request id = %s"..tostring(rid)) + +end + +--- On after "Delivered" event. +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Core.Set#SET_GROUP groupset The set of cargo groups that was delivered. +function WAREHOUSE:onafterDelivered(From, Event, To, groupset) + + env.info("FF all assets delivered!") end @@ -766,28 +981,36 @@ function WAREHOUSE:AddAsset(templategroupname, ngroups) local DCSgroup=group:GetDCSObject() local DCSunit=DCSgroup:getUnit(1) local DCSdesc=DCSunit:getDesc() - local DCSdisplay=DCSunit:getDesc().displayName + local DCSdisplay=DCSdesc.displayName local DCScategory=DCSgroup:getCategory() local DCStype=DCSunit:getTypeName() + local SpeedMax=group:GetSpeedMax() + local RangeMin=group:GetRange() env.info(string.format("group name = %s", group:GetName())) env.info(string.format("display name = %s", DCSdisplay)) env.info(string.format("category = %s", DCScategory)) env.info(string.format("type = %s", DCStype)) - self:E({desc=DCSdesc}) + env.info(string.format("speed max = %s", tostring(SpeedMax))) + env.info(string.format("range min = %s", tostring(RangeMin))) + self:E({fullassetdesc=DCSdesc}) local attribute=self:_GetAttribute(templategroupname) -- Add this n times to the table. for i=1,n do local stockitem={} --#WAREHOUSE.Stockitem + self.assetid=self.assetid+1 - stockitem.id=self.assetid + + stockitem.uid=self.assetid stockitem.templatename=templategroupname stockitem.template=UTILS.DeepCopy(_DATABASE.Templates.Groups[templategroupname].Template) stockitem.category=DCScategory stockitem.unittype=DCStype stockitem.attribute=attribute + stockitem.range=RangeMin + stockitem.speedmax=SpeedMax -- Modify the template so that the group is spawned with the right coalition. stockitem.template.CoalitionID=self.coalition @@ -818,16 +1041,31 @@ function WAREHOUSE:SetSpawnZone(zone) return self end ---- Add a request for the warehouse. +--- Add a delayed request for the warehouse. -- @param #WAREHOUSE self --- @param Wrapper.Airbase#AIRBASE Airbase airbase requesting supply. +-- @param #number delay Delay before the request is made in seconds. +-- @param #WAREHOUSE warehouse The warehouse requesting supply. -- @param #WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. -- @param AssetDescriptorValue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. -- @param #number nAsset Number of groups requested that match the asset specification. -- @param #WAREHOUSE.TransportType TransportType Type of transport. -- @param #number nTransport Number of transport units requested. -- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. -function WAREHOUSE:AddRequest(airbase, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType, nTransport, Prio) +function WAREHOUSE:__AddRequest(delay, warehouse, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType, nTransport, Prio) + SCHEDULER:New(nil,WAREHOUSE.AddRequest,{self, warehouse, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType, nTransport, Prio}, delay) +end + + +--- Add a request for the warehouse. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE warehouse The warehouse requesting supply. +-- @param #WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. +-- @param AssetDescriptorValue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. +-- @param #number nAsset Number of groups requested that match the asset specification. +-- @param #WAREHOUSE.TransportType TransportType Type of transport. +-- @param #number nTransport Number of transport units requested. +-- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. +function WAREHOUSE:AddRequest(warehouse, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType, nTransport, Prio) nAsset=nAsset or 1 TransportType=TransportType or WAREHOUSE.TransportType.SELFPROPELLED @@ -838,25 +1076,28 @@ function WAREHOUSE:AddRequest(airbase, AssetDescriptor, AssetDescriptorValue, nA -- if warehouse or requestor is a FARP, plane asset and transport not possible. -- if requestor or warehouse is a SHIP, APC transport not possible, SELFPROPELLED only for AIR/SHIP -- etc. etc... - - local request_category=airbase:GetDesc().category --DCS#Airbase.Category - - -- No airplanes from to to FARPS. - if self.category==Airbase.Category.HELIPAD or request_category==Airbase.Category.HELIPAD then - if TransportType==WAREHOUSE.TransportType.AIRPLANE then - self:E("ERROR: incorrect request. Warehouse or requestor is FARP. No transport by plane possible!") - return - end + + --[[ + local warehouse=nil --#WAREHOUSE + local airbase=nil --Wrapper.Airbase#AIRBASE + if requestor:IsInstanceOf("AIRBASE") then + airbase=requestor + elseif requestor:IsInstanceOf("WAREHOUSE") then + warehouse=requestor + airbase=warehouse.airbase end - + ]] + local request_air=false local request_plane=false local request_helo=false local request_ground=false local request_naval=false + local request_category=warehouse.category or -1 --DCS#Airbase.Category -- Check if category is matching if AssetDescriptor==WAREHOUSE.Descriptor.CATEGORY then + if AssetDescriptorValue==Group.Category.AIRPLANE then request_plane=true elseif AssetDescriptorValue==Group.Category.HELICOPTER then @@ -871,6 +1112,7 @@ function WAREHOUSE:AddRequest(airbase, AssetDescriptor, AssetDescriptorValue, nA self:E("ERROR: incorrect request. Asset Descriptor missmatch! Has to be Group.Cagetory.AIRPLANE, ...") return end + end -- Check attribute is matching @@ -912,6 +1154,7 @@ function WAREHOUSE:AddRequest(airbase, AssetDescriptor, AssetDescriptorValue, nA end end + -- General air request. request_air=request_helo or request_plane if request_air then @@ -938,133 +1181,72 @@ function WAREHOUSE:AddRequest(airbase, AssetDescriptor, AssetDescriptorValue, nA elseif request_naval then end + + -- Check if transport is possible. + if TransportType~=nil then + if TransportType==WAREHOUSE.TransportType.AIRPLANE then + + -- No airplanes from or to FARPS. + if self.category==Airbase.Category.HELIPAD or request_category==Airbase.Category.HELIPAD then + self:E("ERROR: incorrect request. Warehouse or requestor is FARP. No transport by plane possible!") + return + end + + elseif TransportType==WAREHOUSE.TransportType.APC then + + -- Transport by ground units. + + elseif TransportType==WAREHOUSE.TransportType.HELICOPTER then + + -- Transport by helicopters ==> need FARP or AIRBASE, well not really... + + elseif TransportType==WAREHOUSE.TransportType.SELFPROPELLED then + + + elseif TransportType==WAREHOUSE.TransportType.SHIP then + + -- Transport by ship. + self:E("ERROR: incorrect request. Transport by SHIP not implemented yet!") + return + + elseif TransportType==WAREHOUSE.TransportType.TRAIN then + + -- Transport by train. + self:E("ERROR: incorrect request. Transport by TRAIN not implemented yet!") + return + + else + -- No match. + self:E("ERROR: incorrect request. Transport type unknown!") + return + end + + end -- Increase id. self.queueid=self.queueid+1 -- Request queue table item. - local request={uid=self.queueid, prio=Prio, airbase=airbase, category=request_category, assetdesc=AssetDescriptor, assetdescval=AssetDescriptorValue, nasset=nAsset, transporttype=TransportType, ntransport=nTransport} + local request={ + uid=self.queueid, + prio=Prio, + warehouse=warehouse, + airbase=warehouse.airbase, + category=request_category, + assetdesc=AssetDescriptor, + assetdescval=AssetDescriptorValue, + nasset=nAsset, + transporttype=TransportType, + ntransport=nTransport} -- Add request to queue. table.insert(self.queue, request) end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Helper functions +-- Routing functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Checks if the request can be fullfilled. --- @param #WAREHOUSE self --- @param #WAREHOUSE.Queueitem qitem The request to be checked. --- @return #boolean If true, request can be executed. If false, something is not right. -function WAREHOUSE:_CheckRequest(request) - - local okay=true - - -- Check if number of requested assets is in stock. - local _assets=self:_FilterStock(self.stock, request.assetdesc, request.assetdescval, request.nasset) - - -- Get the attibute of the requested asset. - local _assetattribute=_assets[1].attribute - local _assetcategory=_assets[1].category - - -- Check if enough assets are in stock. - if request.nasset > #_assets then - local text=string.format("Request denied! Not enough assets currently available.") - MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:E(self.wid..text) - okay=false - end - - -- Check available parking for asset units. - local Parkingdata=self.homebase:GetParkingSpotsTable() - local Parking - if _assetcategory==Group.Category.AIRPLANE or _assetcategory==Group.Category.HELICOPTER then - Parking, Parkingdata=self:_GetParkingForAssets(_assets, Parkingdata) - if Parking==nil then - local text=string.format("Request denied! Not enough free parking spots for all assets at the moment.") - MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:E(self.wid..text) - okay=false - end - end - - - -- Check that a transport units. - if request.transporttype~=WAREHOUSE.TransportType.SELFPROPELLED then - - -- Transports in stock. - local _transports=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, request.transporttype, request.ntransport) - - -- Get the attibute of the transport units. - local _transportattribute=_transports[1].attribute - local _transportcategory=_transports[1].category - - -- Check if enough transport units are available. - if request.ntransport > #_transports then - local text=string.format("Request denied! Not enough transport units currently available.") - MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:E(self.wid..text) - okay=false - end - - -- Check available parking for transport units. - if _transportcategory==Group.Category.AIRPLANE or _transportcategory==Group.Category.HELICOPTER then - Parking, Parkingdata=self:_GetParkingForAssets(_transports, Parkingdata) - if Parking==nil then - local text=string.format("Request denied! Not enough free parking spots for all transports at the moment.") - MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:E(self.wid..text) - okay=false - end - end - - else - -- self propelled case. - - end - - return okay -end - - ---- Creates a unique name for spawned assets. --- @param #WAREHOUSE self --- @param #WAREHOUSE.Stockitem _assetitem Asset for which the name is created. --- @param #WAREHOUSE.Queueitem _queueitem (Optional) Request specific name. --- @return #string Alias name "UnitType\_WID-%02d\_AID-%04d" -function WAREHOUSE:_Alias(_assetitem,_queueitem) - local _alias=string.format("%s_WID-%02d_AID-%04d", _assetitem.unittype, self.uid,_assetitem.id) - if _queueitem then - _alias=_alias..string.format("_RID-%04d",_queueitem.uid) - end - return _alias -end - ----Sorts the queue and checks if the request can be fullfilled. --- @param #WAREHOUSE self --- @return #WAREHOUSE.Queueitem Chosen request. -function WAREHOUSE:_CheckQueue() - - -- Sort queue wrt to first prio and then qid. - self:_SortQueue() - - -- Search for a request we can execute. - local request=nil --#WAREHOUSE.Queueitem - for _,_qitem in ipairs(self.queue) do - local qitem=_qitem --#WAREHOUSE.Queueitem - local okay=self:_CheckRequest(qitem) - if okay==true then - request=qitem - break - end - end - - -- Execute request. - return request -end - --- Route ground units to destination. -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP Group The ground group. @@ -1077,36 +1259,28 @@ function WAREHOUSE:_RouteGround(Group, Coordinate, Speed) local _speed=Speed or Group:GetSpeedMax()*0.6 -- Create a - local Waypoints = Group:TaskGroundOnRoad(Coordinate, _speed, "Off Road", true) + local Waypoints, canroad = Group:TaskGroundOnRoad(Coordinate, _speed, "Off Road", true) -- Task function triggering the arrived event. local TaskFunction = Group:TaskFunction("WAREHOUSE._Arrived", self) -- Put task function on last waypoint. local Waypoint = Waypoints[#Waypoints] - Group:SetTaskWaypoint( Waypoint, TaskFunction ) + Group:SetTaskWaypoint(Waypoint, TaskFunction) -- Route group to destination. Group:Route(Waypoints, 1) end end ---- Task function for last waypoint. Triggering the Delivered event. --- @param Wrapper.Group#GROUP Group The group that arrived. --- @param #WAREHOUSE self -function WAREHOUSE._Arrived(Group, self) - --Trigger delivered event. - self:__Delivered(1, Group) -end - --- Route the airplane from one airbase another. --- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane Airplane group to be routed. +-- @param #WAREHOUSE self +-- @param Wrapper.Group#GROUP Aircraft Airplane group to be routed. -- @param Wrapper.Airbase#AIRBASE ToAirbase Destination airbase. -- @param #number Speed Speed in km/h. Default is 80% of max possible speed the group can do. function WAREHOUSE:_RouteAir(Aircraft, ToAirbase, Speed) - if Aircraft and Aircraft:IsAlive() then + if Aircraft and Aircraft:IsAlive()~=nil then -- Set takeoff type. local Takeoff = SPAWN.Takeoff.Cold @@ -1116,6 +1290,7 @@ function WAREHOUSE:_RouteAir(Aircraft, ToAirbase, Speed) -- Nil check if Template==nil then + self:E("ERROR: Template nil") return end @@ -1146,20 +1321,30 @@ function WAREHOUSE:_RouteAir(Aircraft, ToAirbase, Speed) ToWaypoint.helipadId = nil ToWaypoint.linkUnit = nil end - - -- Task function triggering the arrived event. - local Task = Aircraft:TaskFunction("WAREHOUSE._Arrived", self) - - --or - ToWaypoint.task=Aircraft:TaskCombo({Task}) - --ToWaypoint.task={Task} - + + --[[ + -- tasks + local TaskCombo = {} + TaskCombo[#TaskCombo+1]=self:_SimpleTaskFunction("WAREHOUSE._Arrived2") + + ToWaypoint.task = {} + ToWaypoint.task.id = "ComboTask" + ToWaypoint.task.params = {} + ToWaypoint.task.params.tasks = TaskCombo + ]] + -- Second point of the route. First point is done in RespawnAtCurrentAirbase() routine. Template.route.points[2] = ToWaypoint - - -- Respawn group at the current airbase. - Aircraft:RespawnAtCurrentAirbase(Template, Takeoff, false) - + + -- Respawn group at the current airbase. + env.info("FF Respawn at current airbase group = "..Aircraft:GetName().." name before") + local bla=Aircraft:RespawnAtCurrentAirbase(Template, Takeoff, false) + env.info("FF Respawn at current airbase group = "..bla:GetName().." name after") + + -- Handle event engine shutdown and trigger delivered event. + bla:HandleEvent(EVENTS.EngineShutdown, self._ArrivedEvent) + else + self:E(string.format("ERROR: aircraft %s cannot be routed since it does not exist or is not alive %s!", tostring(Aircraft:GetName()), tostring(Aircraft:IsAlive()))) end end @@ -1189,9 +1374,188 @@ function WAREHOUSE:_RouteTrain(Group, Coordinate, Speed) end end +--- Task function for last waypoint. Triggering the Delivered event. +-- @param Wrapper.Group#GROUP group The group that arrived. +-- @param #WAREHOUSE self +function WAREHOUSE._Arrived(group, warehouse) + env.info(warehouse.wid..string.format("Group %s arrived", tostring(group:GetName()))) + --Trigger delivered event. + warehouse:__Arrived(1, group) +end + +--- Task function for last waypoint. Triggering the Delivered event. +-- @param #WAREHOUSE self +-- @param Wrapper.Group#GROUP group The group that arrived. +function WAREHOUSE:_Arrived2(group) + env.info(self.wid..string.format("Group %s arrived2", tostring(group:GetName()))) + --Trigger delivered event. + self:__Arrived(1, group) +end + +--- Arrived event if an air unit/group arrived at its destination. +-- @param #WAREHOUSE self +-- @param Core.Event#EVENTDATA EventData Event data table. +function WAREHOUSE:_ArrivedEvent(EventData) + + local unit=EventData.IniUnit + unit:SmokeBlue() + + local group=EventData.IniGroup + self:__Arrived(1, group) + +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Helper functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Checks if the request can be fullfilled. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE.Queueitem qitem The request to be checked. +-- @return #boolean If true, request can be executed. If false, something is not right. +function WAREHOUSE:_CheckRequest(request) + + local okay=true + + -- Check if number of requested assets is in stock. + local _assets=self:_FilterStock(self.stock, request.assetdesc, request.assetdescval, request.nasset) + + -- Nothing in stock. + if #_assets==0 then + local text=string.format("Request denied! No assets for this request currently available.") + MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:E(self.wid..text) + return false + end + + -- Get the attibute of the requested asset. + local _assetattribute=_assets[1].attribute + local _assetcategory=_assets[1].category + + -- Check if enough assets are in stock. + if request.nasset > #_assets then + local text=string.format("Request denied! Not enough assets currently available.") + MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:E(self.wid..text) + okay=false + end + + -- Check available parking for asset units. + local Parkingdata + local Parking + if self.airbase and (_assetcategory==Group.Category.AIRPLANE or _assetcategory==Group.Category.HELICOPTER) then + Parkingdata=self.airbase:GetParkingSpotsTable() + Parking, Parkingdata=self:_GetParkingForAssets(_assets, Parkingdata) + if Parking==nil then + local text=string.format("Request denied! Not enough free parking spots for all assets at the moment.") + MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:E(self.wid..text) + okay=false + end + end + + + -- Check that a transport units. + if request.transporttype~=WAREHOUSE.TransportType.SELFPROPELLED then + + -- Transports in stock. + local _transports=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, request.transporttype, request.ntransport) + + -- Get the attibute of the transport units. + local _transportattribute=_transports[1].attribute + local _transportcategory=_transports[1].category + + -- Check if enough transport units are available. + if request.ntransport > #_transports then + local text=string.format("Request denied! Not enough transport units currently available.") + MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:E(self.wid..text) + okay=false + end + + -- Check available parking for transport units. + if self.airbase and (_transportcategory==Group.Category.AIRPLANE or _transportcategory==Group.Category.HELICOPTER) then + Parking, Parkingdata=self:_GetParkingForAssets(_transports, Parkingdata) + if Parking==nil then + local text=string.format("Request denied! Not enough free parking spots for all transports at the moment.") + MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:E(self.wid..text) + okay=false + end + end + + else + -- self propelled case. + + end + + return okay +end + + +--- Creates a unique name for spawned assets. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE.Stockitem _assetitem Asset for which the name is created. +-- @param #WAREHOUSE.Queueitem _queueitem (Optional) Request specific name. +-- @return #string Alias name "UnitType\_WID-%02d\_AID-%04d" +function WAREHOUSE:_Alias(_assetitem,_queueitem) + local _alias=string.format("%s_WID-%02d_AID-%04d", _assetitem.unittype, self.uid,_assetitem.uid) + if _queueitem then + _alias=_alias..string.format("_RID-%04d",_queueitem.uid) + end + return _alias +end + +---Sorts the queue and checks if the request can be fullfilled. +-- @param #WAREHOUSE self +-- @return #WAREHOUSE.Queueitem Chosen request. +function WAREHOUSE:_CheckQueue() + + -- Sort queue wrt to first prio and then qid. + self:_SortQueue() + + -- Search for a request we can execute. + local request=nil --#WAREHOUSE.Queueitem + for _,_qitem in ipairs(self.queue) do + local qitem=_qitem --#WAREHOUSE.Queueitem + local okay=self:_CheckRequest(qitem) + if okay==true then + request=qitem + break + end + end + + -- Execute request. + return request +end + +--- Simple task function. Can be used to call a function which has the warehouse and the executing group as parameters. +-- @param #WAREHOUSE self +-- @param #string Function The name of the function to call passed as string. +function WAREHOUSE:_SimpleTaskFunction(Function) + self:F2({Function}) + + -- Name of the warehouse (static) object. + local warehouse=self.warehouse:GetName() + + -- Task script. + local DCSScript = {} + DCSScript[#DCSScript+1] = string.format('env.info("FF hello simple task function") ') + DCSScript[#DCSScript+1] = string.format('local mygroup = GROUP:Find( ... ) ') -- The group that executes the task function. Very handy with the "...". + DCSScript[#DCSScript+1] = string.format('local mystatic=STATIC:FindByName(%s) ', warehouse) -- The static that holds the warehouse self object. + DCSScript[#DCSScript+1] = string.format('local warehouse = mygroup:GetState(mystatic, "WAREHOUSE") ') -- Get the warehouse self object from the static. + DCSScript[#DCSScript+1] = string.format('%s(warehouse, mygroup)', Function) -- Call the function, e.g. myfunction.(warehouse,mygroup) + + -- Create task. + local DCSTask = CONTROLLABLE.TaskWrappedAction(self, CONTROLLABLE.CommandDoScript(self, table.concat(DCSScript))) + + return DCSTask +end + --- Get the proper terminal type based on generalized attribute of the group. --@param #WAREHOUSE self --@param #WAREHOUSE.Attribute _attribute Generlized attibute of unit. +--@return Wrapper.Airbase#AIRBASE.TerminalType Terminal type for this group. function WAREHOUSE:_GetTerminal(_attribute) local _terminal=AIRBASE.TerminalType.OpenBig @@ -1206,6 +1570,7 @@ function WAREHOUSE:_GetTerminal(_attribute) _terminal=AIRBASE.TerminalType.HelicopterUsable end + return _terminal end --- Get parking data for all air assets that need to be spawned at an airbase. @@ -1221,7 +1586,7 @@ function WAREHOUSE:_GetParkingForAssets(assetlist, parkingdata) for j=1,#spots do for i=1,#parkingdata do if parkingdata[i].TerminalID==spots[j].TerminalID then - table.remove(parkingdata,j) + table.remove(parkingdata, i) break end end @@ -1229,7 +1594,7 @@ function WAREHOUSE:_GetParkingForAssets(assetlist, parkingdata) end -- Get complete parking data of the airbase. - parkingdata=parkingdata or self.homebase:GetParkingSpotsTable() + parkingdata=parkingdata or self.airbase:GetParkingSpotsTable() local assetparking={} for i=1,#assetlist do @@ -1240,8 +1605,22 @@ function WAREHOUSE:_GetParkingForAssets(assetlist, parkingdata) local nunits=#group:GetUnits() local terminal=self:_GetTerminal(asset.attribute) + --[[ + env.info("asset name = "..tostring(asset.templatename)) + env.info("asset attribute = "..tostring(asset.attribute)) + env.info("terminal type = "..tostring(terminal)) + env.info("parking spots = "..tostring(#parkingdata)) + ]] + -- Find appropiate parking spots for this group. - local spots=self.homebase:FindFreeParkingSpotForAircraft(group, terminal, nil, nil, nil, nil, nil, nil, parkingdata) + local spots=self.airbase:FindFreeParkingSpotForAircraft(group, terminal, nil, nil, nil, nil, nil, nil, parkingdata) + + for _,spot in pairs(spots) do + if spot then + local coord=spot.Coordinate --Core.Point#COORDINATE + coord:MarkToAll("Parking spot for "..asset.templatename.." id "..asset.uid.." terminal id "..spot.TerminalID) + end + end -- Not enough parking spots for this group. if #spots LengthDirect*10) or (LengthRoad/LengthOnRoad*100<5)) + + -- Debug info. + self:T(string.format("Length on road = %.3f km", LengthOnRoad/1000)) + self:T(string.format("Length directly = %.3f km", LengthDirect/1000)) + self:T(string.format("Length fraction = %.3f km", LengthOnRoad/LengthDirect)) + self:T(string.format("Length only road = %.3f km", LengthRoad/1000)) + self:T(string.format("Length off road = %.3f km", LengthOffRoad/1000)) + self:T(string.format("Percent on road = %.1f", LengthRoad/LengthOnRoad*100)) + + end + -- Route, ground waypoints along road. local route={} - - -- Length on road is 10 times longer than direct route or path on road is very short (<5% of total path). - local LongRoad=LengthOnRoad and ((LengthOnRoad > LengthDirect*10) or (LengthRoad/LengthOnRoad*100<5)) - + local canroad=false + -- Check if a valid path on road could be found. if PathOnRoad then - -- Check whether the road is very long compared to direct path. if LongRoad and Shortcut then @@ -2088,6 +2097,7 @@ do -- Route methods end + canroad=true else -- No path on road could be found (can happen!) ==> Route group directly from A to B. @@ -2096,7 +2106,7 @@ do -- Route methods end - return route + return route, canroad end --- Make a task for a TRAIN Controllable to drive towards a specific point using railroad. diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 1cf57eb77..f78f9ef04 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -413,6 +413,38 @@ function GROUP:GetSpeedMax() return nil end +--- Returns the maximum range of the group. +-- If the group is heterogenious and consists of different units, the smallest range of all units is returned. +-- @param #GROUP self +-- @return #number Range in meters. +function GROUP:GetRange() + self:F2( self.GroupName ) + + local DCSGroup = self:GetDCSObject() + if DCSGroup then + + local Units=self:GetUnits() + + local Rangemin=nil + + for _,unit in pairs(Units) do + local unit=unit --Wrapper.Unit#UNIT + local range=unit:GetRange() + if range then + if Rangemin==nil then + Rangemin=range + elseif range Date: Mon, 13 Aug 2018 07:38:18 +0200 Subject: [PATCH 250/420] Got the airplanes finally to load together ... --- .../Moose/AI/AI_Cargo_Airplane.lua | 83 ++++++++++--------- .../Moose/AI/AI_Cargo_Dispatcher.lua | 2 +- Moose Development/Moose/Cargo/Cargo.lua | 22 ++++- Moose Development/Moose/Cargo/CargoGroup.lua | 19 +---- Moose Development/Moose/Cargo/CargoUnit.lua | 6 +- .../Moose/Wrapper/Positionable.lua | 36 ++++---- 6 files changed, 86 insertions(+), 82 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 28a71989f..75f3f2873 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -37,7 +37,7 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) self:AddTransition( { "Unloaded", "Loaded" }, "Pickup", "*" ) self:AddTransition( "Loaded", "Deploy", "*" ) - self:AddTransition( { "Unloaded", "Loaded" }, "Load", "Boarding" ) + self:AddTransition( { "Unloaded", "Boarding" }, "Load", "Boarding" ) self:AddTransition( "Boarding", "Board", "Boarding" ) self:AddTransition( "Boarding", "Loaded", "Loaded" ) self:AddTransition( "Loaded", "Unload", "Unboarding" ) @@ -128,8 +128,13 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) -- Set carrier. self:SetCarrier( Airplane ) - Airplane:SetCargoBayWeightLimit( 5000 ) - Airplane:SetCargoBayVolumeLimit( 15 ) + local Desc = Airplane:GetUnit(1):GetDesc() + + self:F({Desc=Desc}) + + + Airplane:SetCargoBayWeightLimit( Desc.massMax - ( Desc.massEmpty + Desc.fuelMassMax ) ) + --Airplane:SetCargoBayVolumeLimit( 15 ) self.Relocating = true @@ -344,8 +349,7 @@ function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Airbase, Sp -- Set destination airbase for next :Route() command. self.Airbase = Airbase - -- Unclear?! - self.Transporting = false + self.Transporting = true self.Relocating = false end @@ -398,6 +402,38 @@ function AI_CARGO_AIRPLANE:onafterBoard( Airplane, From, Event, To, Cargo ) if not Cargo:IsLoaded() then self:__Board( 10, Cargo ) else + -- Check if another cargo can be loaded into the airplane. + for _,_Cargo in pairs( self.CargoSet:GetSet() ) do + + self:F({_Cargo:GetName()}) + local Cargo =_Cargo --Cargo.Cargo#CARGO + + -- Is there a cargo still unloaded? + if Cargo:IsUnLoaded() == true then + + -- Only when the cargo is within load radius. + local InRadius = Cargo:IsInLoadRadius( Airplane:GetCoordinate() ) + if InRadius then + + local CargoBayFreeWeight = Airplane:GetCargoBayFreeWeight() + --local CargoBayFreeVolume = Airplane:GetCargoBayFreeVolume() + + local CargoWeight = Cargo:GetWeight() + --local CargoVolume = Cargo:GetVolume() + + -- Only when there is space within the bay to load the next cargo item! + if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then + + -- ok, board. + self:__Load( 5, Airplane:GetCoordinate() ) + + -- And start the boarding loop for the AI_CARGO_AIRPLANE object until the cargo is boarded. + --Cargo:Board( Airplane, 25 ) + return + end + end + end + end self:__Loaded( 1, Cargo ) end end @@ -415,41 +451,8 @@ function AI_CARGO_AIRPLANE:onafterLoaded( Airplane, From, Event, To, Cargo ) env.info("FF troops loaded into cargo plane") if Airplane and Airplane:IsAlive() then - - -- Check if another cargo can be loaded into the airplane. - for _,_Cargo in pairs( self.CargoSet:GetSet() ) do - - self:F({_Cargo:GetName()}) - local Cargo =_Cargo --Cargo.Cargo#CARGO - - -- Is there a cargo still unloaded? - if Cargo:IsUnLoaded() == true then - - -- Only when the cargo is within load radius. - local InRadius = Cargo:IsInLoadRadius( Airplane:GetCoordinate() ) - if InRadius then - - local CargoBayFreeWeight = Airplane:GetCargoBayFreeWeight() - local CargoBayFreeVolume = Airplane:GetCargoBayFreeVolume() - - local CargoWeight = Cargo:GetWeight() - local CargoVolume = Cargo:GetVolume() - - -- Only when there is space within the bay to load the next cargo item! - if CargoBayFreeWeight > CargoWeight and CargoBayFreeVolume > CargoVolume then - - -- ok, board. - self:__Load( 5, Airplane:GetCoordinate() ) - - -- And start the boarding loop for the AI_CARGO_AIRPLANE object until the cargo is boarded. - --Cargo:Board( Airplane, 25 ) - return - end - end - end - end - self:F( { "Transporting" } ) - self.Transporting = true -- This will only be executed when there is no cargo boarded anymore. The dispatcher will then kick-off the deploy cycle! + self:F( { "Transporting" } ) + self.Transporting = true -- This will only be executed when there is no cargo boarded anymore. The dispatcher will then kick-off the deploy cycle! end end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 9a5082f52..c7a0ea4b9 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -405,7 +405,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() -- Check if this Carrier need to go and Pickup something... -- So, if the cargo bay is not full yet with cargo to be loaded ... self:I( { IsRelocating = AI_Cargo:IsRelocating() } ) - if AI_Cargo:IsRelocating() == false then + if AI_Cargo:IsRelocating() == false and AI_Cargo:IsTransporting() == false then -- ok, so there is a free Carrier -- now find the first cargo that is Unloaded diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index e0b6f4d4a..50cade298 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -1020,13 +1020,27 @@ do -- CARGO_REPRESENTABLE -- @param #CARGO_REPRESENTABLE self -- @param #string Type -- @param #string Name - -- @param #number Weight -- @param #number LoadRadius (optional) -- @param #number NearRadius (optional) -- @return #CARGO_REPRESENTABLE - function CARGO_REPRESENTABLE:New( CargoObject, Type, Name, Weight, LoadRadius, NearRadius ) - local self = BASE:Inherit( self, CARGO:New( Type, Name, Weight, LoadRadius, NearRadius ) ) -- #CARGO_REPRESENTABLE - self:F( { Type, Name, Weight, LoadRadius, NearRadius } ) + function CARGO_REPRESENTABLE:New( CargoObject, Type, Name, LoadRadius, NearRadius ) + local self = BASE:Inherit( self, CARGO:New( Type, Name, 0, LoadRadius, NearRadius ) ) -- #CARGO_REPRESENTABLE + self:F( { Type, Name, LoadRadius, NearRadius } ) + + local Desc = CargoObject:GetDesc() + self:I( { Desc = Desc } ) + local Weight = math.random( 80, 120 ) + if Desc then + Weight = Desc.massEmpty + end + + self:SetWeight( Weight ) + +-- local Box = CargoUnit:GetBoundingBox() +-- local VolumeUnit = ( Box.max.x - Box.min.x ) * ( Box.max.y - Box.min.y ) * ( Box.max.z - Box.min.z ) +-- self:I( { VolumeUnit = VolumeUnit, WeightUnit = WeightUnit } ) + --self:SetVolume( VolumeUnit ) + return self end diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index b7c78a713..72a3430cf 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -107,32 +107,19 @@ do -- CARGO_GROUP self.CargoObject = _DATABASE:Spawn( GroupTemplate ) for CargoUnitID, CargoUnit in pairs( self.CargoObject:GetUnits() ) do - local Desc = CargoUnit:GetDesc() - self:I( { Desc = Desc } ) - local WeightUnit = math.random( 80, 120 ) - if Desc then - WeightUnit = Desc.massEmpty - end - - local Box = CargoUnit:GetBoundingBox() - local VolumeUnit = ( Box.max.x - Box.min.x ) * ( Box.max.y - Box.min.y ) * ( Box.max.z - Box.min.z ) - self:I( { VolumeUnit = VolumeUnit, WeightUnit = WeightUnit } ) local CargoUnitName = CargoUnit:GetName() - local Cargo = CARGO_UNIT:New( CargoUnit, Type, CargoUnitName, 0, LoadRadius, NearRadius ) - Cargo:SetWeight( WeightUnit ) - Cargo:SetVolume( VolumeUnit ) + local Cargo = CARGO_UNIT:New( CargoUnit, Type, CargoUnitName, LoadRadius, NearRadius ) self.CargoSet:Add( CargoUnitName, Cargo ) - WeightGroup = WeightGroup + WeightUnit - VolumeGroup = VolumeGroup + VolumeUnit + WeightGroup = WeightGroup + Cargo:GetWeight() + --VolumeGroup = VolumeGroup + VolumeUnit end self:SetWeight( WeightGroup ) - self.CargoLimit = 10 self:T( { "Weight Cargo", WeightGroup } ) diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index c7992256c..90eceeda9 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -42,9 +42,9 @@ do -- CARGO_UNIT -- @param #number LoadRadius (optional) -- @param #number NearRadius (optional) -- @return #CARGO_UNIT - function CARGO_UNIT:New( CargoUnit, Type, Name, Weight, LoadRadius, NearRadius ) - local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, Weight, LoadRadius, NearRadius ) ) -- #CARGO_UNIT - self:I( { Type, Name, Weight, LoadRadius, NearRadius } ) + function CARGO_UNIT:New( CargoUnit, Type, Name, LoadRadius, NearRadius ) + local self = BASE:Inherit( self, CARGO_REPRESENTABLE:New( CargoUnit, Type, Name, LoadRadius, NearRadius ) ) -- #CARGO_UNIT + self:I( { Type, Name, LoadRadius, NearRadius } ) self:T( CargoUnit ) self.CargoObject = CargoUnit diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 44fa06d52..1de5c21fe 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -851,17 +851,17 @@ do -- Cargo return ItemCount end - --- Get Cargo Bay Free Volume in m3. - -- @param #POSITIONABLE self - -- @return #number CargoBayFreeVolume - function POSITIONABLE:GetCargoBayFreeVolume() - local CargoVolume = 0 - for CargoName, Cargo in pairs( self.__.Cargo ) do - CargoVolume = CargoVolume + Cargo:GetVolume() - end - return self.__.CargoBayVolumeLimit - CargoVolume - end - +-- --- Get Cargo Bay Free Volume in m3. +-- -- @param #POSITIONABLE self +-- -- @return #number CargoBayFreeVolume +-- function POSITIONABLE:GetCargoBayFreeVolume() +-- local CargoVolume = 0 +-- for CargoName, Cargo in pairs( self.__.Cargo ) do +-- CargoVolume = CargoVolume + Cargo:GetVolume() +-- end +-- return self.__.CargoBayVolumeLimit - CargoVolume +-- end +-- --- Get Cargo Bay Free Weight in kg. -- @param #POSITIONABLE self -- @return #number CargoBayFreeWeight @@ -874,13 +874,13 @@ do -- Cargo return self.__.CargoBayWeightLimit - CargoWeight end - --- Get Cargo Bay Volume Limit in m3. - -- @param #POSITIONABLE self - -- @param #number VolumeLimit - function POSITIONABLE:SetCargoBayVolumeLimit( VolumeLimit ) - self.__.CargoBayVolumeLimit = VolumeLimit - end - +-- --- Get Cargo Bay Volume Limit in m3. +-- -- @param #POSITIONABLE self +-- -- @param #number VolumeLimit +-- function POSITIONABLE:SetCargoBayVolumeLimit( VolumeLimit ) +-- self.__.CargoBayVolumeLimit = VolumeLimit +-- end + --- Get Cargo Bay Weight Limit in kg. -- @param #POSITIONABLE self -- @param #number WeightLimit From ab5b74cf316a186223edb73f6141e3b4c4b549f2 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 13 Aug 2018 16:25:51 +0200 Subject: [PATCH 251/420] Warehouse v0.1.5 --- .../Moose/Functional/Warehouse.lua | 1055 +++++++++-------- 1 file changed, 563 insertions(+), 492 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 00c3714c0..c2d3cde99 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -29,7 +29,7 @@ -- @field DCS#Airbase.Category category Category of the home airbase, i.e. airdrome, helipad/farp or ship. -- @field Core.Point#COORDINATE coordinate Coordinate of the warehouse. -- @field Core.Point#COORDINATE road Closest point to warehouse on road. --- @field Core.Point#COORDINATE road Closest point to warehouse on rail. +-- @field Core.Point#COORDINATE rail Closest point to warehouse on rail. -- @field Core.Zone#ZONE spawnzone Zone in which assets are spawned. -- @field #string wid Identifier of the warehouse printed before other output to DCS.log file. -- @field #number uid Unit identifier of the warehouse. Derived from the associated airbase. @@ -59,13 +59,19 @@ -- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_Main.JPG) -- -- # What is a warehouse? --- A warehouse is an abstract object that can hold virtual assets in stock. It is usually associated with a particular airbase. --- If another airbase or warehouse requests assets, the corresponding troops are spawned at the warehouse and being transported to the requestor. +-- A warehouse is an abstract object represented by a physical (static) building that can hold virtual assets in stock. +-- It can but it must not be associated with a particular airbase. The associated airbase can be an airdrome, a Helipad/FARP or a ship. +-- +-- If another another warehouse requests assets, the corresponding troops are spawned at the warehouse and being transported to the requestor or go their +-- by themselfs. Once arrived at the requesting warehouse, the assets go into the stock of the requestor and can be reactivated when necessary. -- -- ## What assets can be stored? -- Any kind of ground or airborn asset can be stored. Ships not supported at the moment due to the fact that airbases are bound to airbases which are -- normally not located near the sea. +-- +-- # Adding Assets -- +-- # Requests -- -- -- === @@ -83,7 +89,7 @@ WAREHOUSE = { coalition = nil, country = nil, airbase = nil, - category = nil, + category = -1, coordinate = nil, road = nil, rail = nil, @@ -171,10 +177,10 @@ WAREHOUSE.TransportType = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.1.4" +WAREHOUSE.version="0.1.5" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Warehuse todo list. +-- TODO: Warehouse todo list. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Add event handlers. @@ -197,10 +203,13 @@ WAREHOUSE.version="0.1.4" --- WAREHOUSE constructor. Creates a new WAREHOUSE object accociated with an airbase. -- @param #WAREHOUSE self -- @param Wrapper.Static#STATIC warehouse The physical structure of the warehouse. +-- @param Core.Zone#ZONE spawnzone (Optional) The zone in which units are spawned and despawned when they leave or arrive the warehouse. Default is a zone of 200 meters around the warehouse. +-- @param Wrapper.Airbase#AIRBASE airbase (Optional) The airbase belonging to the warehouse. Default is the closest airbase to the warehouse structure as long as it within a range of 3 km. -- @return #WAREHOUSE self -function WAREHOUSE:New(warehouse) +function WAREHOUSE:New(warehouse, spawnzone, airbase) BASE:E({warehouse=warehouse:GetName()}) + -- Nil check. if warehouse==nil then BASE:E("ERROR: Warehouse does not exist!") return nil @@ -222,29 +231,35 @@ function WAREHOUSE:New(warehouse) self.country=warehouse:GetCountry() self.coordinate=warehouse:GetCoordinate() - --TODO: Add max range parameter to check if airbase is close enough. Range should be? ~3 km maybe? - local _airbase=self.coordinate:GetClosestAirbase(nil, self.coalition) - - if _airbase and _airbase:GetCoordinate():Get2DDistance(self.coordinate) < 2500 then - self.airbase=_airbase - self.category=self.airbase:GetDesc().category + -- Set airbase. + if airbase then + -- User requested. + self.airbase=airbase + else + -- Closest of the same coalition but within a certain range. + local _airbase=self.coordinate:GetClosestAirbase(nil, self.coalition) + if _airbase and _airbase:GetCoordinate():Get2DDistance(self.coordinate) < 3000 then + self.airbase=_airbase + self.category=self.airbase:GetDesc().category + end end - - + -- Get the closest point on road and rail. local _road=self.coordinate:GetClosestPointToRoad() local _rail=self.coordinate:GetClosestPointToRoad(true) - - if _road and _road:Get2DDistance(self.coordinate) < 2500 then - self.road=_road - end - if _rail and _rail:Get2DDistance(self.coordinate) < 2500 then - self.rail=_rail - end + -- Set connections. + if _road and _road:Get2DDistance(self.coordinate) < 3000 then + self.road=_road + _road:MarkToAll(string.format("%s road connection.", self.warehouse:GetName()), true) + end + if _rail and _rail:Get2DDistance(self.coordinate) < 3000 then + self.rail=_rail + _rail:MarkToAll(string.format("%s rail connection.", self.warehouse:GetName()), true) + end -- Define the default spawn zone. - self.spawnzone=ZONE_RADIUS:New("Spawnzone", warehouse:GetVec2(), 200) + self.spawnzone=ZONE_RADIUS:New(string.format("Spawnzone %s",warehouse:GetName()), warehouse:GetVec2(), 200) -- Add FSM transitions. self:AddTransition("*", "Start", "Running") @@ -311,6 +326,137 @@ function WAREHOUSE:New(warehouse) return self end +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- User functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Add an airplane group to the warehouse stock. +-- @param #WAREHOUSE self +-- @param #string templategroupname Name of the late activated template group as defined in the mission editor. +-- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1. +-- @return #WAREHOUSE self +function WAREHOUSE:AddAsset(templategroupname, ngroups) + + -- Set default. + local n=ngroups or 1 + + local group=GROUP:FindByName(templategroupname) + + if group then + + local DCSgroup=group:GetDCSObject() + local DCSunit=DCSgroup:getUnit(1) + local DCSdesc=DCSunit:getDesc() + local DCSdisplay=DCSdesc.displayName + local DCScategory=DCSgroup:getCategory() + local DCStype=DCSunit:getTypeName() + local SpeedMax=group:GetSpeedMax() + local RangeMin=group:GetRange() + + env.info(string.format("group name = %s", group:GetName())) + env.info(string.format("display name = %s", DCSdisplay)) + env.info(string.format("category = %s", DCScategory)) + env.info(string.format("type = %s", DCStype)) + env.info(string.format("speed max = %s", tostring(SpeedMax))) + env.info(string.format("range min = %s", tostring(RangeMin))) + self:E({fullassetdesc=DCSdesc}) + + local attribute=self:_GetAttribute(templategroupname) + + -- Add this n times to the table. + for i=1,n do + local stockitem={} --#WAREHOUSE.Stockitem + + self.assetid=self.assetid+1 + + stockitem.uid=self.assetid + stockitem.templatename=templategroupname + stockitem.template=UTILS.DeepCopy(_DATABASE.Templates.Groups[templategroupname].Template) + stockitem.category=DCScategory + stockitem.unittype=DCStype + stockitem.attribute=attribute + stockitem.range=RangeMin + stockitem.speedmax=SpeedMax + + -- Modify the template so that the group is spawned with the right coalition. + stockitem.template.CoalitionID=self.coalition + stockitem.template.CountryID=self.country + + table.insert(self.stock, stockitem) + end + + -- Destroy group if it is alive. + if group:IsAlive()==true then + group:Destroy() + end + + else + -- Group name does not exist! + self:E(string.format("ERROR: Template group name not defined in the mission editor. Check the spelling! templategroupname=%s",tostring(templategroupname))) + end + + return self +end + +--- Add a request for the warehouse. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE warehouse The warehouse requesting supply. +-- @param #WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. +-- @param AssetDescriptorValue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. +-- @param #number nAsset Number of groups requested that match the asset specification. +-- @param #WAREHOUSE.TransportType TransportType Type of transport. +-- @param #number nTransport Number of transport units requested. +-- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. +function WAREHOUSE:AddRequest(warehouse, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType, nTransport, Prio) + + nAsset=nAsset or 1 + TransportType=TransportType or WAREHOUSE.TransportType.SELFPROPELLED + nTransport=nTransport or 1 + Prio=Prio or 50 + + -- Increase id. + self.queueid=self.queueid+1 + + -- Request queue table item. + local request={ + uid=self.queueid, + prio=Prio, + warehouse=warehouse, + airbase=warehouse.airbase, + category=warehouse.category, + assetdesc=AssetDescriptor, + assetdescval=AssetDescriptorValue, + nasset=nAsset, + transporttype=TransportType, + ntransport=nTransport} + + -- Add request to queue. + table.insert(self.queue, request) +end + +--- Add a delayed request for the warehouse. +-- @param #WAREHOUSE self +-- @param #number delay Delay before the request is made in seconds. +-- @param #WAREHOUSE warehouse The warehouse requesting supply. +-- @param #WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. +-- @param AssetDescriptorValue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. +-- @param #number nAsset Number of groups requested that match the asset specification. +-- @param #WAREHOUSE.TransportType TransportType Type of transport. +-- @param #number nTransport Number of transport units requested. +-- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. +function WAREHOUSE:__AddRequest(delay, warehouse, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType, nTransport, Prio) + SCHEDULER:New(nil,WAREHOUSE.AddRequest,{self, warehouse, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType, nTransport, Prio}, delay) +end + +--- Set a zone where the (ground) assets of the warehouse are spawned once requested. +-- @param #WAREHOUSE self +-- @param Core.Zone#ZONE zone The spawn zone. +-- @return #WAREHOUSE self +function WAREHOUSE:SetSpawnZone(zone) + self.spawnzone=zone + return self +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- FSM states ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -342,6 +488,11 @@ function WAREHOUSE:onafterStart(From, Event, To) -- event crash/dead -- event base captured ==> change coalition ==> add assets to other coalition + -- Handle event that airbase was captured. + if self.airbase then + --self.airbase:HandleEvent(EVENTS.BaseCaptured, self._BaseCaptured) + end + self:__Status(5) end @@ -404,97 +555,6 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Spawns requested asset at warehouse or associated airbase. --- @param #WAREHOUSE self --- @param #WAREHOUSE.Queueitem Request Information table of the request. --- @return Core.Set#SET_GROUP Set of groups that were spawned. --- @return #WAREHOUSE.Attribute Generalized attribute of asset. --- @return DCS#Group.Category Category of asset, i.e. ground, air, ship, ... -function WAREHOUSE:_SpawnAssetRequest(Request) - - -- Filter the requested cargo assets. - local _assetstock=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval, Request.nasset) - - if #_assetstock==0 then - return nil,nil,nil - end - - -- General type and category. - local _cargotype=_assetstock[1].attribute --#WAREHOUSE.Attribute - local _cargocategory=_assetstock[1].category --DCS#Group.Category - - -- Now we try to find all parking spots for all cargo groups in advance. Due to the for loop, the parking spots do not get updated while spawning. - local Parking={} - if _cargocategory==Group.Category.AIRPLANE or _cargocategory==Group.Category.HELICOPTER then - Parking=self:_GetParkingForAssets(_assetstock) - end - - -- Create an empty set. - local groupset=SET_GROUP:New():FilterDeads() - - -- Spawn the assets. - local _delid={} - local _spawngroups={} - - -- Loop over cargo requests. - for i=1,Request.nasset do - - -- Get stock item. - local _assetitem=_assetstock[i] --#WAREHOUSE.Stockitem - - -- Find a random point within the spawn zone. - local spawncoord=self.spawnzone:GetRandomCoordinate() - - -- Alias of the group. - local _alias=self:_Alias(_assetitem,Request) - - -- Spawn object. Spawn with ALIAS here or DCS crashes! - --local _spawn=SPAWN:NewFromTemplate(_assetitem.template,_assetitem.templatename,_alias):InitCoalition(self.coalition):InitCountry(self.country) - local _spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias):InitCoalition(self.coalition):InitCountry(self.country) - - local _group=nil --Wrapper.Group#GROUP - local _attribute=_assetitem.attribute - - if _assetitem.category==Group.Category.GROUND then - - -- Spawn ground troops. - _group=_spawn:SpawnFromCoordinate(spawncoord) - - elseif _assetitem.category==Group.Category.AIRPLANE or _assetitem.category==Group.Category.HELICOPTER then - - --TODO: spawn only so many groups as there are parking spots. Adjust request and create a new one with the reduced number! - - -- Spawn air units. - _group=_spawn:SpawnAtAirbase(self.airbase, SPAWN.Takeoff.Cold, nil, nil, true, Parking[i]) - - elseif _assetitem.category==Group.Category.TRAIN then - - -- Spawn train. - local _railroad=self.coordinate:GetClosestPointToRoad(true) - if _railroad then - _group=_spawn:SpawnFromCoordinate(_railroad) - end - - end - - if _group then - --_spawngroups[i]=_group - groupset:AddGroup(_group) - table.insert(_delid,_assetitem.uid) - else - self:E(self.wid.."ERROR: cargo asset could not be spawned!") - end - - end - - -- Delete spawned items from warehouse stock. - for _,_id in pairs(_delid) do - self:_DeleteStockItem(_id) - end - - return groupset,_cargotype,_cargocategory -end - --- On before "Request" event. Checks if the request can be fullfilled. -- @param #WAREHOUSE self -- @param #string From From state. @@ -520,7 +580,7 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request) MESSAGE:New(text, 10):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:E(self.wid..text) --TODO Delete request from queue because it will never be possible! - return false + return false end end @@ -536,7 +596,7 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request) end ---- On after "Request" event. Initiates the transport of the assets to the requesting airbase. +--- On after "Request" event. Initiates the transport of the assets to the requesting warehouse. -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. @@ -565,7 +625,6 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) end end - ------------------------------------------------------------------------------------------------------------------------------------ -- Self propelled assets. ------------------------------------------------------------------------------------------------------------------------------------ @@ -573,19 +632,14 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- No transport unit requested. Assets go by themselfes. if Request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then env.info("FF selfpropelled") - --for _i,_spawngroup in pairs(_spawngroups) do + for _,_spawngroup in pairs(_spawngroups:GetSetObjects()) do local group=_spawngroup --Wrapper.Group#GROUP local ToCoordinate=Request.warehouse.spawnzone:GetRandomCoordinate(50) ToCoordinate:MarkToAll("Destination") - - local dist=Request.airbase:GetCoordinate():Get2DDistance(ToCoordinate) - - env.info("Dist to warehouse = "..dist) - env.info("FF group "..group:GetName()) - + -- Route cargo to their destination. if _cargocategory==Group.Category.GROUND then env.info("FF route ground "..group:GetName()) @@ -615,6 +669,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Add request to pending queue. table.insert(self.pending, Request) + -- Delete request from queue. self:_DeleteQueueItem(Request.uid) @@ -626,9 +681,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Transport assets and dispachers ------------------------------------------------------------------------------------------------------------------------------------ - env.info("FF cargo set name(s) = "..CargoGroups:GetObjectNames()) - ---------------------------------------------------------------- - + -- Set of cargo carriers. local TransportSet = SET_GROUP:New():FilterDeads() -- Pickup and deploy zones/bases. @@ -646,9 +699,6 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) local _transporttype=_assetstock[1].attribute --#WAREHOUSE.Attribute local _transportcategory=_assetstock[1].category --DCS#Group.Category - -- Now we try to find all parking spots for all transport groups in advance. Due to the for loop, the parking spots do not get updated while spawning. - local Parking={} - -- Now we try to find all parking spots for all cargo groups in advance. Due to the for loop, the parking spots do not get updated while spawning. local Parking={} if _transportcategory==Group.Category.AIRPLANE or _transportcategory==Group.Category.HELICOPTER then @@ -750,7 +800,6 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) local _assetitem=_assetstock[i] --#WAREHOUSE.Stockitem -- Spawn with ALIAS here or DCS crashes! - --local _alias=string.format("%s_%d", _assetitem.templatename,_assetitem.id) local _alias=self:_Alias(_assetitem, Request) -- Spawn plane at airport in uncontrolled state. @@ -782,12 +831,12 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) elseif Request.transporttype==WAREHOUSE.TransportType.TRAIN then - self:E(self.wid.."ERROR: transport by train not supported yet!") + self:E(self.wid.."ERROR: cargo transport by train not supported yet!") return elseif Request.transporttype==WAREHOUSE.TransportType.SHIP then - self:E(self.wid.."ERROR: transport by ship not supported yet!") + self:E(self.wid.."ERROR: cargo transport by ship not supported yet!") return elseif Request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then @@ -836,12 +885,106 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Start dispatcher. CargoTransport:__Start(5) - -- Delete request from queue. + -- Add request to pending queue. table.insert(self.pending, Request) + + -- Delete request from queue. self:_DeleteQueueItem(Request.uid) end + +--- Spawns requested asset at warehouse or associated airbase. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE.Queueitem Request Information table of the request. +-- @return Core.Set#SET_GROUP Set of groups that were spawned. +-- @return #WAREHOUSE.Attribute Generalized attribute of asset. +-- @return DCS#Group.Category Category of asset, i.e. ground, air, ship, ... +function WAREHOUSE:_SpawnAssetRequest(Request) + + -- Filter the requested cargo assets. + local _assetstock=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval, Request.nasset) + + if #_assetstock==0 then + return nil,nil,nil + end + + -- General type and category. + local _cargotype=_assetstock[1].attribute --#WAREHOUSE.Attribute + local _cargocategory=_assetstock[1].category --DCS#Group.Category + + -- Now we try to find all parking spots for all cargo groups in advance. Due to the for loop, the parking spots do not get updated while spawning. + local Parking={} + if _cargocategory==Group.Category.AIRPLANE or _cargocategory==Group.Category.HELICOPTER then + Parking=self:_GetParkingForAssets(_assetstock) + end + + -- Create an empty set. + local groupset=SET_GROUP:New():FilterDeads() + + -- Spawn the assets. + local _delid={} + local _spawngroups={} + + -- Loop over cargo requests. + for i=1,Request.nasset do + + -- Get stock item. + local _assetitem=_assetstock[i] --#WAREHOUSE.Stockitem + + -- Find a random point within the spawn zone. + local spawncoord=self.spawnzone:GetRandomCoordinate() + + -- Alias of the group. + local _alias=self:_Alias(_assetitem, Request) + + -- Spawn object. Spawn with ALIAS here or DCS crashes! + --local _spawn=SPAWN:NewFromTemplate(_assetitem.template,_assetitem.templatename,_alias):InitCoalition(self.coalition):InitCountry(self.country) + local _spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias):InitCoalition(self.coalition):InitCountry(self.country) + + local _group=nil --Wrapper.Group#GROUP + local _attribute=_assetitem.attribute + + if _assetitem.category==Group.Category.GROUND then + + -- Spawn ground troops. + _group=_spawn:SpawnFromCoordinate(spawncoord) + + elseif _assetitem.category==Group.Category.AIRPLANE or _assetitem.category==Group.Category.HELICOPTER then + + --TODO: spawn only so many groups as there are parking spots. Adjust request and create a new one with the reduced number! + + -- Spawn air units. + _group=_spawn:SpawnAtAirbase(self.airbase, SPAWN.Takeoff.Cold, nil, nil, true, Parking[i]) + + elseif _assetitem.category==Group.Category.TRAIN then + + -- Spawn train. + if self.rail then + --TODO: Rail should only get one asset because they would spawn on top! + _group=_spawn:SpawnFromCoordinate(self.rail) + end + + end + + if _group then + --_spawngroups[i]=_group + groupset:AddGroup(_group) + table.insert(_delid,_assetitem.uid) + else + self:E(self.wid.."ERROR: cargo asset could not be spawned!") + end + + end + + -- Delete spawned items from warehouse stock. + for _,_id in pairs(_delid) do + self:_DeleteStockItem(_id) + end + + return groupset,_cargotype,_cargocategory +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- On after "Arrived" event. Triggered when a group has arrived at its destination. @@ -858,16 +1001,6 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) -- Update pending request. self:_UpdatePending(group) - -- Update pending - - --[[ - -- Route a ground group to the closest point on road. - if group:IsGround() and group:GetSpeedMax()>0 then - local road=group:GetCoordinate():GetClosestPointToRoad() - local speed=group:GetSpeedMax()*0.6 - group:RouteGroundTo(road, speed, "Off Road") - end - ]] end --- Update the pending requests by removing assets that have arrived. @@ -876,77 +1009,21 @@ end function WAREHOUSE:_UpdatePending(group) -- Get request from group name. - local request=self:_GetRequestOfGroup(group) + local request=self:_GetRequestOfGroup(group, self.pending) -- Increase number of delivered assets. request.ndelivered=request.ndelivered+1 + -- Number of alive groups. + local nalive=request.cargogroupset:Count() + -- TODO: Well this does not handle the case when a group that has been delivered was killed! - if request.ndelivered>=request.cargogroupset:Count() then - self:__Delivered(5, request.cargogroupset) + if request.ndelivered>=nalive then + self:__Delivered(5, request.cargogroupset, request) end end ---- Get the request from the group. --- @param #WAREHOUSE self --- @param Wrapper.Group#GROUP group The group from which the info is gathered. --- @return #WAREHOUSE.Queueitem The request belonging to this group. -function WAREHOUSE:_GetRequestOfGroup(group) - local wid,aid,rid=self:_GetInfoFromGroup(group) - - for _,_request in pairs(self.pending) do - local request=_request --#WAREHOUSE.Queueitem - if request.uid==rid then - return request - end - end - -end - ---- Get warehouse id, asset id and request id from group name. --- @param #WAREHOUSE self --- @param Wrapper.Group#GROUP group The group from which the info is gathered. -function WAREHOUSE:_GetInfoFromGroup(group) - - ---@param #string text The text to analyse. - local function analyse(text) - - -- Get rid of #0001 tail from spawn. - local unspawned=UTILS.Split(text, "#")[1] - - -- Split keywords. - local keywords=UTILS.Split(unspawned, "_") - - local _wid=nil - local _aid=nil - local _rid=nil - - -- loop - for _,keys in pairs(keywords) do - local str=UTILS.Split(keys,"-") - local key=str[1] - local val=str[2] - if key:find("WID") then - _wid=tonumber(val) - elseif key:find("AID") then - _aid=tonumber(val) - elseif key:find("RID") then - _rid=tonumber(val) - end - end - - return _wid,_aid,_rid - end - - local name=group:GetName() - env.info("FF Name = "..tostring(name)) - local wid,aid,rid=analyse(name) - env.info("FF warehouse id = %s"..tostring(wid)) - env.info("FF asset id = %s"..tostring(aid)) - env.info("FF request id = %s"..tostring(rid)) - -end --- On after "Delivered" event. -- @param #WAREHOUSE self @@ -954,293 +1031,17 @@ end -- @param #string Event Event. -- @param #string To To state. -- @param Core.Set#SET_GROUP groupset The set of cargo groups that was delivered. -function WAREHOUSE:onafterDelivered(From, Event, To, groupset) +-- @param #WAREHOUSE.Queueitem request +function WAREHOUSE:onafterDelivered(From, Event, To, groupset, request) env.info("FF all assets delivered!") -end - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- User functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - ---- Add an airplane group to the warehouse stock. --- @param #WAREHOUSE self --- @param #string templategroupname Name of the late activated template group as defined in the mission editor. --- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1. --- @return #WAREHOUSE self -function WAREHOUSE:AddAsset(templategroupname, ngroups) - - -- Set default. - local n=ngroups or 1 - - local group=GROUP:FindByName(templategroupname) - - if group then - - local DCSgroup=group:GetDCSObject() - local DCSunit=DCSgroup:getUnit(1) - local DCSdesc=DCSunit:getDesc() - local DCSdisplay=DCSdesc.displayName - local DCScategory=DCSgroup:getCategory() - local DCStype=DCSunit:getTypeName() - local SpeedMax=group:GetSpeedMax() - local RangeMin=group:GetRange() - - env.info(string.format("group name = %s", group:GetName())) - env.info(string.format("display name = %s", DCSdisplay)) - env.info(string.format("category = %s", DCScategory)) - env.info(string.format("type = %s", DCStype)) - env.info(string.format("speed max = %s", tostring(SpeedMax))) - env.info(string.format("range min = %s", tostring(RangeMin))) - self:E({fullassetdesc=DCSdesc}) - - local attribute=self:_GetAttribute(templategroupname) - - -- Add this n times to the table. - for i=1,n do - local stockitem={} --#WAREHOUSE.Stockitem - - self.assetid=self.assetid+1 - - stockitem.uid=self.assetid - stockitem.templatename=templategroupname - stockitem.template=UTILS.DeepCopy(_DATABASE.Templates.Groups[templategroupname].Template) - stockitem.category=DCScategory - stockitem.unittype=DCStype - stockitem.attribute=attribute - stockitem.range=RangeMin - stockitem.speedmax=SpeedMax - - -- Modify the template so that the group is spawned with the right coalition. - stockitem.template.CoalitionID=self.coalition - stockitem.template.CountryID=self.country - - table.insert(self.stock, stockitem) - end - - -- Destroy group if it is alive. - if group:IsAlive()==true then - group:Destroy() - end - - else - -- Group name does not exist! - self:E(string.format("ERROR: Template group name not defined in the mission editor. Check the spelling! templategroupname=%s",tostring(templategroupname))) - end - - return self -end - ---- Set a zone where the (ground) assets of the warehouse are spawned once requested. --- @param #WAREHOUSE self --- @param Core.Zone#ZONE zone The spawn zone. --- @return #WAREHOUSE self -function WAREHOUSE:SetSpawnZone(zone) - self.spawnzone=zone - return self -end - ---- Add a delayed request for the warehouse. --- @param #WAREHOUSE self --- @param #number delay Delay before the request is made in seconds. --- @param #WAREHOUSE warehouse The warehouse requesting supply. --- @param #WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. --- @param AssetDescriptorValue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. --- @param #number nAsset Number of groups requested that match the asset specification. --- @param #WAREHOUSE.TransportType TransportType Type of transport. --- @param #number nTransport Number of transport units requested. --- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. -function WAREHOUSE:__AddRequest(delay, warehouse, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType, nTransport, Prio) - SCHEDULER:New(nil,WAREHOUSE.AddRequest,{self, warehouse, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType, nTransport, Prio}, delay) -end - - ---- Add a request for the warehouse. --- @param #WAREHOUSE self --- @param #WAREHOUSE warehouse The warehouse requesting supply. --- @param #WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. --- @param AssetDescriptorValue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. --- @param #number nAsset Number of groups requested that match the asset specification. --- @param #WAREHOUSE.TransportType TransportType Type of transport. --- @param #number nTransport Number of transport units requested. --- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. -function WAREHOUSE:AddRequest(warehouse, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType, nTransport, Prio) - - nAsset=nAsset or 1 - TransportType=TransportType or WAREHOUSE.TransportType.SELFPROPELLED - nTransport=nTransport or 1 - Prio=Prio or 50 - - --TODO: check that - -- if warehouse or requestor is a FARP, plane asset and transport not possible. - -- if requestor or warehouse is a SHIP, APC transport not possible, SELFPROPELLED only for AIR/SHIP - -- etc. etc... - - --[[ - local warehouse=nil --#WAREHOUSE - local airbase=nil --Wrapper.Airbase#AIRBASE - if requestor:IsInstanceOf("AIRBASE") then - airbase=requestor - elseif requestor:IsInstanceOf("WAREHOUSE") then - warehouse=requestor - airbase=warehouse.airbase - end - ]] - - local request_air=false - local request_plane=false - local request_helo=false - local request_ground=false - local request_naval=false - local request_category=warehouse.category or -1 --DCS#Airbase.Category - - -- Check if category is matching - if AssetDescriptor==WAREHOUSE.Descriptor.CATEGORY then - - if AssetDescriptorValue==Group.Category.AIRPLANE then - request_plane=true - elseif AssetDescriptorValue==Group.Category.HELICOPTER then - request_helo=true - elseif AssetDescriptorValue==Group.Category.GROUND then - request_ground=true - elseif AssetDescriptorValue==Group.Category.SHIP then - request_naval=true - elseif AssetDescriptorValue==Group.Category.TRAIN then - request_ground=true - else - self:E("ERROR: incorrect request. Asset Descriptor missmatch! Has to be Group.Cagetory.AIRPLANE, ...") - return - end - + -- Put assets in new warehouse. + for _,_group in pairs(groupset:GetSetObjects()) do + local group=_group --Wrapper.Group#GROUP + request.warehouse:AddAsset(group:GetTemplate(), 1) end - -- Check attribute is matching - if AssetDescriptor==WAREHOUSE.Descriptor.ATTRIBUTE then - if AssetDescriptorValue==WAREHOUSE.Attribute.ARTILLERY then - request_ground=true - elseif AssetDescriptorValue==WAREHOUSE.Attribute.ATTACKHELICOPTER then - request_helo=true - elseif AssetDescriptorValue==WAREHOUSE.Attribute.AWACS then - request_plane=true - elseif AssetDescriptorValue==WAREHOUSE.Attribute.BOMBER then - request_plane=true - elseif AssetDescriptorValue==WAREHOUSE.Attribute.FIGHTER then - request_plane=true - elseif AssetDescriptorValue==WAREHOUSE.Attribute.INFANTRY then - request_ground=true - elseif AssetDescriptorValue==WAREHOUSE.Attribute.OTHER then - self:E("ERROR: incorrect request. Asset attribute WAREHOUSE.Attribute.OTHER is not valid!") - return - elseif AssetDescriptorValue==WAREHOUSE.Attribute.SHIP then - request_naval=true - elseif AssetDescriptorValue==WAREHOUSE.Attribute.TANK then - request_ground=true - elseif AssetDescriptorValue==WAREHOUSE.Attribute.TANKER then - request_plane=true - elseif AssetDescriptorValue==WAREHOUSE.Attribute.TRAIN then - request_ground=true - elseif AssetDescriptorValue==WAREHOUSE.Attribute.TRANSPORT_APC then - request_ground=true - elseif AssetDescriptorValue==WAREHOUSE.Attribute.TRANSPORT_HELO then - request_helo=true - elseif AssetDescriptorValue==WAREHOUSE.Attribute.TRANSPORT_PLANE then - request_plane=true - elseif AssetDescriptorValue==WAREHOUSE.Attribute.TRUCK then - request_ground=true - else - self:E("ERROR: incorrect request. Unknown asset attribute!") - return - end - end - - -- General air request. - request_air=request_helo or request_plane - - if request_air then - - if request_plane then - -- No airplane to or from FARPS. - if request_category==Airbase.Category.HELIPAD or self.category==Airbase.Category.HELIPAD then - self:E("ERROR: incorrect request. Asset aircraft requestst but warehouse or requestor is HELIPAD/FARP!") - return - end - elseif request_helo then - - end - - elseif request_ground then - - -- No ground assets directly to ships. - if (request_category==Airbase.Category.SHIP or self.category==Airbase.Category.SHIP) - and not (TransportType==WAREHOUSE.TransportType.AIRPLANE or TransportType==WAREHOUSE.TransportType.HELICOPTER) then - self:E("ERROR: incorrect request. Ground asset requested but warehouse or requestor is SHIP!") - return - end - - elseif request_naval then - - end - - -- Check if transport is possible. - if TransportType~=nil then - if TransportType==WAREHOUSE.TransportType.AIRPLANE then - - -- No airplanes from or to FARPS. - if self.category==Airbase.Category.HELIPAD or request_category==Airbase.Category.HELIPAD then - self:E("ERROR: incorrect request. Warehouse or requestor is FARP. No transport by plane possible!") - return - end - - elseif TransportType==WAREHOUSE.TransportType.APC then - - -- Transport by ground units. - - elseif TransportType==WAREHOUSE.TransportType.HELICOPTER then - - -- Transport by helicopters ==> need FARP or AIRBASE, well not really... - - elseif TransportType==WAREHOUSE.TransportType.SELFPROPELLED then - - - elseif TransportType==WAREHOUSE.TransportType.SHIP then - - -- Transport by ship. - self:E("ERROR: incorrect request. Transport by SHIP not implemented yet!") - return - - elseif TransportType==WAREHOUSE.TransportType.TRAIN then - - -- Transport by train. - self:E("ERROR: incorrect request. Transport by TRAIN not implemented yet!") - return - - else - -- No match. - self:E("ERROR: incorrect request. Transport type unknown!") - return - end - - end - - -- Increase id. - self.queueid=self.queueid+1 - - -- Request queue table item. - local request={ - uid=self.queueid, - prio=Prio, - warehouse=warehouse, - airbase=warehouse.airbase, - category=request_category, - assetdesc=AssetDescriptor, - assetdescval=AssetDescriptorValue, - nasset=nAsset, - transporttype=TransportType, - ntransport=nTransport} - - -- Add request to queue. - table.insert(self.queue, request) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1256,9 +1057,10 @@ function WAREHOUSE:_RouteGround(Group, Coordinate, Speed) if Group and Group:IsAlive() then + -- Set speed. local _speed=Speed or Group:GetSpeedMax()*0.6 - -- Create a + -- Create task. local Waypoints, canroad = Group:TaskGroundOnRoad(Coordinate, _speed, "Off Road", true) -- Task function triggering the arrived event. @@ -1409,11 +1211,215 @@ end -- Helper functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Checks if the request can be fullfilled. +--- Checks if the request can be fulfilled in general. If not, it is removed from the queue. +-- Check if departure and destination bases are of the right type. -- @param #WAREHOUSE self -- @param #WAREHOUSE.Queueitem qitem The request to be checked. -- @return #boolean If true, request can be executed. If false, something is not right. -function WAREHOUSE:_CheckRequest(request) +function WAREHOUSE:_CheckRequestValid() + + -- Requests to delete. + local delid={} + + for _,_request in pairs(self.queue) do + local request=_request --#WAREHOUSE.Queueitem + + local valid=true + + --TODO: check that + -- if warehouse or requestor is a FARP, plane asset and transport not possible. + -- if requestor or warehouse is a SHIP, APC transport not possible, SELFPROPELLED only for AIR/SHIP + -- etc. etc... + + local asset_air=false + local asset_plane=false + local asset_helo=false + local asset_ground=false + local asset_train=false + local asset_naval=false + + -- Check if category was provided. + if request.assetdesc==WAREHOUSE.Descriptor.CATEGORY then + + if request.assetdescval==Group.Category.AIRPLANE then + asset_plane=true + elseif request.assetdescval==Group.Category.HELICOPTER then + asset_helo=true + elseif request.assetdescval==Group.Category.GROUND then + asset_ground=true + elseif request.assetdescval==Group.Category.SHIP then + asset_naval=true + elseif request.assetdescval==Group.Category.TRAIN then + asset_ground=true + asset_train=true + -- Only one train due to finding spawn placen on rail! + --nAsset=1 + else + self:E("ERROR: incorrect request. Asset Descriptor missmatch! Has to be Group.Cagetory.AIRPLANE, ...") + valid=false + end + + end + + -- Check attribute is matching + if request.assetdesc==WAREHOUSE.Descriptor.ATTRIBUTE then + if request.assetdescval==WAREHOUSE.Attribute.ARTILLERY then + asset_ground=true + elseif request.assetdescval==WAREHOUSE.Attribute.ATTACKHELICOPTER then + asset_helo=true + elseif request.assetdescval==WAREHOUSE.Attribute.AWACS then + asset_plane=true + elseif request.assetdescval==WAREHOUSE.Attribute.BOMBER then + asset_plane=true + elseif request.assetdescval==WAREHOUSE.Attribute.FIGHTER then + asset_plane=true + elseif request.assetdescval==WAREHOUSE.Attribute.INFANTRY then + asset_ground=true + elseif request.assetdescval==WAREHOUSE.Attribute.OTHER then + self:E("ERROR: incorrect request. Asset attribute WAREHOUSE.Attribute.OTHER is not valid!") + valid=false + elseif request.assetdescval==WAREHOUSE.Attribute.SHIP then + asset_naval=true + elseif request.assetdescval==WAREHOUSE.Attribute.TANK then + asset_ground=true + elseif request.assetdescval==WAREHOUSE.Attribute.TANKER then + asset_plane=true + elseif request.assetdescval==WAREHOUSE.Attribute.TRAIN then + asset_ground=true + elseif request.assetdescval==WAREHOUSE.Attribute.TRANSPORT_APC then + asset_ground=true + elseif request.assetdescval==WAREHOUSE.Attribute.TRANSPORT_HELO then + asset_helo=true + elseif request.assetdescval==WAREHOUSE.Attribute.TRANSPORT_PLANE then + asset_plane=true + elseif request.assetdescval==WAREHOUSE.Attribute.TRUCK then + asset_ground=true + else + self:E("ERROR: incorrect request. Unknown asset attribute!") + valid=false + end + end + + -- General air request. + asset_air=asset_helo or asset_plane + + if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then + + if asset_air then + + if asset_plane then + + -- No airplane to or from FARPS. + if request.category==Airbase.Category.HELIPAD or self.category==Airbase.Category.HELIPAD then + self:E("ERROR: incorrect request. Asset aircraft requestst but warehouse or requestor is HELIPAD/FARP!") + valid=false + end + + -- Category SHIP is not general enough! Fighters can go to carriers. Which fighters, is there an attibute? + -- Also for carriers, attibute? + + elseif asset_helo then + + -- Helos need a FARP or AIRBASE or SHIP for spawning. Event if they go there they "cannot" be spawned again. + -- Unless I allow spawning of helos in the the spawn zone. But one should place at least a FARP there. + if self.category==-1 or request.category==-1 then + self:E("ERROR: incorrect request. Helos need a AIRBASE/HELIPAD/SHIP as home/destinaion base!") + valid=false + end + + end + + elseif asset_ground then + + -- No ground assets directly to or from ships. + -- TODO: May needs refinement if warehouse is on land and requestor is ship in harbour?! + if (request.category==Airbase.Category.SHIP or self.category==Airbase.Category.SHIP) then + self:E("ERROR: incorrect request. Ground asset requested but warehouse or requestor is SHIP!") + valid=false + end + + elseif asset_naval then + + self:E("ERROR: incorrect request. Naval units not supported yet!") + valid=false + + end + + else + + -- Assests need a transport. + + if request.transporttype==WAREHOUSE.TransportType.AIRPLANE then + + -- Airplanes only to AND from airdromes. + if self.category~=Airbase.Category.AIRDROME or request.category~=Airbase.Category.AIRDROME then + self:E("ERROR: incorrect request. Warehouse or requestor does not have an airdrome. No transport by plane possible!") + valid=false + end + + --TODO: Not sure if there are any transport planes that can land on a carrier? + + elseif request.transporttype==WAREHOUSE.TransportType.APC then + + -- Transport by ground units. + + -- No transport to or from ships + if self.category==Airbase.Category.SHIP or request.category==Airbase.Category.SHIP then + self:E("ERROR: incorrect request. Warehouse or requestor is SHIP. No transport by APC possible!") + valid=false + end + + elseif request.transporttype==WAREHOUSE.TransportType.HELICOPTER then + + -- Transport by helicopters ==> need airbase for spawning but not for delivering to the zone. + if self.category==-1 then + self:E("ERROR: incorrect request. Warehouse has no airbase. Transport by helicopter not possible!") + valid=false + end + + elseif request.transporttype==WAREHOUSE.TransportType.SHIP then + + -- Transport by ship. + self:E("ERROR: incorrect request. Transport by SHIP not implemented yet!") + valid=false + + elseif request.transporttype==WAREHOUSE.TransportType.TRAIN then + + -- Only one train due to limited spawn place. + --nTransport=1 + + -- Transport by train. + self:E("ERROR: incorrect request. Transport by TRAIN not implemented yet!") + valid=false + + else + -- No match. + self:E("ERROR: incorrect request. Transport type unknown!") + valid=false + end + + end + + if not valid then + table.insert(delid, request.id) + end + + end -- loop queue items. + + + -- Delete invalid requests. + for _,_uid in pairs(delid) do + self:_DeleteQueueItem(_uid) + end + +end + +--- Checks if the request can be fullfilled right now. +-- Check for current parking situation, number of assets and transports currently in stock +-- @param #WAREHOUSE self +-- @param #WAREHOUSE.Queueitem request The request to be checked. +-- @return #boolean If true, request can be executed. If false, something is not right. +function WAREHOUSE:_CheckRequestNow(request) local okay=true @@ -1518,7 +1524,7 @@ function WAREHOUSE:_CheckQueue() local request=nil --#WAREHOUSE.Queueitem for _,_qitem in ipairs(self.queue) do local qitem=_qitem --#WAREHOUSE.Queueitem - local okay=self:_CheckRequest(qitem) + local okay=self:_CheckRequestNow(qitem) if okay==true then request=qitem break @@ -1637,6 +1643,71 @@ function WAREHOUSE:_GetParkingForAssets(assetlist, parkingdata) return assetparking, parkingdata end +--- Get the request belonging to a group. +-- @param #WAREHOUSE self +-- @param Wrapper.Group#GROUP group The group from which the info is gathered. +-- @param #table queue Queue holding all requests. +-- @return #WAREHOUSE.Queueitem The request belonging to this group. +function WAREHOUSE:_GetRequestOfGroup(group, queue) + + -- Get warehouse, asset and request ID from group name. + local wid,aid,rid=self:_GetInfoFromGroup(group) + + -- Find the request. + for _,_request in pairs(queue) do + local request=_request --#WAREHOUSE.Queueitem + if request.uid==rid then + return request + end + end + +end + +--- Get warehouse id, asset id and request id from group name. +-- @param #WAREHOUSE self +-- @param Wrapper.Group#GROUP group The group from which the info is gathered. +function WAREHOUSE:_GetInfoFromGroup(group) + + ---@param #string text The text to analyse. + local function analyse(text) + + -- Get rid of #0001 tail from spawn. + local unspawned=UTILS.Split(text, "#")[1] + + -- Split keywords. + local keywords=UTILS.Split(unspawned, "_") + + local _wid=nil -- warehouse UID + local _aid=nil -- asset UID + local _rid=nil -- request UID + + -- Loop over keys. + for _,keys in pairs(keywords) do + local str=UTILS.Split(keys, "-") + local key=str[1] + local val=str[2] + if key:find("WID") then + _wid=tonumber(val) + elseif key:find("AID") then + _aid=tonumber(val) + elseif key:find("RID") then + _rid=tonumber(val) + end + end + + return _wid,_aid,_rid + end + + local name=group:GetName() + env.info("FF Name = "..tostring(name)) + + local wid,aid,rid=analyse(name) + env.info("FF warehouse id = %s"..tostring(wid)) + env.info("FF asset id = %s"..tostring(aid)) + env.info("FF request id = %s"..tostring(rid)) + +end + --- Filter stock assets by table entry. -- @param #WAREHOUSE self -- @param #table stock Table holding all assets in stock of the warehouse. Each entry is of type @{#WAREHOUSE.Stockitem}. From 4d7812b368eaf7453c951a7a4a8bfb5fe7bdd891 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Mon, 13 Aug 2018 21:42:44 +0200 Subject: [PATCH 252/420] Working version for airplanes, now need to debug to make it also happen for helicopters and apcs --- .../Moose/AI/AI_Cargo_Airplane.lua | 35 ++++++++++++++++--- .../Moose/AI/AI_Cargo_Dispatcher.lua | 2 +- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 75f3f2873..8cacb48ea 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -42,7 +42,7 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) self:AddTransition( "Boarding", "Loaded", "Loaded" ) self:AddTransition( "Loaded", "Unload", "Unboarding" ) self:AddTransition( "Unboarding", "Unboard", "Unboarding" ) - self:AddTransition( "Unboarding", "Unloaded", "Unloaded" ) + self:AddTransition( "Unboarding" , "Unloaded", "Unloaded" ) self:AddTransition( "*", "Landed", "*" ) self:AddTransition( "*", "Home" , "*" ) @@ -469,7 +469,15 @@ function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To ) if Airplane and Airplane:IsAlive() then local Cargos = Airplane:GetCargo() for CargoID, Cargo in pairs( Cargos ) do - Cargo:UnBoard() + + local Angle = 180 + local CargoCarrierHeading = Airplane:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + self:T( { CargoCarrierHeading, CargoDeployHeading } ) + local CargoDeployCoordinate = Airplane:GetPointVec2():Translate( 150, CargoDeployHeading ) + + Cargo:UnBoard( CargoDeployCoordinate ) + Cargo:SetDeployed( true ) self:__Unboard( 10, Cargo ) end end @@ -484,14 +492,30 @@ end -- @param #string To To state. function AI_CARGO_AIRPLANE:onafterUnboard( Airplane, From, Event, To, Cargo ) + self:E( { "Unboard", Cargo } ) + if Airplane and Airplane:IsAlive() then if not Cargo:IsUnLoaded() then self:__Unboard( 10, Cargo ) else + local Cargos = Airplane:GetCargo() + for CargoID, Cargo in pairs( Cargos ) do + if Cargo:IsLoaded() then + local Angle = 180 + local CargoCarrierHeading = Airplane:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + self:T( { CargoCarrierHeading, CargoDeployHeading } ) + local CargoDeployCoordinate = Airplane:GetPointVec2():Translate( 150, CargoDeployHeading ) + Cargo:UnBoard( CargoDeployCoordinate ) + Cargo:SetDeployed( true ) + + self:__Unboard( 10, Cargo ) + return + end + end self:__Unloaded( 1, Cargo ) end end - end --- On after Unloaded event. Cargo has been unloaded, i.e. the unboarding process is finished. @@ -500,12 +524,15 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. +-- @param Cargo.Cargo#CARGO Cargo function AI_CARGO_AIRPLANE:onafterUnloaded( Airplane, From, Event, To, Cargo ) + self:E( { "Unloaded", Cargo } ) + if Airplane and Airplane:IsAlive() then self.Airplane = Airplane + self.Transporting = false -- This will only be executed when there is no cargo onboard anymore. The dispatcher will then kick-off the pickup cycle! end - end --- Route the airplane from one airport or it's current position to another airbase. diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index c7a0ea4b9..c19d760a8 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -414,7 +414,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() for CargoName, Cargo in pairs( self.SetCargo:GetSet() ) do local Cargo = Cargo -- Cargo.Cargo#CARGO self:F( { Cargo = Cargo:GetName(), UnLoaded = Cargo:IsUnLoaded(), Deployed = Cargo:IsDeployed(), PickupCargo = self.PickupCargo[Carrier] ~= nil } ) - if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then + if Cargo:IsUnLoaded() == true and Cargo:IsDeployed() == false then local CargoCoordinate = Cargo:GetCoordinate() local CoordinateFree = true for CarrierPickup, Coordinate in pairs( self.PickupCargo ) do From d21cee93588ae77f75f462b1e2bdc7e9e5cf4451 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Tue, 14 Aug 2018 00:10:42 +0200 Subject: [PATCH 253/420] Warehouse v0.1.6 --- Moose Development/Moose/Core/Set.lua | 4 +- .../Moose/Functional/Warehouse.lua | 572 ++++++++++++------ 2 files changed, 394 insertions(+), 182 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 6a284ab73..da13df75d 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -3984,7 +3984,9 @@ end -- @return self function SET_AIRBASE:AddAirbase( airbase ) - self:Add( airbase:GetName(), airbase ) + if airbase then + self:Add( airbase:GetName(), airbase ) + end return self end diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index c2d3cde99..d819b2e1c 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -177,7 +177,7 @@ WAREHOUSE.TransportType = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.1.5" +WAREHOUSE.version="0.1.6" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -194,7 +194,11 @@ WAREHOUSE.version="0.1.5" -- TODO: Spawn warehouse assets as uncontrolled or AI off and activate them when requested. -- TODO: Handle cases with immobile units. -- TODO: How to handle multiple units in a transport group? --- DONE: Add phyical object, if destroyed asssets are gone. +-- DONE: Add phyical object +-- TODO: If warehouse is destoyed, all asssets are gone. +-- TODO: If warehosue is captured, change warehouse and assets to other coalition. +-- TODO: Handle cases for aircraft carriers and other ships. Place warehouse on carrier possible? On others probably not - exclude them? +-- TODO: Handle cargo crates. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor(s) @@ -260,14 +264,27 @@ function WAREHOUSE:New(warehouse, spawnzone, airbase) -- Define the default spawn zone. self.spawnzone=ZONE_RADIUS:New(string.format("Spawnzone %s",warehouse:GetName()), warehouse:GetVec2(), 200) + + -- Start State. + self:SetStartState("Stopped") -- Add FSM transitions. - self:AddTransition("*", "Start", "Running") - self:AddTransition("*", "Status", "*") - self:AddTransition("*", "Request", "*") - self:AddTransition("*", "Arrived", "*") - self:AddTransition("*", "Delivered", "*") + self:AddTransition("Stopped", "Load", "Stopped") + self:AddTransition("Stopped", "Start", "Running") + self:AddTransition("Running", "Status", "*") + self:AddTransition("Paused", "Status", "*") + self:AddTransition("*", "AddAsset", "*") + self:AddTransition("*", "AddRequest", "*") + self:AddTransition("Running", "Request", "*") + self:AddTransition("*", "Arrived", "*") + self:AddTransition("*", "Delivered", "*") + self:AddTransition("Running", "Pause", "Paused") + self:AddTransition("Paused", "Unpause", "Running") + self:AddTransition("*", "Stop", "Stopped") + self:AddTransition("*", "Save", "*") + -- Pseudo Functions + --- Triggers the FSM event "Start". Starts the warehouse. -- @function [parent=#WAREHOUSE] Start -- @param #WAREHOUSE self @@ -288,12 +305,50 @@ function WAREHOUSE:New(warehouse, spawnzone, airbase) -- @param #number delay Delay in seconds. - --- Triggers the FSM event "Request". Executes a request if possible. + --- Trigger the FSM event "AddAsset". Add an airplane group to the warehouse stock. + -- @function [parent=#WAREHOUSE] AddAsset + -- @param #WAREHOUSE self + -- @param #string templategroupname Name of the late activated template group as defined in the mission editor. + -- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1. + + --- Trigger the FSM event "AddAsset" with a delay. Add an airplane group to the warehouse stock. + -- @function [parent=#WAREHOUSE] __AddAsset + -- @param #WAREHOUSE self + -- @param #number delay Delay in seconds. + -- @param #string templategroupname Name of the late activated template group as defined in the mission editor. + -- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1. + + + --- Triggers the FSM event "AddRequest". Add a request to the warehouse queue, which is processed when possible. + -- @function [parent=#WAREHOUSE] AddRequest + -- @param #WAREHOUSE self + -- @param #WAREHOUSE warehouse The warehouse requesting supply. + -- @param #WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. + -- @param AssetDescriptorValue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. + -- @param #number nAsset Number of groups requested that match the asset specification. + -- @param #WAREHOUSE.TransportType TransportType Type of transport. + -- @param #number nTransport Number of transport units requested. + -- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. + + --- Triggers the FSM event "AddRequest" with a delay. Add a request to the warehouse queue, which is processed when possible. + -- @function [parent=#WAREHOUSE] __AddRequest + -- @param #WAREHOUSE self + -- @param #number delay Delay in seconds. + -- @param #WAREHOUSE warehouse The warehouse requesting supply. + -- @param #WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. + -- @param AssetDescriptorValue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. + -- @param #number nAsset Number of groups requested that match the asset specification. + -- @param #WAREHOUSE.TransportType TransportType Type of transport. + -- @param #number nTransport Number of transport units requested. + -- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. + + + --- Triggers the FSM event "Request". Executes a request from the queue if possible. -- @function [parent=#WAREHOUSE] Request -- @param #WAREHOUSE self -- @param #WAREHOUSE.Queueitem Request Information table of the request. - --- Triggers the FSM event "Request" after a delay. Executes a request if possible. + --- Triggers the FSM event "Request" after a delay. Executes a request from the queue if possible. -- @function [parent=#WAREHOUSE] __Request -- @param #WAREHOUSE self -- @param #number Delay Delay in seconds. @@ -330,124 +385,6 @@ end -- User functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Add an airplane group to the warehouse stock. --- @param #WAREHOUSE self --- @param #string templategroupname Name of the late activated template group as defined in the mission editor. --- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1. --- @return #WAREHOUSE self -function WAREHOUSE:AddAsset(templategroupname, ngroups) - - -- Set default. - local n=ngroups or 1 - - local group=GROUP:FindByName(templategroupname) - - if group then - - local DCSgroup=group:GetDCSObject() - local DCSunit=DCSgroup:getUnit(1) - local DCSdesc=DCSunit:getDesc() - local DCSdisplay=DCSdesc.displayName - local DCScategory=DCSgroup:getCategory() - local DCStype=DCSunit:getTypeName() - local SpeedMax=group:GetSpeedMax() - local RangeMin=group:GetRange() - - env.info(string.format("group name = %s", group:GetName())) - env.info(string.format("display name = %s", DCSdisplay)) - env.info(string.format("category = %s", DCScategory)) - env.info(string.format("type = %s", DCStype)) - env.info(string.format("speed max = %s", tostring(SpeedMax))) - env.info(string.format("range min = %s", tostring(RangeMin))) - self:E({fullassetdesc=DCSdesc}) - - local attribute=self:_GetAttribute(templategroupname) - - -- Add this n times to the table. - for i=1,n do - local stockitem={} --#WAREHOUSE.Stockitem - - self.assetid=self.assetid+1 - - stockitem.uid=self.assetid - stockitem.templatename=templategroupname - stockitem.template=UTILS.DeepCopy(_DATABASE.Templates.Groups[templategroupname].Template) - stockitem.category=DCScategory - stockitem.unittype=DCStype - stockitem.attribute=attribute - stockitem.range=RangeMin - stockitem.speedmax=SpeedMax - - -- Modify the template so that the group is spawned with the right coalition. - stockitem.template.CoalitionID=self.coalition - stockitem.template.CountryID=self.country - - table.insert(self.stock, stockitem) - end - - -- Destroy group if it is alive. - if group:IsAlive()==true then - group:Destroy() - end - - else - -- Group name does not exist! - self:E(string.format("ERROR: Template group name not defined in the mission editor. Check the spelling! templategroupname=%s",tostring(templategroupname))) - end - - return self -end - ---- Add a request for the warehouse. --- @param #WAREHOUSE self --- @param #WAREHOUSE warehouse The warehouse requesting supply. --- @param #WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. --- @param AssetDescriptorValue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. --- @param #number nAsset Number of groups requested that match the asset specification. --- @param #WAREHOUSE.TransportType TransportType Type of transport. --- @param #number nTransport Number of transport units requested. --- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. -function WAREHOUSE:AddRequest(warehouse, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType, nTransport, Prio) - - nAsset=nAsset or 1 - TransportType=TransportType or WAREHOUSE.TransportType.SELFPROPELLED - nTransport=nTransport or 1 - Prio=Prio or 50 - - -- Increase id. - self.queueid=self.queueid+1 - - -- Request queue table item. - local request={ - uid=self.queueid, - prio=Prio, - warehouse=warehouse, - airbase=warehouse.airbase, - category=warehouse.category, - assetdesc=AssetDescriptor, - assetdescval=AssetDescriptorValue, - nasset=nAsset, - transporttype=TransportType, - ntransport=nTransport} - - -- Add request to queue. - table.insert(self.queue, request) -end - ---- Add a delayed request for the warehouse. --- @param #WAREHOUSE self --- @param #number delay Delay before the request is made in seconds. --- @param #WAREHOUSE warehouse The warehouse requesting supply. --- @param #WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. --- @param AssetDescriptorValue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. --- @param #number nAsset Number of groups requested that match the asset specification. --- @param #WAREHOUSE.TransportType TransportType Type of transport. --- @param #number nTransport Number of transport units requested. --- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. -function WAREHOUSE:__AddRequest(delay, warehouse, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType, nTransport, Prio) - SCHEDULER:New(nil,WAREHOUSE.AddRequest,{self, warehouse, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType, nTransport, Prio}, delay) -end - --- Set a zone where the (ground) assets of the warehouse are spawned once requested. -- @param #WAREHOUSE self -- @param Core.Zone#ZONE zone The spawn zone. @@ -483,19 +420,55 @@ function WAREHOUSE:onafterStart(From, Event, To) self.spawnzone:GetCoordinate():MarkToAll("Spawnzone of warehouse "..self.warehouse:GetName()) -- Handle events: - -- event takeoff - -- event landing - -- event crash/dead - -- event base captured ==> change coalition ==> add assets to other coalition - - -- Handle event that airbase was captured. - if self.airbase then - --self.airbase:HandleEvent(EVENTS.BaseCaptured, self._BaseCaptured) - end + self:HandleEvent(EVENTS.Birth, self._OnEventBirth) + self:HandleEvent(EVENTS.EngineStartup, self._OnEventEngineStartup) + self:HandleEvent(EVENTS.Takeoff, self._OnEventTakeOff) + self:HandleEvent(EVENTS.Land, self._OnEventLanding) + self:HandleEvent(EVENTS.EngineShutdown, self._OnEventEngineShutdown) + self:HandleEvent(EVENTS.Crash, self._OnEventCrashOrDead) + self:HandleEvent(EVENTS.Dead, self._OnEventCrashOrDead) + self:HandleEvent(EVENTS.BaseCaptured, self._OnEventBaseCaptured) self:__Status(5) end +--- On after "Stop" event. Stops the warehouse, unhandles all events. +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function WAREHOUSE:onafterStop(From, Event, To) + self:E(self.wid..string.format("Warehouse stopped")) + + -- Unhandle event. + self:UnHandleEvent(EVENTS.Birth) + self:UnHandleEvent(EVENTS.EngineStartup) + self:UnHandleEvent(EVENTS.Takeoff) + self:UnHandleEvent(EVENTS.Land) + self:UnHandleEvent(EVENTS.EngineShutdown) + self:UnHandleEvent(EVENTS.Crash) + self:UnHandleEvent(EVENTS.Dead) + self:UnHandleEvent(EVENTS.BaseCaptured) +end + +--- On after "Pause" event. Pauses the warehouse, i.e. no requests are processed. However, new requests and new assets can be added in this state. +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function WAREHOUSE:onafterPause(From, Event, To) + self:E(self.wid..string.format("Warehouse paused! Queued requests are not processed in this state.")) +end + +--- On after "Unpause" event. Unpauses the warehouse, i.e. requests in queue are processed again. +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function WAREHOUSE:onafterUnpause(From, Event, To) + self:E(self.wid..string.format("Warehouse unpaused! Processing of requests is resumed again.")) +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- On after Status event. Checks the queue and handles requests. @@ -555,6 +528,131 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- On after "AddAsset" event. Add an airplane group to the warehouse stock. +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #string templategroupname Name of the late activated template group as defined in the mission editor. +-- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1. +function WAREHOUSE:onafterAddAsset(From, Event, To, templategroupname, ngroups) + + -- Set default. + local n=ngroups or 1 + + local group=GROUP:FindByName(templategroupname) + + if group then + + local DCSgroup=group:GetDCSObject() + local DCSunit=DCSgroup:getUnit(1) + local DCSdesc=DCSunit:getDesc() + local DCSdisplay=DCSdesc.displayName + local DCScategory=DCSgroup:getCategory() + local DCStype=DCSunit:getTypeName() + local SpeedMax=group:GetSpeedMax() + local RangeMin=group:GetRange() + + env.info(string.format("New asset:")) + env.info(string.format("Group name = %s", group:GetName())) + env.info(string.format("Display name = %s", DCSdisplay)) + env.info(string.format("Category = %s", DCScategory)) + env.info(string.format("Type = %s", DCStype)) + env.info(string.format("Speed max = %s km/h", tostring(SpeedMax))) + env.info(string.format("Range min = %s m", tostring(RangeMin))) + self:E({fullassetdesc=DCSdesc}) + + -- Get the generalized attribute. + local attribute=self:_GetAttribute(templategroupname) + + -- Add this n times to the table. + for i=1,n do + local stockitem={} --#WAREHOUSE.Stockitem + + self.assetid=self.assetid+1 + + stockitem.uid=self.assetid + stockitem.templatename=templategroupname + stockitem.template=UTILS.DeepCopy(_DATABASE.Templates.Groups[templategroupname].Template) + stockitem.category=DCScategory + stockitem.unittype=DCStype + stockitem.attribute=attribute + stockitem.range=RangeMin + stockitem.speedmax=SpeedMax + + -- Modify the template so that the group is spawned with the right coalition. + stockitem.template.CoalitionID=self.coalition + stockitem.template.CountryID=self.country + + table.insert(self.stock, stockitem) + end + + -- Destroy group if it is alive. + if group:IsAlive()==true then + group:Destroy() + end + + else + -- Group name does not exist! + self:E(string.format("ERROR: Template group name not defined in the mission editor. Check the spelling! templategroupname=%s",tostring(templategroupname))) + end + +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- On after "AddRequest" event. Add a request to the warehouse queue, which is processed when possible. +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #WAREHOUSE warehouse The warehouse requesting supply. +-- @param #WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. +-- @param AssetDescriptorValue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. +-- @param #number nAsset Number of groups requested that match the asset specification. +-- @param #WAREHOUSE.TransportType TransportType Type of transport. +-- @param #number nTransport Number of transport units requested. +-- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. +function WAREHOUSE:onafterAddRequest(From, Event, To, warehouse, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType, nTransport, Prio) + + -- Defaults. + nAsset=nAsset or 1 + TransportType=TransportType or WAREHOUSE.TransportType.SELFPROPELLED + Prio=Prio or 50 + if nTransport==nil then + if TransportType==WAREHOUSE.TransportType.SELFPROPELLED then + nTransport=0 + else + nTransport=1 + end + end + + -- Increase id. + self.queueid=self.queueid+1 + + -- Request queue table item. + local request={ + uid=self.queueid, + prio=Prio, + warehouse=warehouse, + airbase=warehouse.airbase, + category=warehouse.category, + assetdesc=AssetDescriptor, + assetdescval=AssetDescriptorValue, + nasset=nAsset, + transporttype=TransportType, + ntransport=nTransport, + ndelivered=0, + ntransporthome=0 + } --#WAREHOUSE.Queueitem + + -- Add request to queue. + table.insert(self.queue, request) + +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + --- On before "Request" event. Checks if the request can be fullfilled. -- @param #WAREHOUSE self -- @param #string From From state. @@ -611,6 +709,10 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Spawn assets. local _spawngroups,_cargotype,_cargocategory=self:_SpawnAssetRequest(Request) --Core.Set#SET_GROUP + -- Add cargo groups to request. + Request.cargogroupset=_spawngroups + Request.ndelivered=0 + -- Add groups to cargo if they don't go by themselfs. local CargoGroups --Core.Set#SET_CARGO if Request.transporttype ~= WAREHOUSE.TransportType.SELFPROPELLED then @@ -618,11 +720,31 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) --TODO: make nearradius depended on transport type and asset type. local _loadradius=5000 local _nearradius=35 + + if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then + _loadradius=5000 + elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then + _loadradius=500 + elseif Request.transporttype==WAREHOUSE.TransportType.APC then + _loadradius=100 + end + CargoGroups = SET_CARGO:New() - for _,_group in pairs(_spawngroups:GetSetObjects()) do - local cargogroup = CARGO_GROUP:New(_group, _cargotype, "Peter", _loadradius, _nearradius) + + for _i,_group in pairs(_spawngroups:GetSetObjects()) do + local group=_group --Wrapper.Group#GROUP + local _wid,_aid,_rid=self:_GetIDsFromGroup(group) + local _alias=self:_alias(group:GetTypeName(),_wid,_aid,_rid) + local cargogroup = CARGO_GROUP:New(_group, _cargotype,_alias,_loadradius,_nearradius) CargoGroups:AddCargo(cargogroup) end + + end + + -- Self request! Assets are only spawned but not routed or transported anywhere. + if self.warehouse:GetName()==Request.warehouse.warehouse:GetName() then + env.info("FF selfrequest!") + return end ------------------------------------------------------------------------------------------------------------------------------------ @@ -661,12 +783,6 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) end - -- Add cargo groups to request. - Request.cargogroupset=_spawngroups - Request.ndelivered=0 - - --local bla=UTILS.DeepCopy(Request) - -- Add request to pending queue. table.insert(self.pending, Request) @@ -687,7 +803,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Pickup and deploy zones/bases. local PickupAirbaseSet = SET_AIRBASE:New():AddAirbase(self.airbase) local DeployAirbaseSet = SET_AIRBASE:New():AddAirbase(Request.airbase) - local DeployZoneSet = SET_ZONE:New():AddZone(Request.airbase:GetZone()) + local DeployZoneSet = SET_ZONE:New():AddZone(Request.warehouse.spawnzone) -- Cargo dispatcher. local CargoTransport --AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER @@ -905,6 +1021,7 @@ function WAREHOUSE:_SpawnAssetRequest(Request) -- Filter the requested cargo assets. local _assetstock=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval, Request.nasset) + -- No assets in stock :( if #_assetstock==0 then return nil,nil,nil end @@ -919,6 +1036,14 @@ function WAREHOUSE:_SpawnAssetRequest(Request) Parking=self:_GetParkingForAssets(_assetstock) end + -- Spawn aircraft in uncontrolled state if request comes from the same warehouse. + local UnControlled=false + local AIOnOff=true + if self.warehouse:GetName()==Request.warehouse.warehouse:GetName() then + UnControlled=true + AIOnOff=false + end + -- Create an empty set. local groupset=SET_GROUP:New():FilterDeads() @@ -940,7 +1065,7 @@ function WAREHOUSE:_SpawnAssetRequest(Request) -- Spawn object. Spawn with ALIAS here or DCS crashes! --local _spawn=SPAWN:NewFromTemplate(_assetitem.template,_assetitem.templatename,_alias):InitCoalition(self.coalition):InitCountry(self.country) - local _spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias):InitCoalition(self.coalition):InitCountry(self.country) + local _spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias):InitCoalition(self.coalition):InitCountry(self.country):InitUnControlled(UnControlled):InitAIOnOff(AIOnOff) local _group=nil --Wrapper.Group#GROUP local _attribute=_assetitem.attribute @@ -1039,7 +1164,7 @@ function WAREHOUSE:onafterDelivered(From, Event, To, groupset, request) -- Put assets in new warehouse. for _,_group in pairs(groupset:GetSetObjects()) do local group=_group --Wrapper.Group#GROUP - request.warehouse:AddAsset(group:GetTemplate(), 1) + request.warehouse:AddAsset(group:GetName(), 1) end end @@ -1092,7 +1217,7 @@ function WAREHOUSE:_RouteAir(Aircraft, ToAirbase, Speed) -- Nil check if Template==nil then - self:E("ERROR: Template nil") + self:E(self.wid.."ERROR: Template nil in RouteAir!") return end @@ -1127,7 +1252,7 @@ function WAREHOUSE:_RouteAir(Aircraft, ToAirbase, Speed) --[[ -- tasks local TaskCombo = {} - TaskCombo[#TaskCombo+1]=self:_SimpleTaskFunction("WAREHOUSE._Arrived2") + TaskCombo[#TaskCombo+1]=self:_SimpleTaskFunction("WAREHOUSE._ArrivedSimple") ToWaypoint.task = {} ToWaypoint.task.id = "ComboTask" @@ -1176,22 +1301,30 @@ function WAREHOUSE:_RouteTrain(Group, Coordinate, Speed) end end ---- Task function for last waypoint. Triggering the Delivered event. +--- Task function for last waypoint. Triggering the "Arrived" event. -- @param Wrapper.Group#GROUP group The group that arrived. -- @param #WAREHOUSE self function WAREHOUSE._Arrived(group, warehouse) env.info(warehouse.wid..string.format("Group %s arrived", tostring(group:GetName()))) + --Trigger delivered event. warehouse:__Arrived(1, group) + end ---- Task function for last waypoint. Triggering the Delivered event. +--- Simple task function for last waypoint. Triggering the "Arrived" event. -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group The group that arrived. -function WAREHOUSE:_Arrived2(group) - env.info(self.wid..string.format("Group %s arrived2", tostring(group:GetName()))) - --Trigger delivered event. - self:__Arrived(1, group) +function WAREHOUSE:_ArrivedSimple(group) + + if group then + --local self:_GetIDsFromGroup(group) + env.info(self.wid..string.format("Group %s arrived at warehouse ", tostring(group:GetName()))) + + --Trigger delivered event. + self:__Arrived(1, group) + end + end --- Arrived event if an air unit/group arrived at its destination. @@ -1207,6 +1340,59 @@ function WAREHOUSE:_ArrivedEvent(EventData) end +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Event handler functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Warehouse event handling function. +-- @param #WAREHOUSE self +-- @param Core.Event#EVENTDATA Eventdata Event data. +function WAREHOUSE:_OnEventBirth(EventData) + self:E(self.wid..string.format("Warehouse %s captured event birth!",self.warehouse:GetName())) +end + +--- Warehouse event handling function. +-- @param #WAREHOUSE self +-- @param Core.Event#EVENTDATA Eventdata Event data. +function WAREHOUSE:_OnEventEngineStartup(EventData) + self:E(self.wid..string.format("Warehouse %s captured event engine startup!",self.warehouse:GetName())) +end + +--- Warehouse event handling function. +-- @param #WAREHOUSE self +-- @param Core.Event#EVENTDATA Eventdata Event data. +function WAREHOUSE:_OnEventTakeOff(EventData) + self:E(self.wid..string.format("Warehouse %s captured event takeoff!",self.warehouse:GetName())) +end + +--- Warehouse event handling function. +-- @param #WAREHOUSE self +-- @param Core.Event#EVENTDATA Eventdata Event data. +function WAREHOUSE:_OnEventLanding(EventData) + self:E(self.wid..string.format("Warehouse %s captured event landing!",self.warehouse:GetName())) +end + +--- Warehouse event handling function. +-- @param #WAREHOUSE self +-- @param Core.Event#EVENTDATA Eventdata Event data. +function WAREHOUSE:_OnEventEngineShutdown(EventData) + self:E(self.wid..string.format("Warehouse %s captured event engine shutdown!",self.warehouse:GetName())) +end + +--- Warehouse event handling function. +-- @param #WAREHOUSE self +-- @param Core.Event#EVENTDATA Eventdata Event data. +function WAREHOUSE:_OnEventCrashOrDead(EventData) + self:E(self.wid..string.format("Warehouse %s captured event birth!",self.warehouse:GetName())) +end + +--- Warehouse event handling function. +-- @param #WAREHOUSE self +-- @param Core.Event#EVENTDATA Eventdata Event data. +function WAREHOUSE:_OnEventBaseCaptured(EventData) + self:E(self.wid..string.format("Warehouse %s captured event base captured!",self.warehouse:GetName())) +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Helper functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1214,14 +1400,15 @@ end --- Checks if the request can be fulfilled in general. If not, it is removed from the queue. -- Check if departure and destination bases are of the right type. -- @param #WAREHOUSE self +-- @param #table queue The queue which is holding the requests to check. -- @param #WAREHOUSE.Queueitem qitem The request to be checked. -- @return #boolean If true, request can be executed. If false, something is not right. -function WAREHOUSE:_CheckRequestValid() +function WAREHOUSE:_CheckRequestValid(queue) -- Requests to delete. local delid={} - for _,_request in pairs(self.queue) do + for _,_request in pairs(queue) do local request=_request --#WAREHOUSE.Queueitem local valid=true @@ -1400,6 +1587,7 @@ function WAREHOUSE:_CheckRequestValid() end + -- Add request as unvalid and delete it later. if not valid then table.insert(delid, request.id) end @@ -1498,20 +1686,6 @@ function WAREHOUSE:_CheckRequestNow(request) return okay end - ---- Creates a unique name for spawned assets. --- @param #WAREHOUSE self --- @param #WAREHOUSE.Stockitem _assetitem Asset for which the name is created. --- @param #WAREHOUSE.Queueitem _queueitem (Optional) Request specific name. --- @return #string Alias name "UnitType\_WID-%02d\_AID-%04d" -function WAREHOUSE:_Alias(_assetitem,_queueitem) - local _alias=string.format("%s_WID-%02d_AID-%04d", _assetitem.unittype, self.uid,_assetitem.uid) - if _queueitem then - _alias=_alias..string.format("_RID-%04d",_queueitem.uid) - end - return _alias -end - ---Sorts the queue and checks if the request can be fullfilled. -- @param #WAREHOUSE self -- @return #WAREHOUSE.Queueitem Chosen request. @@ -1546,7 +1720,7 @@ function WAREHOUSE:_SimpleTaskFunction(Function) -- Task script. local DCSScript = {} - DCSScript[#DCSScript+1] = string.format('env.info("FF hello simple task function") ') + DCSScript[#DCSScript+1] = string.format('env.info("WAREHOUSE: Simple task function called!") ') DCSScript[#DCSScript+1] = string.format('local mygroup = GROUP:Find( ... ) ') -- The group that executes the task function. Very handy with the "...". DCSScript[#DCSScript+1] = string.format('local mystatic=STATIC:FindByName(%s) ', warehouse) -- The static that holds the warehouse self object. DCSScript[#DCSScript+1] = string.format('local warehouse = mygroup:GetState(mystatic, "WAREHOUSE") ') -- Get the warehouse self object from the static. @@ -1651,7 +1825,7 @@ end function WAREHOUSE:_GetRequestOfGroup(group, queue) -- Get warehouse, asset and request ID from group name. - local wid,aid,rid=self:_GetInfoFromGroup(group) + local wid,aid,rid=self:_GetIDsFromGroup(group) -- Find the request. for _,_request in pairs(queue) do @@ -1663,10 +1837,41 @@ function WAREHOUSE:_GetRequestOfGroup(group, queue) end ---- Get warehouse id, asset id and request id from group name. +--- Creates a unique name for spawned assets. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE.Stockitem _assetitem Asset for which the name is created. +-- @param #WAREHOUSE.Queueitem _queueitem (Optional) Request specific name. +-- @return #string Alias name "UnitType\_WID-%d\_AID-%d\_RID-%d" +function WAREHOUSE:_Alias(_assetitem,_queueitem) + local _alias=string.format("%s_WID-%d_AID-%d", _assetitem.unittype, self.uid,_assetitem.uid) + if _queueitem then + _alias=_alias..string.format("_RID-%d",_queueitem.uid) + end + return _alias +end + +--- Creates a unique name for spawned assets. +-- @param #WAREHOUSE self +-- @param #string unittype Type of unit. +-- @param #number wid Warehouse id. +-- @param #number aid Asset item id. +-- @param #number qid Queue/request item id. +-- @return #string Alias name "UnitType\_WID-%d\_AID-%d\_RID-%d" +function WAREHOUSE:_alias(unittype, wid, aid, qid) + local _alias=string.format("%s_WID-%d_AID-%d", unittype, wid, aid) + if qid then + _alias=_alias..string.format("_RID-%d", qid) + end + return _alias +end + +--- Get warehouse id, asset id and request id from group name (alias). -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group The group from which the info is gathered. -function WAREHOUSE:_GetInfoFromGroup(group) +-- @return #number Warehouse ID. +-- @return #number Asset ID. +-- @return #number Request ID. +function WAREHOUSE:_GetIDsFromGroup(group) ---@param #string text The text to analyse. local function analyse(text) @@ -1676,7 +1881,6 @@ function WAREHOUSE:_GetInfoFromGroup(group) -- Split keywords. local keywords=UTILS.Split(unspawned, "_") - local _wid=nil -- warehouse UID local _aid=nil -- asset UID local _rid=nil -- request UID @@ -1698,14 +1902,19 @@ function WAREHOUSE:_GetInfoFromGroup(group) return _wid,_aid,_rid end + -- Group name local name=group:GetName() - env.info("FF Name = "..tostring(name)) - + + -- Get ids local wid,aid,rid=analyse(name) - env.info("FF warehouse id = %s"..tostring(wid)) - env.info("FF asset id = %s"..tostring(aid)) - env.info("FF request id = %s"..tostring(rid)) + -- Debug info + self:E(self.wid..string.format("Group Name = %s", tostring(name))) + self:E(self.wid..string.format("Warehouse ID = %s", tostring(wid))) + self:E(self.wid..string.format("Asset ID = %s", tostring(aid))) + self:E(self.wid..string.format("Request ID = %s", tostring(rid))) + + return wid,aid,rid end --- Filter stock assets by table entry. @@ -1918,6 +2127,7 @@ function WAREHOUSE:_PrintQueue(queue, name) env.info(text) end end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- From 7e4c1d8d3d9a214f765f8acd0d07e57f75455780 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Tue, 14 Aug 2018 16:01:45 +0200 Subject: [PATCH 254/420] Warehouse 0.1.7 --- Moose Development/Moose/Core/Event.lua | 10 + .../Moose/Functional/Warehouse.lua | 253 ++++++++++++++---- 2 files changed, 212 insertions(+), 51 deletions(-) diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index a1be00587..add9fc3d8 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -257,6 +257,10 @@ EVENTS = { -- @field DCS#Unit.Category TgtCategory (UNIT) The category of the target. -- @field #string TgtTypeName (UNIT) The type name of the target. -- +-- @field DCS#Airbase place The @{DCS#Airbase} +-- @field Wrapper.Airbase#AIRBASE Place The MOOSE airbase object. +-- @field #string PlaceName The name of the airbase. +-- -- @field weapon The weapon used during the event. -- @field Weapon -- @field WeaponName @@ -933,6 +937,12 @@ function EVENT:onEvent( Event ) Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName() --Event.WeaponTgtDCSUnit = Event.Weapon:getTarget() end + + -- Place should be given for takeoff and landing events as well as base captured. It should be a DCS airbase. + if Event.place then + Event.Place=AIRBASE:Find(Event.place) + Event.PlaceName=Event.Place:GetName() + end -- @FC: something like this should be added. --[[ diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index d819b2e1c..34e90698e 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -26,6 +26,7 @@ -- @field DCS#coalition.side coalition Coalition ID the warehouse belongs to. -- @field DCS#country.id country Country ID the warehouse belongs to. -- @field Wrapper.Airbase#AIRBASE airbase Airbase the warehouse belongs to. +-- @field #string airbasename Name of the airbase associated to the warehouse. -- @field DCS#Airbase.Category category Category of the home airbase, i.e. airdrome, helipad/farp or ship. -- @field Core.Point#COORDINATE coordinate Coordinate of the warehouse. -- @field Core.Point#COORDINATE road Closest point to warehouse on road. @@ -89,6 +90,7 @@ WAREHOUSE = { coalition = nil, country = nil, airbase = nil, + airbasename = nil, category = -1, coordinate = nil, road = nil, @@ -111,6 +113,7 @@ WAREHOUSE = { -- @field #table template The spawn template of the group. -- @field DCS#Group.Category category Category of the group. -- @field #string unittype Type of the first unit of the group as obtained by the Object.getTypeName() DCS API function. +-- @field #number nunits Number of units in the group. -- @field #number range Range of the unit in meters. -- @field #number speedmax Maximum speed in km/h the unit can do. -- @field #WAREHOUSE.Attribute attribute Generalized attribute of the group. @@ -127,6 +130,11 @@ WAREHOUSE = { -- @field #number nasset Number of asset groups requested. -- @field #WAREHOUSE.TransportType transporttype Transport unit type. -- @field #number ntransport Number of transport units requested. + +--- Item of the warehouse pending queue table. +-- @type WAREHOUSE.Pendingitem +-- @extends #WAREHOUSE.Queueitem +-- @field #table assetlist Table of assets to be delivered. Each element of the table is a @{#WAREHOUSE.Stockitem}. -- @field #number ndelivered Number of groups delivered to destination. Is managed automatically. -- @field #number ntransporthome Number of transports back home. Is managed automatically. -- @field Core.Set#SET_GROUP cargogroupset Set of cargo groups do be delivered. Is managed automatically. @@ -177,7 +185,7 @@ WAREHOUSE.TransportType = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.1.6" +WAREHOUSE.version="0.1.7" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -239,11 +247,13 @@ function WAREHOUSE:New(warehouse, spawnzone, airbase) if airbase then -- User requested. self.airbase=airbase + self.airbasename=self.airbase:GetName() else -- Closest of the same coalition but within a certain range. local _airbase=self.coordinate:GetClosestAirbase(nil, self.coalition) if _airbase and _airbase:GetCoordinate():Get2DDistance(self.coordinate) < 3000 then self.airbase=_airbase + self.airbasename=self.airbase:GetName() self.category=self.airbase:GetDesc().category end end @@ -269,19 +279,21 @@ function WAREHOUSE:New(warehouse, spawnzone, airbase) self:SetStartState("Stopped") -- Add FSM transitions. - self:AddTransition("Stopped", "Load", "Stopped") - self:AddTransition("Stopped", "Start", "Running") - self:AddTransition("Running", "Status", "*") - self:AddTransition("Paused", "Status", "*") - self:AddTransition("*", "AddAsset", "*") - self:AddTransition("*", "AddRequest", "*") - self:AddTransition("Running", "Request", "*") - self:AddTransition("*", "Arrived", "*") - self:AddTransition("*", "Delivered", "*") - self:AddTransition("Running", "Pause", "Paused") - self:AddTransition("Paused", "Unpause", "Running") - self:AddTransition("*", "Stop", "Stopped") - self:AddTransition("*", "Save", "*") + self:AddTransition("Stopped", "Load", "Stopped") + self:AddTransition("Stopped", "Start", "Running") + self:AddTransition("Running", "Status", "*") + self:AddTransition("Paused", "Status", "*") + self:AddTransition("*", "AddAsset", "*") + self:AddTransition("*", "AddRequest", "*") + self:AddTransition("Running", "Request", "*") + self:AddTransition("*", "Unloaded", "*") -- Cargo has been unloaded from the carrier. + self:AddTransition("*", "Arrived", "*") + self:AddTransition("*", "Delivered", "*") + self:AddTransition("*", "SelfDelivered", "*") + self:AddTransition("Running", "Pause", "Paused") + self:AddTransition("Paused", "Unpause", "Running") + self:AddTransition("*", "Stop", "Stopped") + self:AddTransition("*", "Save", "*") -- Pseudo Functions @@ -294,6 +306,34 @@ function WAREHOUSE:New(warehouse, spawnzone, airbase) -- @param #WAREHOUSE self -- @param #number delay Delay in seconds. + --- Triggers the FSM event "Stop". Stops the warehouse. + -- @function [parent=#WAREHOUSE] Stop + -- @param #WAREHOUSE self + + --- Triggers the FSM event "Stop" after a delay. Stops the warehouse. + -- @function [parent=#WAREHOUSE] __Stop + -- @param #WAREHOUSE self + -- @param #number delay Delay in seconds. + + --- Triggers the FSM event "Pause". Pauses the warehouse. + -- @function [parent=#WAREHOUSE] Pauses + -- @param #WAREHOUSE self + + --- Triggers the FSM event "Pause" after a delay. Pause the warehouse. + -- @function [parent=#WAREHOUSE] __Pause + -- @param #WAREHOUSE self + -- @param #number delay Delay in seconds. + + --- Triggers the FSM event "Unpause". Pauses the warehouse. + -- @function [parent=#WAREHOUSE] UnPause + -- @param #WAREHOUSE self + + --- Triggers the FSM event "Unpause" after a delay. Pause the warehouse. + -- @function [parent=#WAREHOUSE] __Unpause + -- @param #WAREHOUSE self + -- @param #number delay Delay in seconds. + + --- Triggers the FSM event "Status". Queue is updated and requests are executed. -- @function [parent=#WAREHOUSE] Status @@ -705,13 +745,18 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) ------------------------------------------------------------------------------------------------------------------------------------ -- Cargo assets. ------------------------------------------------------------------------------------------------------------------------------------ + + -- Pending request. + local Pending=Request --#WAREHOUSE.Pendingitem -- Spawn assets. - local _spawngroups,_cargotype,_cargocategory=self:_SpawnAssetRequest(Request) --Core.Set#SET_GROUP + local _spawngroups,_cargotype,_cargocategory,_cargoassets=self:_SpawnAssetRequest(Request) --Core.Set#SET_GROUP -- Add cargo groups to request. - Request.cargogroupset=_spawngroups - Request.ndelivered=0 + Pending.cargogroupset=_spawngroups + Pending.cargoassets=_cargoassets + --Request.cargogroupset=_spawngroups + --Request.ndelivered=0 -- Add groups to cargo if they don't go by themselfs. local CargoGroups --Core.Set#SET_CARGO @@ -729,8 +774,10 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) _loadradius=100 end + -- Empty cargo group set. CargoGroups = SET_CARGO:New() + -- Add cargo groups to set. for _i,_group in pairs(_spawngroups:GetSetObjects()) do local group=_group --Wrapper.Group#GROUP local _wid,_aid,_rid=self:_GetIDsFromGroup(group) @@ -744,6 +791,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Self request! Assets are only spawned but not routed or transported anywhere. if self.warehouse:GetName()==Request.warehouse.warehouse:GetName() then env.info("FF selfrequest!") + self:__SelfDelivered(_spawngroups) return end @@ -1050,6 +1098,7 @@ function WAREHOUSE:_SpawnAssetRequest(Request) -- Spawn the assets. local _delid={} local _spawngroups={} + local _assets={} -- Loop over cargo requests. for i=1,Request.nasset do @@ -1095,6 +1144,7 @@ function WAREHOUSE:_SpawnAssetRequest(Request) if _group then --_spawngroups[i]=_group groupset:AddGroup(_group) + table.insert(_assets, _assetitem) table.insert(_delid,_assetitem.uid) else self:E(self.wid.."ERROR: cargo asset could not be spawned!") @@ -1107,11 +1157,42 @@ function WAREHOUSE:_SpawnAssetRequest(Request) self:_DeleteStockItem(_id) end - return groupset,_cargotype,_cargocategory + return groupset,_cargotype,_cargocategory,_assets end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- On after "Unloaded" event. Triggered when a group was unloaded from the carrier. +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Wrapper.Group#GROUP group The group that was delivered. +function WAREHOUSE:onafterUnloaded(From, Event, To, group) + -- Debug info. + self:E(self.wid..string.format("Cargo %s unloaded!", tostring(group:GetName()))) + group:SmokeWhite() + + -- Get max speed of group. + local speedmax=group:GetSpeedMax() + + if group:IsGround() then + if speedmax>1 then + group:RouteGroundTo(self.spawnzone:GetRandomCoordinate(50), speedmax*0.5, AI.Task.VehicleFormation.RANK, 3) + else + -- Immobile ground unit ==> directly put it into the warehouse. + self:Arrived(group) + end + elseif group:IsAir() then + -- Not sure if air units will be allowed as cargo even though it might be possible. Best put them into warehouse immediately. + self:Arrived(group) + elseif group:IsShip() then + -- Not sure if naval units will be allowed as cargo even though it might be possible. Best put them into warehouse immediately. + self:Arrived(group) + end + +end + --- On after "Arrived" event. Triggered when a group has arrived at its destination. -- @param #WAREHOUSE self -- @param #string From From state. @@ -1124,40 +1205,58 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) group:SmokeOrange() -- Update pending request. - self:_UpdatePending(group) + local request=self:_UpdatePending(group) + + -- All cargo delivered. + if request and request.cargogroupset:Count()==0 then + self:__Delivered(5, request.cargogroupset, request) + end end --- Update the pending requests by removing assets that have arrived. -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group The group that has arrived at its destination. +-- @return #WAREHOUSE.Pendingitem The updated request from the pending queue. function WAREHOUSE:_UpdatePending(group) -- Get request from group name. local request=self:_GetRequestOfGroup(group, self.pending) - - -- Increase number of delivered assets. - request.ndelivered=request.ndelivered+1 - -- Number of alive groups. - local nalive=request.cargogroupset:Count() + -- Get the IDs for this group. In particular, we use the asset ID to figure out which group was delivered. + local wid,aid,rid=self:_GetIDsFromGroup(group) - -- TODO: Well this does not handle the case when a group that has been delivered was killed! - if request.ndelivered>=nalive then - self:__Delivered(5, request.cargogroupset, request) + if request then + + -- Loop over cargo groups. + for _,_cargogroup in pairs(request.cargogroupset) do + local cargogroup=_cargogroup --Wrapper.Group#GROUP + + -- IDs of cargo group. + local cwid,caid,crid=self:_GetIDsFromGroup(cargogroup) + + -- Remove group from cargo group set. + if caid==aid then + request.cargogroupset:Remove(cargogroup:GetName()) + request.ndelivered=request.ndelivered+1 + break + end + end + else + self:E(self.wid..string.format("ERROR: pending request could not be updated since request did not exist in pending queue!")) end + + return request end - --- On after "Delivered" event. -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param Core.Set#SET_GROUP groupset The set of cargo groups that was delivered. --- @param #WAREHOUSE.Queueitem request -function WAREHOUSE:onafterDelivered(From, Event, To, groupset, request) +-- @param #WAREHOUSE.Pendingitem request +function WAREHOUSE:onafterDelivered(From, Event, To, request) env.info("FF all assets delivered!") @@ -1169,6 +1268,27 @@ function WAREHOUSE:onafterDelivered(From, Event, To, groupset, request) end +--- On after "SelfDelivered" event. Request was initiated to the warehouse itself. Groups are just spawned at the warehouse or the associated airbase. +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Core.Set#SET_GROUP groupset The set of cargo groups that was delivered. +-- @param #WAREHOUSE.Pendingitem request +function WAREHOUSE:onafterSelfDelivered(From, Event, To, groupset, request) + + env.info("FF all assets delivered!") + + -- Put assets in new warehouse. + for _,_group in pairs(groupset:GetSetObjects()) do + local group=_group --Wrapper.Group#GROUP + group:SmokeGreen() + end + + --TODO: delete pending queue item. + +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Routing functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1249,27 +1369,16 @@ function WAREHOUSE:_RouteAir(Aircraft, ToAirbase, Speed) ToWaypoint.linkUnit = nil end - --[[ - -- tasks - local TaskCombo = {} - TaskCombo[#TaskCombo+1]=self:_SimpleTaskFunction("WAREHOUSE._ArrivedSimple") - - ToWaypoint.task = {} - ToWaypoint.task.id = "ComboTask" - ToWaypoint.task.params = {} - ToWaypoint.task.params.tasks = TaskCombo - ]] - -- Second point of the route. First point is done in RespawnAtCurrentAirbase() routine. Template.route.points[2] = ToWaypoint -- Respawn group at the current airbase. env.info("FF Respawn at current airbase group = "..Aircraft:GetName().." name before") - local bla=Aircraft:RespawnAtCurrentAirbase(Template, Takeoff, false) - env.info("FF Respawn at current airbase group = "..bla:GetName().." name after") + local newAC=Aircraft:RespawnAtCurrentAirbase(Template, Takeoff, false) + env.info("FF Respawn at current airbase group = "..newAC:GetName().." name after") -- Handle event engine shutdown and trigger delivered event. - bla:HandleEvent(EVENTS.EngineShutdown, self._ArrivedEvent) + newAC:HandleEvent(EVENTS.EngineShutdown, self._OnEventArrived) else self:E(string.format("ERROR: aircraft %s cannot be routed since it does not exist or is not alive %s!", tostring(Aircraft:GetName()), tostring(Aircraft:IsAlive()))) end @@ -1327,10 +1436,14 @@ function WAREHOUSE:_ArrivedSimple(group) end +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Event handler functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + --- Arrived event if an air unit/group arrived at its destination. -- @param #WAREHOUSE self -- @param Core.Event#EVENTDATA EventData Event data table. -function WAREHOUSE:_ArrivedEvent(EventData) +function WAREHOUSE:_OnEventArrived(EventData) local unit=EventData.IniUnit unit:SmokeBlue() @@ -1340,15 +1453,18 @@ function WAREHOUSE:_ArrivedEvent(EventData) end -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Event handler functions -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - --- Warehouse event handling function. -- @param #WAREHOUSE self -- @param Core.Event#EVENTDATA Eventdata Event data. function WAREHOUSE:_OnEventBirth(EventData) self:E(self.wid..string.format("Warehouse %s captured event birth!",self.warehouse:GetName())) + + if EventData and EventData.id==world.event.S_EVENT_BIRTH then + if EventData.IniGroup then + local group=EventData.IniGroup + + end + end end --- Warehouse event handling function. @@ -1391,6 +1507,41 @@ end -- @param Core.Event#EVENTDATA Eventdata Event data. function WAREHOUSE:_OnEventBaseCaptured(EventData) self:E(self.wid..string.format("Warehouse %s captured event base captured!",self.warehouse:GetName())) + + -- This warehouse does not have an airbase and never had one. So i could not be captured. + if self.airbasename==nil then + return + end + + if EventData and EventData.id==world.event.S_EVENT_BASE_CAPTURED then + if EventData.Place then + + local airbase=EventData.Place --Wrapper.Airbase#AIRBASE + + if EventData.PlaceName==self.airbasename then + -- Okay, this airbase belongs or did belong to this warehouse. + + -- New coalition of airbase after it was captured. + local coalitionAirbase=airbase:GetCoalition() + + -- what can happen? + -- warehouse is blue, airbase is blue and belongs to warehouse and red captures it. ==> self.airbase=nil + -- warehouse is blue, airbase is blue self.airbase is nil and blue (re-)captures it ==> self.airbase=Event.Place + if self.airbase==nil then + -- Warehouse lost this airbase previously and not it was re-captured. + if coalitionAirbase == self.coalition then + self.airbase=airbase + end + else + -- Captured airbase belongs to this warehouse but was captured by other coaltion. + if coalitionAirbase ~= self.coalition then + self.airbase=nil + end + end + + end + end + end end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1821,7 +1972,7 @@ end -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group The group from which the info is gathered. -- @param #table queue Queue holding all requests. --- @return #WAREHOUSE.Queueitem The request belonging to this group. +-- @return #WAREHOUSE.Pendingitem The request belonging to this group. function WAREHOUSE:_GetRequestOfGroup(group, queue) -- Get warehouse, asset and request ID from group name. From e4f8b5afc3586e3c8b2b69159e75ac510a5e2355 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 14 Aug 2018 21:04:11 +0200 Subject: [PATCH 255/420] Working plane cargo AI dispatcher, but still need to work on the collisions on the runway etc of moving vehicles with planes... --- Moose Development/Moose/Cargo/CargoGroup.lua | 4 +-- Moose Development/Moose/Core/Database.lua | 35 +++++++++++--------- Moose Development/Moose/Core/Event.lua | 2 +- Moose Development/Moose/Wrapper/Group.lua | 14 ++++---- 4 files changed, 31 insertions(+), 24 deletions(-) diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 72a3430cf..b51e5502c 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -89,7 +89,7 @@ do -- CARGO_GROUP for UnitID, UnitTemplate in pairs( self.CargoTemplate.units ) do UnitTemplate.name = UnitTemplate.name .. "#CARGO" - local CargoUnitName = UnitTemplate.name + local CargoUnitName = UnitTemplate.name self.CargoUnitTemplate[CargoUnitName] = UnitTemplate GroupTemplate.units[#GroupTemplate.units+1] = self.CargoUnitTemplate[CargoUnitName] @@ -101,7 +101,7 @@ do -- CARGO_GROUP end -- Then we register the new group in the database - self.CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID) + GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID ) -- Now we spawn the new group based on the template created. self.CargoObject = _DATABASE:Spawn( GroupTemplate ) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 3b0fb0337..59e4676e4 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -374,14 +374,15 @@ do -- cargo -- @return #DATABASE self function DATABASE:_RegisterCargos() + local Groups = UTILS.DeepCopy( self.GROUPS ) -- This is a very important statement. CARGO_GROUP:New creates a new _DATABASE.GROUP entry, which will confuse the loop. I searched 4 hours on this to find the bug! - for CargoGroupName, CargoGroup in pairs( self.GROUPS ) do + for CargoGroupName, CargoGroup in pairs( Groups ) do + self:I( { Cargo = CargoGroupName } ) if self:IsCargo( CargoGroupName ) then local CargoInfo = CargoGroupName:match("~CARGO(.*)") local CargoParam = CargoInfo and CargoInfo:match( "%((.*)%)") local CargoName1 = CargoGroupName:match("(.*)~CARGO%(.*%)") local CargoName2 = CargoGroupName:match(".*~CARGO%(.*%)(.*)") - self:E({CargoName1 = CargoName1, CargoName2 = CargoName2 }) local CargoName = CargoName1 .. ( CargoName2 or "" ) local Type = CargoParam and CargoParam:match( "T=([%a%d ]+),?") local Name = CargoParam and CargoParam:match( "N=([%a%d]+),?") or CargoName @@ -459,7 +460,7 @@ end function DATABASE:AddGroup( GroupName ) if not self.GROUPS[GroupName] then - self:E( { "Add GROUP:", GroupName } ) + self:I( { "Add GROUP:", GroupName } ) self.GROUPS[GroupName] = GROUP:Register( GroupName ) end @@ -471,7 +472,7 @@ end function DATABASE:AddPlayer( UnitName, PlayerName ) if PlayerName then - self:E( { "Add player for unit:", UnitName, PlayerName } ) + self:I( { "Add player for unit:", UnitName, PlayerName } ) self.PLAYERS[PlayerName] = UnitName self.PLAYERUNITS[PlayerName] = self:FindUnit( UnitName ) self.PLAYERSJOINED[PlayerName] = PlayerName @@ -483,7 +484,7 @@ end function DATABASE:DeletePlayer( UnitName, PlayerName ) if PlayerName then - self:E( { "Clean player:", PlayerName } ) + self:I( { "Clean player:", PlayerName } ) self.PLAYERS[PlayerName] = nil self.PLAYERUNITS[PlayerName] = nil end @@ -750,7 +751,7 @@ function DATABASE:_RegisterPlayers() local UnitName = UnitData:getName() local PlayerName = UnitData:getPlayerName() if not self.PLAYERS[PlayerName] then - self:E( { "Add player for unit:", UnitName, PlayerName } ) + self:I( { "Add player for unit:", UnitName, PlayerName } ) self:AddPlayer( UnitName, PlayerName ) end end @@ -773,13 +774,13 @@ function DATABASE:_RegisterGroupsAndUnits() if DCSGroup:isExist() then local DCSGroupName = DCSGroup:getName() - self:E( { "Register Group:", DCSGroupName } ) + self:I( { "Register Group:", DCSGroupName } ) self:AddGroup( DCSGroupName ) for DCSUnitId, DCSUnit in pairs( DCSGroup:getUnits() ) do local DCSUnitName = DCSUnit:getName() - self:E( { "Register Unit:", DCSUnitName } ) + self:I( { "Register Unit:", DCSUnitName } ) self:AddUnit( DCSUnitName ) end else @@ -788,6 +789,11 @@ function DATABASE:_RegisterGroupsAndUnits() end end + + self:I("Groups:") + for GroupName, Group in pairs( self.GROUPS ) do + self:I( { "Group:", GroupName } ) + end return self end @@ -798,7 +804,7 @@ end function DATABASE:_RegisterClients() for ClientName, ClientTemplate in pairs( self.Templates.ClientsByName ) do - self:E( { "Register Client:", ClientName } ) + self:I( { "Register Client:", ClientName } ) self:AddClient( ClientName ) end @@ -809,14 +815,14 @@ end function DATABASE:_RegisterStatics() local CoalitionsData = { GroupsRed = coalition.getStaticObjects( coalition.side.RED ), GroupsBlue = coalition.getStaticObjects( coalition.side.BLUE ) } - self:E( { Statics = CoalitionsData } ) + self:I( { Statics = CoalitionsData } ) for CoalitionId, CoalitionData in pairs( CoalitionsData ) do for DCSStaticId, DCSStatic in pairs( CoalitionData ) do if DCSStatic:isExist() then local DCSStaticName = DCSStatic:getName() - self:E( { "Register Static:", DCSStaticName } ) + self:I( { "Register Static:", DCSStaticName } ) self:AddStatic( DCSStaticName ) else self:E( { "Static does not exist: ", DCSStatic } ) @@ -836,7 +842,7 @@ function DATABASE:_RegisterAirbases() local DCSAirbaseName = DCSAirbase:getName() - self:E( { "Register Airbase:", DCSAirbaseName, DCSAirbase:getID() } ) + self:I( { "Register Airbase:", DCSAirbaseName, DCSAirbase:getID() } ) self:AddAirbase( DCSAirbaseName ) end end @@ -866,9 +872,8 @@ function DATABASE:_EventOnBirth( Event ) Event.IniUnit = self:FindUnit( Event.IniDCSUnitName ) Event.IniGroup = self:FindGroup( Event.IniDCSGroupName ) local PlayerName = Event.IniUnit:GetPlayerName() - self:E( { "PlayerName:", PlayerName } ) if PlayerName then - self:E( { "Player Joined:", PlayerName } ) + self:I( { "Player Joined:", PlayerName } ) if not self.PLAYERS[PlayerName] then self:AddPlayer( Event.IniUnitName, PlayerName ) end @@ -937,7 +942,7 @@ function DATABASE:_EventOnPlayerLeaveUnit( Event ) if Event.IniObjectCategory == 1 then local PlayerName = Event.IniUnit:GetPlayerName() if PlayerName and self.PLAYERS[PlayerName] then - self:E( { "Player Left:", PlayerName } ) + self:I( { "Player Left:", PlayerName } ) local Settings = SETTINGS:Set( PlayerName ) Settings:RemovePlayerMenu( Event.IniUnit ) self:DeletePlayer( Event.IniUnit, PlayerName ) diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index a1be00587..c7f937c8b 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -719,7 +719,7 @@ do -- Event Creation -- @param #EVENT self -- @param AI.AI_Cargo#AI_CARGO Cargo The Cargo created. function EVENT:CreateEventNewCargo( Cargo ) - self:F( { Cargo } ) + self:I( { Cargo } ) local Event = { id = EVENTS.NewCargo, diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 1cf57eb77..81d26911e 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -121,13 +121,16 @@ GROUPTEMPLATE.Takeoff = { -- @return #GROUP self function GROUP:NewTemplate( GroupTemplate, CoalitionSide, CategoryID, CountryID ) local GroupName = GroupTemplate.name + _DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, CategoryID, CountryID, GroupName ) - self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) - self:F2( GroupName ) + + local self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) self.GroupName = GroupName - - _DATABASE:AddGroup( GroupName ) - + + if not _DATABASE.GROUPS[GroupName] then + _DATABASE.GROUPS[GroupName] = self + end + self:SetEventPriority( 4 ) return self end @@ -140,7 +143,6 @@ end -- @return #GROUP self function GROUP:Register( GroupName ) local self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) ) -- #GROUP - self:F( GroupName ) self.GroupName = GroupName self:SetEventPriority( 4 ) From 965012976933c4f0aa7856fb7bdf724c43c532fa Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 14 Aug 2018 22:40:28 +0200 Subject: [PATCH 256/420] AI helicopter dispatcher working to load multiple infantry groups ! --- .../Moose/AI/AI_Cargo_Airplane.lua | 11 ++- .../Moose/AI/AI_Cargo_Dispatcher.lua | 2 +- .../Moose/AI/AI_Cargo_Helicopter.lua | 85 ++++++++++++------- Moose Development/Moose/Cargo/CargoGroup.lua | 2 +- .../Moose/Wrapper/Positionable.lua | 11 +++ 5 files changed, 71 insertions(+), 40 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 8cacb48ea..638211643 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -129,10 +129,7 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) self:SetCarrier( Airplane ) local Desc = Airplane:GetUnit(1):GetDesc() - self:F({Desc=Desc}) - - Airplane:SetCargoBayWeightLimit( Desc.massMax - ( Desc.massEmpty + Desc.fuelMassMax ) ) --Airplane:SetCargoBayVolumeLimit( 15 ) @@ -251,12 +248,15 @@ function AI_CARGO_AIRPLANE:onafterLanded( Airplane, From, Event, To ) env.info("FF load airplane "..Airplane:GetName()) self:Load( Airplane:GetCoordinate() ) self.RoutePickup = false + self.Relocating = true end -- Aircraft was send to this airbase to deploy troops. Initiate unloading. if self.RouteDeploy == true then self:Unload() self.RouteDeploy = false + self.Transporting = false + self.Relocating = false end end @@ -451,10 +451,9 @@ function AI_CARGO_AIRPLANE:onafterLoaded( Airplane, From, Event, To, Cargo ) env.info("FF troops loaded into cargo plane") if Airplane and Airplane:IsAlive() then - self:F( { "Transporting" } ) - self.Transporting = true -- This will only be executed when there is no cargo boarded anymore. The dispatcher will then kick-off the deploy cycle! + self:F( { "Transporting" } ) + self.Transporting = true -- This will only be executed when there is no cargo boarded anymore. The dispatcher will then kick-off the deploy cycle! end - end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index c19d760a8..933556713 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -404,7 +404,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() -- The Pickup sequence ... -- Check if this Carrier need to go and Pickup something... -- So, if the cargo bay is not full yet with cargo to be loaded ... - self:I( { IsRelocating = AI_Cargo:IsRelocating() } ) + self:I( { IsRelocating = AI_Cargo:IsRelocating(), IsTransporting = AI_Cargo:IsTransporting() } ) if AI_Cargo:IsRelocating() == false and AI_Cargo:IsTransporting() == false then -- ok, so there is a free Carrier -- now find the first cargo that is Unloaded diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 1e3141649..faa85affc 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -43,7 +43,7 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) self:AddTransition( "Unloaded", "Pickup", "*" ) self:AddTransition( "Loaded", "Deploy", "*" ) - self:AddTransition( "Unloaded", "Load", "Boarding" ) + self:AddTransition( { "Unloaded", "Loading" }, "Load", "Boarding" ) self:AddTransition( "Boarding", "Board", "Boarding" ) self:AddTransition( "Boarding", "Loaded", "Loaded" ) self:AddTransition( "Loaded", "Unload", "Unboarding" ) @@ -144,6 +144,16 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) end ) + for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do + local Desc = HelicopterUnit:GetDesc() + self:F({Desc=Desc}) + HelicopterUnit:SetCargoBayWeightLimit( Desc.massMax - ( Desc.massEmpty + Desc.fuelMassMax ) ) + --Airplane:SetCargoBayVolumeLimit( 15 ) + end + + self.Relocating = false + self.Transporting = false + self:SetCarrier( Helicopter ) return self @@ -380,7 +390,6 @@ function AI_CARGO_HELICOPTER:onbeforeLoad( Helicopter, From, Event, To) self.BoardingCount = 0 if Helicopter and Helicopter:IsAlive() then - self.Helicopter_Cargo = {} for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT for _, Cargo in pairs( self.CargoSet:GetSet() ) do @@ -393,10 +402,6 @@ function AI_CARGO_HELICOPTER:onbeforeLoad( Helicopter, From, Event, To) Cargo:Board( HelicopterUnit, 25 ) self:__Board( 1, Cargo ) Boarding = true - - -- So now this APCUnit has Cargo that is being loaded. - -- This will be used further in the logic to follow and to check cargo status. - self.Helicopter_Cargo[HelicopterUnit] = Cargo break end end @@ -424,7 +429,27 @@ function AI_CARGO_HELICOPTER:onafterBoard( Helicopter, From, Event, To, Cargo ) if not Cargo:IsLoaded() then self:__Board( 10, Cargo ) else - self:__Loaded( 1, Cargo ) + for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do + local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT + for _, Cargo in pairs( self.CargoSet:GetSet() ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + if Cargo:IsUnLoaded() then + if Cargo:IsInLoadRadius( HelicopterUnit:GetCoordinate() ) then + local CargoBayFreeWeight = HelicopterUnit:GetCargoBayFreeWeight() + local CargoWeight = Cargo:GetWeight() + + -- Only when there is space within the bay to load the next cargo item! + if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then + + Cargo:Board( HelicopterUnit, 25 ) + self:__Board( 10, Cargo ) + return + end + end + end + end + end + self:__Loaded( 1, Cargo ) -- Will only be executed when no more cargo is boarded. end end @@ -438,24 +463,12 @@ end -- @param #string To To state. -- @param Cargo.Cargo#CARGO Cargo Cargo object. -- @return #boolean Cargo is loaded. -function AI_CARGO_HELICOPTER:onbeforeLoaded( Helicopter, From, Event, To, Cargo ) +function AI_CARGO_HELICOPTER:onafterLoaded( Helicopter, From, Event, To, Cargo ) self:F( { Helicopter, From, Event, To, Cargo } ) - local Loaded = true - if Helicopter and Helicopter:IsAlive() then - for HelicopterUnit, Cargo in pairs( self.Helicopter_Cargo ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - self:F( { IsLoaded = Cargo:IsLoaded(), IsDestroyed = Cargo:IsDestroyed() } ) - if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then - Loaded = false - end - end - + self.Transporting = true end - - return Loaded - end @@ -471,9 +484,11 @@ function AI_CARGO_HELICOPTER:onafterUnload( Helicopter, From, Event, To, Deploye for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT for _, Cargo in pairs( HelicopterUnit:GetCargo() ) do - Cargo:UnBoard() - Cargo:SetDeployed( true ) - self:__Unboard( 10, Cargo, Deployed ) + if Cargo:IsLoaded() then + Cargo:UnBoard() + Cargo:SetDeployed( true ) + self:__Unboard( 10, Cargo, Deployed ) + end end end end @@ -495,6 +510,17 @@ function AI_CARGO_HELICOPTER:onafterUnboard( Helicopter, From, Event, To, Cargo, if not Cargo:IsUnLoaded() then self:__Unboard( 10, Cargo, Deployed ) else + for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do + local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT + for _, Cargo in pairs( HelicopterUnit:GetCargo() ) do + if Cargo:IsLoaded() then + Cargo:UnBoard() + Cargo:SetDeployed( true ) + self:__Unboard( 10, Cargo, Deployed ) + return + end + end + end self:__Unloaded( 1, Cargo, Deployed ) end end @@ -519,21 +545,16 @@ function AI_CARGO_HELICOPTER:onbeforeUnloaded( Helicopter, From, Event, To, Carg if Helicopter and Helicopter:IsAlive() then for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do - local CargoCheck = self.Helicopter_Cargo[HelicopterUnit] -- Cargo.Cargo#CARGO - if CargoCheck then - self:F( { CargoCheck:GetName(), IsUnLoaded = CargoCheck:IsUnLoaded() } ) - if CargoCheck:IsUnLoaded() == false then + local IsEmpty = HelicopterUnit:IsCargoEmpty() + self:I({ IsEmpty = IsEmpty }) + if not IsEmpty then AllUnloaded = false break - end end end if AllUnloaded == true then if Deployed == true then - for HelicopterUnit, Cargo in pairs( self.Helicopter_Cargo ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - end self.Helicopter_Cargo = {} end self.Helicopter = Helicopter diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index b51e5502c..9a0294855 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -101,7 +101,7 @@ do -- CARGO_GROUP end -- Then we register the new group in the database - GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID ) + self.CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID ) -- Now we spawn the new group based on the template created. self.CargoObject = _DATABASE:Spawn( GroupTemplate ) diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 1de5c21fe..017c5f70f 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -840,6 +840,17 @@ do -- Cargo self.__.Cargo = {} end + --- Is cargo bay empty. + -- @param #POSITIONABLE self + function POSITIONABLE:IsCargoEmpty() + local IsEmpty = true + for _, Cargo in pairs( self.__.Cargo ) do + IsEmpty = false + break + end + return IsEmpty + end + --- Get cargo item count. -- @param #POSITIONABLE self -- @return Core.Cargo#CARGO Cargo From 7a5aa3a4f94102519c02bc63b8a37d80f0183494 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 15 Aug 2018 00:13:33 +0200 Subject: [PATCH 257/420] bla --- .../Moose/Functional/Warehouse.lua | 29 ++++++++++++------- Moose Development/Moose/Wrapper/Group.lua | 27 +++++++++++------ 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 34e90698e..b9b5ac0cb 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -2053,19 +2053,26 @@ function WAREHOUSE:_GetIDsFromGroup(group) return _wid,_aid,_rid end - -- Group name - local name=group:GetName() - - -- Get ids - local wid,aid,rid=analyse(name) + if group then - -- Debug info - self:E(self.wid..string.format("Group Name = %s", tostring(name))) - self:E(self.wid..string.format("Warehouse ID = %s", tostring(wid))) - self:E(self.wid..string.format("Asset ID = %s", tostring(aid))) - self:E(self.wid..string.format("Request ID = %s", tostring(rid))) + -- Group name + local name=group:GetName() + + -- Get ids + local wid,aid,rid=analyse(name) - return wid,aid,rid + -- Debug info + self:E(self.wid..string.format("Group Name = %s", tostring(name))) + self:E(self.wid..string.format("Warehouse ID = %s", tostring(wid))) + self:E(self.wid..string.format("Asset ID = %s", tostring(aid))) + self:E(self.wid..string.format("Request ID = %s", tostring(rid))) + + return wid,aid,rid + else + self:E("WARNING: Group not found in GetIDsFromGroup() function!") + end + + end --- Filter stock assets by table entry. diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index f78f9ef04..96b44357d 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1460,9 +1460,11 @@ function GROUP:RespawnAtCurrentAirbase(SpawnTemplate, Takeoff, Uncontrolled) -- -- Get the units of the group. local units=self:GetUnits() - for UnitID,_unit in pairs(units) do + local x + local y + for UnitID=1,#units do - local unit=_unit --Wrapper.Unit#UNIT + local unit=units[UnitID] --Wrapper.Unit#UNIT -- Get closest parking spot of current unit. Note that we look for occupied spots since the unit is currently sitting on it! local Parkingspot, TermialID, Distance=unit:GetCoordinate():GetClosestParkingSpot(airbase) @@ -1472,20 +1474,27 @@ function GROUP:RespawnAtCurrentAirbase(SpawnTemplate, Takeoff, Uncontrolled) -- -- Get unit coordinates for respawning position. local uc=unit:GetCoordinate() - SpawnTemplate.units[UnitID].x = Parkingspot.x - SpawnTemplate.units[UnitID].y = Parkingspot.z - SpawnTemplate.units[UnitID].alt = Parkingspot.y + + + SpawnTemplate.units[UnitID].x = uc.x --Parkingspot.x + SpawnTemplate.units[UnitID].y = uc.z --Parkingspot.z + SpawnTemplate.units[UnitID].alt = uc.y --Parkingspot.y SpawnTemplate.units[UnitID].parking = TermialID SpawnTemplate.units[UnitID].parking_id = nil + + if UnitID==1 then + x=uc.x + y=uc.z + end end - SpawnPoint.x = AirbaseCoord.x - SpawnPoint.y = AirbaseCoord.z + SpawnPoint.x = x --AirbaseCoord.x + SpawnPoint.y = y --AirbaseCoord.z - SpawnTemplate.x = AirbaseCoord.x - SpawnTemplate.y = AirbaseCoord.z + SpawnTemplate.x = x --AirbaseCoord.x + SpawnTemplate.y = y --AirbaseCoord.z -- Set uncontrolled state. SpawnTemplate.uncontrolled=Uncontrolled From 7599459779d19e3271ef06961ae7145c6098ac6a Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 15 Aug 2018 15:58:20 +0200 Subject: [PATCH 258/420] Warehouse 0.1.7w --- .../Moose/Functional/Warehouse.lua | 183 ++++++++++++------ 1 file changed, 122 insertions(+), 61 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index b9b5ac0cb..7d08c18b2 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -4,9 +4,9 @@ -- Features: -- -- * Holds (virtual) assests such as intrantry groups in stock. --- * Manages requests of assets from other airbases or warehouses. --- * Take care of transportation to other airbases. --- * Different means of automatic transportation (planes, helicopters, selfpropelled). +-- * Manages requests of assets from other warehouses. +-- * Take care of transportation to other warehouses and its accociated airbases. +-- * Different means of automatic transportation (planes, helicopters, APCs, selfpropelled). -- -- # QUICK START GUIDE -- @@ -32,6 +32,7 @@ -- @field Core.Point#COORDINATE road Closest point to warehouse on road. -- @field Core.Point#COORDINATE rail Closest point to warehouse on rail. -- @field Core.Zone#ZONE spawnzone Zone in which assets are spawned. +-- @field Functional.ZoneCaptureCoalition#ZONE_CAPTURE_COALITION capturezone Zone capture object handling the capturing of the warehouse spawn zone. -- @field #string wid Identifier of the warehouse printed before other output to DCS.log file. -- @field #number uid Unit identifier of the warehouse. Derived from the associated airbase. -- @field #number markerid ID of the warehouse marker at the airbase. @@ -96,6 +97,7 @@ WAREHOUSE = { road = nil, rail = nil, spawnzone = nil, + capturezone = nil, wid = nil, uid = nil, markerid = nil, @@ -458,6 +460,23 @@ function WAREHOUSE:onafterStart(From, Event, To) -- Debug mark spawn zone. self.spawnzone:BoundZone(60, self.country) self.spawnzone:GetCoordinate():MarkToAll("Spawnzone of warehouse "..self.warehouse:GetName()) + + -- Create a zone capture object. + self.capturezone=ZONE_CAPTURE_COALITION:New(self.spawnzone, self.coalition) + + -- Add warehouse to zone capture object. Does this work? + self.capturezone.warehouse=self + + -- Start capturing monitoring. + self.capturezone:Start(10, 60) + + -- Handle capturing. + function self.capturezone:OnEnterCaptured() + local coalition = self:GetCoalition() + self:E(string.format("Warehouse %s was captured by coalition %d", tostring(self.warehouse:GetName()), coalition)) + self.warehouse.coalition=coalition --:SetCoalition(coalition) + self:Guard() + end -- Handle events: self:HandleEvent(EVENTS.Birth, self._OnEventBirth) @@ -469,6 +488,7 @@ function WAREHOUSE:onafterStart(From, Event, To) self:HandleEvent(EVENTS.Dead, self._OnEventCrashOrDead) self:HandleEvent(EVENTS.BaseCaptured, self._OnEventBaseCaptured) + -- Start the status monitoring. self:__Status(5) end @@ -550,6 +570,14 @@ function WAREHOUSE:onafterStatus(From, Event, To) self:_PrintQueue(self.queue, "Queue:") self:_PrintQueue(self.pending, "Pending:") + -- Check if requests are valid and remove invalid one. + self:_CheckRequestConsistancy(self.queue) + + -- Print queue. + self:_PrintQueue(self.queue, "Queue after consitancy:") + self:_PrintQueue(self.pending, "Pending after consistancy:") + + -- Check queue and handle requests if possible. local request=self:_CheckQueue() @@ -559,8 +587,8 @@ function WAREHOUSE:onafterStatus(From, Event, To) end -- Print queue. - self:_PrintQueue(self.queue, "Queue2:") - self:_PrintQueue(self.pending, "Pending2:") + self:_PrintQueue(self.queue, "Queue after request:") + self:_PrintQueue(self.pending, "Pending after request:") -- Call status again in 30 sec. self:__Status(30) @@ -750,13 +778,17 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) local Pending=Request --#WAREHOUSE.Pendingitem -- Spawn assets. - local _spawngroups,_cargotype,_cargocategory,_cargoassets=self:_SpawnAssetRequest(Request) --Core.Set#SET_GROUP + local _spawngroups,_cargoassets=self:_SpawnAssetRequest(Request) --Core.Set#SET_GROUP + + -- General type and category. + local _cargotype=_cargoassets[1].attribute --#WAREHOUSE.Attribute + local _cargocategory=_cargoassets[1].category --DCS#Group.Category -- Add cargo groups to request. Pending.cargogroupset=_spawngroups Pending.cargoassets=_cargoassets - --Request.cargogroupset=_spawngroups - --Request.ndelivered=0 + Pending.cargoattribute=_cargotype + Pending.cargocategory=_cargocategory -- Add groups to cargo if they don't go by themselfs. local CargoGroups --Core.Set#SET_CARGO @@ -832,10 +864,10 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) end -- Add request to pending queue. - table.insert(self.pending, Request) + table.insert(self.pending, Pending) -- Delete request from queue. - self:_DeleteQueueItem(Request.uid) + self:_DeleteQueueItem(Request, self.queue) -- No cargo transport necessary. return @@ -868,12 +900,14 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) if _transportcategory==Group.Category.AIRPLANE or _transportcategory==Group.Category.HELICOPTER then Parking=self:_GetParkingForAssets(_assetstock) end + + -- Transport assets table. + local _transportassets={} -- Dependent on transport type, spawn the transports and set up the dispatchers. if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then - -- Spawn the transport groups. - local _delid={} + -- Spawn the transport groups. for i=1,Request.ntransport do -- Get stock item. @@ -897,13 +931,13 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Add group to transportset. TransportSet:AddGroup(spawngroup) - table.insert(_delid,_assetitem.uid) + table.insert(_transportassets,_assetitem) end end -- Delete spawned items from warehouse stock. - for _,_id in pairs(_delid) do - self:_DeleteStockItem(_id) + for _,_item in pairs(_transportassets) do + self:_DeleteStockItem(_item) end -- Define dispatcher for this task. @@ -912,7 +946,6 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then -- Spawn the transport groups. - local _delid={} for i=1,Request.ntransport do -- Get stock item. @@ -936,15 +969,15 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Add group to transportset. TransportSet:AddGroup(spawngroup) - table.insert(_delid,_assetitem.uid) + table.insert(_transportassets,_assetitem) else self:E(self.wid.."ERROR: spawngroup helo transport does not exist!") end end -- Delete spawned items from warehouse stock. - for _,_id in pairs(_delid) do - self:_DeleteStockItem(_id) + for _,_item in pairs(_transportassets) do + self:_DeleteStockItem(_item) end -- Define dispatcher for this task. @@ -957,7 +990,6 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) elseif Request.transporttype==WAREHOUSE.TransportType.APC then -- Spawn the transport groups. - local _delid={} for i=1,Request.ntransport do -- Get stock item. @@ -978,13 +1010,13 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Add group to transportset. TransportSet:AddGroup(spawngroup) - table.insert(_delid,_assetitem.uid) + table.insert(_transportassets,_assetitem) end end -- Delete spawned items from warehouse stock. - for _,_id in pairs(_delid) do - self:_DeleteStockItem(_id) + for _,_item in pairs(_transportassets) do + self:_DeleteStockItem(_item) end -- Define dispatcher for this task. @@ -1048,12 +1080,21 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Start dispatcher. CargoTransport:__Start(5) + + -- Add transportassets to pending queue item. + Pending.transportassets=_transportassets + + -- Add cargo groups to request. + Pending.transportgroupset=_transportgroups + Pending.transportassets=_transportassets + Pending.transportattribute=_transporttype + Pending.transportcategory=_transportcategory -- Add request to pending queue. - table.insert(self.pending, Request) + table.insert(self.pending, Pending) -- Delete request from queue. - self:_DeleteQueueItem(Request.uid) + self:_DeleteQueueItem(Request, self.queue) end @@ -1062,8 +1103,7 @@ end -- @param #WAREHOUSE self -- @param #WAREHOUSE.Queueitem Request Information table of the request. -- @return Core.Set#SET_GROUP Set of groups that were spawned. --- @return #WAREHOUSE.Attribute Generalized attribute of asset. --- @return DCS#Group.Category Category of asset, i.e. ground, air, ship, ... +-- @return #table List of spawned assets. function WAREHOUSE:_SpawnAssetRequest(Request) -- Filter the requested cargo assets. @@ -1093,10 +1133,9 @@ function WAREHOUSE:_SpawnAssetRequest(Request) end -- Create an empty set. - local groupset=SET_GROUP:New():FilterDeads() + local _groupset=SET_GROUP:New():FilterDeads() -- Spawn the assets. - local _delid={} local _spawngroups={} local _assets={} @@ -1143,9 +1182,8 @@ function WAREHOUSE:_SpawnAssetRequest(Request) if _group then --_spawngroups[i]=_group - groupset:AddGroup(_group) + _groupset:AddGroup(_group) table.insert(_assets, _assetitem) - table.insert(_delid,_assetitem.uid) else self:E(self.wid.."ERROR: cargo asset could not be spawned!") end @@ -1153,11 +1191,11 @@ function WAREHOUSE:_SpawnAssetRequest(Request) end -- Delete spawned items from warehouse stock. - for _,_id in pairs(_delid) do - self:_DeleteStockItem(_id) + for _,_item in pairs(_assets) do + self:_DeleteStockItem(_item) end - return groupset,_cargotype,_cargocategory,_assets + return _groupset,_assets end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1455,7 +1493,7 @@ end --- Warehouse event handling function. -- @param #WAREHOUSE self --- @param Core.Event#EVENTDATA Eventdata Event data. +-- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventBirth(EventData) self:E(self.wid..string.format("Warehouse %s captured event birth!",self.warehouse:GetName())) @@ -1469,42 +1507,55 @@ end --- Warehouse event handling function. -- @param #WAREHOUSE self --- @param Core.Event#EVENTDATA Eventdata Event data. +-- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventEngineStartup(EventData) self:E(self.wid..string.format("Warehouse %s captured event engine startup!",self.warehouse:GetName())) end --- Warehouse event handling function. -- @param #WAREHOUSE self --- @param Core.Event#EVENTDATA Eventdata Event data. +-- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventTakeOff(EventData) self:E(self.wid..string.format("Warehouse %s captured event takeoff!",self.warehouse:GetName())) end --- Warehouse event handling function. -- @param #WAREHOUSE self --- @param Core.Event#EVENTDATA Eventdata Event data. +-- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventLanding(EventData) self:E(self.wid..string.format("Warehouse %s captured event landing!",self.warehouse:GetName())) end --- Warehouse event handling function. -- @param #WAREHOUSE self --- @param Core.Event#EVENTDATA Eventdata Event data. +-- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventEngineShutdown(EventData) self:E(self.wid..string.format("Warehouse %s captured event engine shutdown!",self.warehouse:GetName())) end --- Warehouse event handling function. -- @param #WAREHOUSE self --- @param Core.Event#EVENTDATA Eventdata Event data. +-- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventCrashOrDead(EventData) - self:E(self.wid..string.format("Warehouse %s captured event birth!",self.warehouse:GetName())) + self:E(self.wid..string.format("Warehouse %s captured event dead or crash!",self.warehouse:GetName())) + + if EventData and EventData.IniUnit then + + -- Check if warehouse was destroyed. + local warehousename=self.warehouse:GetName() + if EventData.IniUnitName==warehousename then + env.info(self.wid..string.format("Warehouse %s was destroyed!", warehousename)) + --TODO: Add destroy event. + self:__Stop(1) + end + end + end --- Warehouse event handling function. +-- Handles the case when the airbase associated with the warehous is captured. -- @param #WAREHOUSE self --- @param Core.Event#EVENTDATA Eventdata Event data. +-- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventBaseCaptured(EventData) self:E(self.wid..string.format("Warehouse %s captured event base captured!",self.warehouse:GetName())) @@ -1515,7 +1566,8 @@ function WAREHOUSE:_OnEventBaseCaptured(EventData) if EventData and EventData.id==world.event.S_EVENT_BASE_CAPTURED then if EventData.Place then - + + -- Place is the airbase that was captured. local airbase=EventData.Place --Wrapper.Airbase#AIRBASE if EventData.PlaceName==self.airbasename then @@ -1548,16 +1600,24 @@ end -- Helper functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Count number of troups in spawn zone of the warehouse. +-- If only enemy troops are captured. +-- @param #WAREHOUSE self +function WAREHOUSE:_CheckSpawnZone() + + --self.spawnzone:IsAllInZoneOfCoalition(Coalition) + +end + --- Checks if the request can be fulfilled in general. If not, it is removed from the queue. -- Check if departure and destination bases are of the right type. -- @param #WAREHOUSE self -- @param #table queue The queue which is holding the requests to check. --- @param #WAREHOUSE.Queueitem qitem The request to be checked. -- @return #boolean If true, request can be executed. If false, something is not right. -function WAREHOUSE:_CheckRequestValid(queue) +function WAREHOUSE:_CheckRequestConsistancy(queue) -- Requests to delete. - local delid={} + local invalid={} for _,_request in pairs(queue) do local request=_request --#WAREHOUSE.Queueitem @@ -1740,15 +1800,15 @@ function WAREHOUSE:_CheckRequestValid(queue) -- Add request as unvalid and delete it later. if not valid then - table.insert(delid, request.id) + table.insert(invalid, request) end end -- loop queue items. -- Delete invalid requests. - for _,_uid in pairs(delid) do - self:_DeleteQueueItem(_uid) + for _,_request in pairs(invalid) do + self:_DeleteQueueItem(_request, self.queue) end end @@ -2235,13 +2295,13 @@ function WAREHOUSE:GetStockInfo(stock) return _data end ---- Delete item from stock. +--- Delete an asset item from stock. -- @param #WAREHOUSE self --- @param #number _uid The unique id of the item to be deleted. -function WAREHOUSE:_DeleteStockItem(_uid) +-- @param #WAREHOUSE.Stockitem stockitem Asset item to delete from stock table. +function WAREHOUSE:_DeleteStockItem(stockitem) for i=1,#self.stock do local item=self.stock[i] --#WAREHOUSE.Stockitem - if item.uid==_uid then + if item.uid==stockitem.uid then table.remove(self.stock,i) break end @@ -2250,12 +2310,13 @@ end --- Delete item from queue. -- @param #WAREHOUSE self --- @param #number _uid The id of the item to be deleted. -function WAREHOUSE:_DeleteQueueItem(_uid) - for i=1,#self.queue do - local item=self.queue[i] --#WAREHOUSE.Queueitem - if item.uid==_uid then - table.remove(self.queue,i) +-- @param #WAREHOUSE.Queueitem qitem Item of queue to be removed. +-- @param #table queue The queue from which the item should be deleted. +function WAREHOUSE:_DeleteQueueItem(qitem, queue) + for i=1,#queue do + local _item=queue[i] --#WAREHOUSE.Queueitem + if _item.uid==qitem.uid then + table.remove(queue,i) break end end @@ -2280,8 +2341,8 @@ function WAREHOUSE:_PrintQueue(queue, name) env.info(self.wid..name) for _,_qitem in ipairs(queue) do local qitem=_qitem --#WAREHOUSE.Queueitem - local text=string.format("uid=%d, prio=%d, airbase=%s (category=%d), descriptor: %s=%s, nasssets=%d, transport=%s, ntransport=%d", - qitem.uid, qitem.prio, qitem.airbase:GetName(),qitem.category, qitem.assetdesc,tostring(qitem.assetdescval),qitem.nasset,qitem.transporttype,qitem.ntransport) + local text=self.wid..string.format("UID=%d, Prio=%d, Warehouse=%s, Airbase=%s (category=%d), Descriptor: %s=%s, Nasssets=%d, Transport=%s, Ntransport=%d", + qitem.uid, qitem.prio, qitem.warehouse:GetName(), qitem.airbase:GetName(),qitem.category, qitem.assetdesc,tostring(qitem.assetdescval),qitem.nasset,qitem.transporttype,qitem.ntransport) env.info(text) end end From 4e63bf6a224912f1dd546efc48f3d586f41b6f84 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 15 Aug 2018 15:59:09 +0200 Subject: [PATCH 259/420] Warehouse 0.1.7w --- Moose Development/Moose/Functional/Warehouse.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 7d08c18b2..9036b3cc7 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -1085,7 +1085,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) Pending.transportassets=_transportassets -- Add cargo groups to request. - Pending.transportgroupset=_transportgroups + Pending.transportgroupset=Transportset Pending.transportassets=_transportassets Pending.transportattribute=_transporttype Pending.transportcategory=_transportcategory From 3ce59eee3586935b9814b5729d75e2f9dcc66cf6 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 16 Aug 2018 00:11:47 +0200 Subject: [PATCH 260/420] Warehouse v0.1.8 Lots of changes and improvements. --- Moose Development/Moose/Core/Point.lua | 8 +- .../Moose/Functional/Warehouse.lua | 394 +++++++++++------- 2 files changed, 255 insertions(+), 147 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 0006d597f..ebd9237e4 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1439,7 +1439,7 @@ do -- COORDINATE --- Flares the point in a color. -- @param #COORDINATE self -- @param Utilities.Utils#FLARECOLOR FlareColor - -- @param DCS#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. + -- @param DCS#Azimuth Azimuth (optional) The azimuth of the flare direction. The default azimuth is 0. function COORDINATE:Flare( FlareColor, Azimuth ) self:F2( { FlareColor } ) trigger.action.signalFlare( self:GetVec3(), FlareColor, Azimuth and Azimuth or 0 ) @@ -1447,7 +1447,7 @@ do -- COORDINATE --- Flare the COORDINATE White. -- @param #COORDINATE self - -- @param DCS#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. + -- @param DCS#Azimuth Azimuth (optional) The azimuth of the flare direction. The default azimuth is 0. function COORDINATE:FlareWhite( Azimuth ) self:F2( Azimuth ) self:Flare( FLARECOLOR.White, Azimuth ) @@ -1455,7 +1455,7 @@ do -- COORDINATE --- Flare the COORDINATE Yellow. -- @param #COORDINATE self - -- @param DCS#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. + -- @param DCS#Azimuth Azimuth (optional) The azimuth of the flare direction. The default azimuth is 0. function COORDINATE:FlareYellow( Azimuth ) self:F2( Azimuth ) self:Flare( FLARECOLOR.Yellow, Azimuth ) @@ -1463,7 +1463,7 @@ do -- COORDINATE --- Flare the COORDINATE Green. -- @param #COORDINATE self - -- @param DCS#Azimuth (optional) Azimuth The azimuth of the flare direction. The default azimuth is 0. + -- @param DCS#Azimuth Azimuth (optional) The azimuth of the flare direction. The default azimuth is 0. function COORDINATE:FlareGreen( Azimuth ) self:F2( Azimuth ) self:Flare( FLARECOLOR.Green, Azimuth ) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 9036b3cc7..786ad6a64 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -25,6 +25,8 @@ -- @field Wrapper.Static#STATIC warehouse The phyical warehouse structure. -- @field DCS#coalition.side coalition Coalition ID the warehouse belongs to. -- @field DCS#country.id country Country ID the warehouse belongs to. +-- @field #string alias Alias of the warehouse. Name its called when sending messages. +-- @field Core.Zone#ZONE zone Zone around the warehouse. If this zone is captured, the warehouse and all its assets goes to the capturing coaliton. -- @field Wrapper.Airbase#AIRBASE airbase Airbase the warehouse belongs to. -- @field #string airbasename Name of the airbase associated to the warehouse. -- @field DCS#Airbase.Category category Category of the home airbase, i.e. airdrome, helipad/farp or ship. @@ -84,28 +86,30 @@ -- -- @field #WAREHOUSE WAREHOUSE = { - ClassName = "WAREHOUSE", - Debug = false, - Report = true, - warehouse = nil, - coalition = nil, - country = nil, - airbase = nil, + ClassName = "WAREHOUSE", + Debug = false, + Report = true, + warehouse = nil, + coalition = nil, + country = nil, + alias = nil, + zone = nil, + airbase = nil, airbasename = nil, - category = -1, - coordinate = nil, - road = nil, - rail = nil, - spawnzone = nil, + category = -1, + coordinate = nil, + road = nil, + rail = nil, + spawnzone = nil, capturezone = nil, - wid = nil, - uid = nil, - markerid = nil, - assetid = 0, - queueid = 0, - stock = {}, - queue = {}, - pending = {}, + wid = nil, + uid = nil, + markerid = nil, + assetid = 0, + queueid = 0, + stock = {}, + queue = {}, + pending = {}, } --- Item of the warehouse stock table. @@ -187,7 +191,7 @@ WAREHOUSE.TransportType = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.1.7" +WAREHOUSE.version="0.1.8" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -209,6 +213,9 @@ WAREHOUSE.version="0.1.7" -- TODO: If warehosue is captured, change warehouse and assets to other coalition. -- TODO: Handle cases for aircraft carriers and other ships. Place warehouse on carrier possible? On others probably not - exclude them? -- TODO: Handle cargo crates. +-- TODO: Add general message function for sending to coaliton or debug. +-- TODO: Use RAT for routing air units. Should be possible but might need some modifications of RAT, e.g. explit spawn place. But flight plan should be better. +-- TODO: Can I make a request with specific assets? E.g., once delivered, make a request for exactly those assests that were in the original request. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor(s) @@ -217,10 +224,11 @@ WAREHOUSE.version="0.1.7" --- WAREHOUSE constructor. Creates a new WAREHOUSE object accociated with an airbase. -- @param #WAREHOUSE self -- @param Wrapper.Static#STATIC warehouse The physical structure of the warehouse. +-- @param #string alias (Optional) Alias of the warehouse, i.e. the name it will be called when sending messages etc. Default is the name of the static -- @param Core.Zone#ZONE spawnzone (Optional) The zone in which units are spawned and despawned when they leave or arrive the warehouse. Default is a zone of 200 meters around the warehouse. -- @param Wrapper.Airbase#AIRBASE airbase (Optional) The airbase belonging to the warehouse. Default is the closest airbase to the warehouse structure as long as it within a range of 3 km. -- @return #WAREHOUSE self -function WAREHOUSE:New(warehouse, spawnzone, airbase) +function WAREHOUSE:New(warehouse, alias) BASE:E({warehouse=warehouse:GetName()}) -- Nil check. @@ -228,15 +236,18 @@ function WAREHOUSE:New(warehouse, spawnzone, airbase) BASE:E("ERROR: Warehouse does not exist!") return nil end + + -- Set alias. + self.alias=alias or warehouse:GetName() -- Print version. - env.info(string.format("Adding warehouse v%s for structure %s", WAREHOUSE.version, warehouse:GetName())) + env.info(string.format("Adding warehouse v%s for structure %s with alias %s", WAREHOUSE.version, warehouse:GetName(), self.alias)) -- Inherit everthing from FSM class. - local self = BASE:Inherit( self, FSM:New() ) -- #WAREHOUSE + local self = BASE:Inherit(self, FSM:New()) -- #WAREHOUSE -- Set some string id for output to DCS.log file. - self.wid=string.format("WAREHOUSE %s | ", warehouse:GetName()) + self.wid=string.format("WAREHOUSE %s | ", self.alias) -- Set some variables. self.warehouse=warehouse @@ -244,59 +255,42 @@ function WAREHOUSE:New(warehouse, spawnzone, airbase) self.coalition=warehouse:GetCoalition() self.country=warehouse:GetCountry() self.coordinate=warehouse:GetCoordinate() - - -- Set airbase. - if airbase then - -- User requested. - self.airbase=airbase + + -- Closest of the same coalition but within a certain range. + local _airbase=self.coordinate:GetClosestAirbase(nil, self.coalition) + if _airbase and _airbase:GetCoordinate():Get2DDistance(self.coordinate) < 3000 then + self.airbase=_airbase self.airbasename=self.airbase:GetName() - else - -- Closest of the same coalition but within a certain range. - local _airbase=self.coordinate:GetClosestAirbase(nil, self.coalition) - if _airbase and _airbase:GetCoordinate():Get2DDistance(self.coordinate) < 3000 then - self.airbase=_airbase - self.airbasename=self.airbase:GetName() - self.category=self.airbase:GetDesc().category - end + self.category=self.airbase:GetDesc().category end - -- Get the closest point on road and rail. - local _road=self.coordinate:GetClosestPointToRoad() - local _rail=self.coordinate:GetClosestPointToRoad(true) - - -- Set connections. - if _road and _road:Get2DDistance(self.coordinate) < 3000 then - self.road=_road - _road:MarkToAll(string.format("%s road connection.", self.warehouse:GetName()), true) - end - if _rail and _rail:Get2DDistance(self.coordinate) < 3000 then - self.rail=_rail - _rail:MarkToAll(string.format("%s rail connection.", self.warehouse:GetName()), true) - end - - -- Define the default spawn zone. - self.spawnzone=ZONE_RADIUS:New(string.format("Spawnzone %s",warehouse:GetName()), warehouse:GetVec2(), 200) + -- Define warehouse and default spawn zone. + self.zone=ZONE_RADIUS:New(string.format("Warehouse zone %s", self.warehouse:GetName()), warehouse:GetVec2(), 500) + self.spawnzone=ZONE_RADIUS:New(string.format("Warehouse %s spawn zone", self.warehouse:GetName()), warehouse:GetVec2(), 200) -- Start State. self:SetStartState("Stopped") -- Add FSM transitions. - self:AddTransition("Stopped", "Load", "Stopped") - self:AddTransition("Stopped", "Start", "Running") - self:AddTransition("Running", "Status", "*") - self:AddTransition("Paused", "Status", "*") - self:AddTransition("*", "AddAsset", "*") - self:AddTransition("*", "AddRequest", "*") - self:AddTransition("Running", "Request", "*") - self:AddTransition("*", "Unloaded", "*") -- Cargo has been unloaded from the carrier. - self:AddTransition("*", "Arrived", "*") - self:AddTransition("*", "Delivered", "*") - self:AddTransition("*", "SelfDelivered", "*") - self:AddTransition("Running", "Pause", "Paused") - self:AddTransition("Paused", "Unpause", "Running") - self:AddTransition("*", "Stop", "Stopped") - self:AddTransition("*", "Save", "*") - + self:AddTransition("Stopped", "Load", "Stopped") -- TODO Load the warehouse state. No sure if it should be in stopped state. + self:AddTransition("Stopped", "Start", "Running") -- Start the warehouse. + self:AddTransition("Running", "Status", "*") -- Status update in running mode. Requests are processed. + self:AddTransition("Paused", "Status", "*") -- TODO Status update in paused mode. Requests are not processed. + self:AddTransition("*", "AddAsset", "*") -- Add asset to warehouse stock. + self:AddTransition("*", "AddRequest", "*") -- New request from other warehouse. + self:AddTransition("Running", "Request", "*") -- Process a request. Only in running mode. + self:AddTransition("*", "Unloaded", "*") -- Cargo has been unloaded from the carrier. + self:AddTransition("*", "Arrived", "*") -- Cargo group has arrived at destination. + self:AddTransition("*", "Delivered", "*") -- All cargo groups of a request have been delivered to the requesting warehouse. + self:AddTransition("*", "SelfRequest", "*") -- Request to warehouse itself. Requested assets are only spawned but not delivered anywhere. + self:AddTransition("Running", "Pause", "Paused") -- TODO Pause the processing of new requests. Still possible to add assets and requests. + self:AddTransition("Paused", "Unpause", "Running") -- TODO Unpause the warehouse. Queued requests are processed again. + self:AddTransition("*", "Stop", "Stopped") -- TODO Stop the warehouse. + self:AddTransition("*", "Save", "*") -- TODO Save the warehouse state to disk. + self:AddTransition("*", "Captured", "*") -- TODO Warehouse was captured by another coalition. + self:AddTransition("*", "Destroyed", "*") -- TODO Warehouse was destoryed. All assets are gone and warehouse is stopped. + + -- Pseudo Functions --- Triggers the FSM event "Start". Starts the warehouse. @@ -436,6 +430,17 @@ function WAREHOUSE:SetSpawnZone(zone) return self end + +--- Set the airbase belonging to this warehouse. +-- Be reasonable and do not put it too far from the phyiscal warehouse structure because you troops might have a long way to get to their transports. +-- @param #WAREHOUSE self +-- @param Wrapper.Airbase#AIRBASE airbase The airbase object associated to this warehouse. +-- @return #WAREHOUSE self +function WAREHOUSE:SetAirbase(airbase) + self.airbase=airbase + return self +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- FSM states ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -448,7 +453,7 @@ end function WAREHOUSE:onafterStart(From, Event, To) -- Short info. - local text=string.format("Starting warehouse %s:\n",self.warehouse:GetName()) + local text=string.format("Starting warehouse %s alias %s:\n",self.warehouse:GetName(), self.alias) text=text..string.format("Coaliton = %d\n", self.coalition) text=text..string.format("Country = %d\n", self.country) text=text..string.format("Airbase = %s (%s)\n", tostring(self.airbase:GetName()), tostring(self.category)) @@ -456,13 +461,38 @@ function WAREHOUSE:onafterStart(From, Event, To) -- Save self in static object. Easier to retrieve later. self.warehouse:SetState(self.warehouse, "WAREHOUSE", self) + + -- Set airbase name and category. + if self.airbase and self.airbase:GetCoalition()==self.coalition then + self.airbasename=self.airbase:GetName() + self.category=self.airbase:GetDesc().category + else + self.airbasename=nil + self.category=-1 -- The -1 indicates that we dont have an airbase at this warehouse. + end - -- Debug mark spawn zone. - self.spawnzone:BoundZone(60, self.country) - self.spawnzone:GetCoordinate():MarkToAll("Spawnzone of warehouse "..self.warehouse:GetName()) + -- Debug mark warehouse & spawn zone. + self.zone:BoundZone(30, self.country) + self.spawnzone:BoundZone(30, self.country) + --self.spawnzone:GetCoordinate():MarkToAll("Spawnzone of warehouse "..self.alias) + -- Get the closest point on road and rail wrt spawnzone of ground assets. + -- TODO: Make road/rail connection input parameter. + local _road=self.spawnzone:GetCoordinate():GetClosestPointToRoad() + local _rail=self.spawnzone:GetCoordinate():GetClosestPointToRoad(true) + + -- Set connections. + if _road and _road:Get2DDistance(self.coordinate) < 3000 then + self.road=_road + _road:MarkToAll(string.format("%s road connection.", self.alias), true) + end + if _rail and _rail:Get2DDistance(self.coordinate) < 3000 then + self.rail=_rail + _rail:MarkToAll(string.format("%s rail connection.", self.alias), true) + end + -- Create a zone capture object. - self.capturezone=ZONE_CAPTURE_COALITION:New(self.spawnzone, self.coalition) + self.capturezone=ZONE_CAPTURE_COALITION:New(self.zone, self.coalition) -- Add warehouse to zone capture object. Does this work? self.capturezone.warehouse=self @@ -473,7 +503,7 @@ function WAREHOUSE:onafterStart(From, Event, To) -- Handle capturing. function self.capturezone:OnEnterCaptured() local coalition = self:GetCoalition() - self:E(string.format("Warehouse %s was captured by coalition %d", tostring(self.warehouse:GetName()), coalition)) + self:E(string.format("Warehouse %s was captured by coalition %d!", tostring(self.alias), coalition)) self.warehouse.coalition=coalition --:SetCoalition(coalition) self:Guard() end @@ -488,6 +518,10 @@ function WAREHOUSE:onafterStart(From, Event, To) self:HandleEvent(EVENTS.Dead, self._OnEventCrashOrDead) self:HandleEvent(EVENTS.BaseCaptured, self._OnEventBaseCaptured) + -- This event triggers the arrived event for air assets. + -- TODO Might need to make this landing or optional! + self:HandleEvent(EVENTS.EngineShutdown, self._OnEventArrived) + -- Start the status monitoring. self:__Status(5) end @@ -509,6 +543,9 @@ function WAREHOUSE:onafterStop(From, Event, To) self:UnHandleEvent(EVENTS.Crash) self:UnHandleEvent(EVENTS.Dead) self:UnHandleEvent(EVENTS.BaseCaptured) + + -- Stop capture zone FSM. + self.capturezone:Stop() end --- On after "Pause" event. Pauses the warehouse, i.e. no requests are processed. However, new requests and new assets can be added in this state. @@ -537,7 +574,7 @@ end -- @param #string Event Event. -- @param #string To To state. function WAREHOUSE:onafterStatus(From, Event, To) - self:E(self.wid..string.format("Checking warehouse status of %s", self.warehouse:GetName())) + self:E(self.wid..string.format("Checking warehouse status of %s", self.alias)) -- Print queue. self:_PrintQueue(self.queue, "Queue0:") @@ -596,7 +633,7 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- On after "AddAsset" event. Add an airplane group to the warehouse stock. +--- On after "AddAsset" event. Add a group to the warehouse stock. If the group is alive, it is destroyed. -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. @@ -608,9 +645,12 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, templategroupname, ngroups) -- Set default. local n=ngroups or 1 + -- Get MOOSE group. local group=GROUP:FindByName(templategroupname) - if group then + -- Check if group exists and has a DCS object. + -- TODO: Need to check this carefully if this words with CARGO etc. + if group and group:IsAlive()~=nil and group:GetDCSObject() then local DCSgroup=group:GetDCSObject() local DCSunit=DCSgroup:getUnit(1) @@ -621,7 +661,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, templategroupname, ngroups) local SpeedMax=group:GetSpeedMax() local RangeMin=group:GetRange() - env.info(string.format("New asset:")) + env.info(string.format("New asset for warehouse %s:", self.alias)) env.info(string.format("Group name = %s", group:GetName())) env.info(string.format("Display name = %s", DCSdisplay)) env.info(string.format("Category = %s", DCScategory)) @@ -637,8 +677,10 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, templategroupname, ngroups) for i=1,n do local stockitem={} --#WAREHOUSE.Stockitem + -- Increase asset unique id counter. self.assetid=self.assetid+1 + -- Set parameters. stockitem.uid=self.assetid stockitem.templatename=templategroupname stockitem.template=UTILS.DeepCopy(_DATABASE.Templates.Groups[templategroupname].Template) @@ -649,6 +691,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, templategroupname, ngroups) stockitem.speedmax=SpeedMax -- Modify the template so that the group is spawned with the right coalition. + -- TODO: somehow this is now acknoleged properly. Found a workaround however with SPAWN AIP functions. stockitem.template.CoalitionID=self.coalition stockitem.template.CountryID=self.country @@ -729,11 +772,10 @@ end -- @param #WAREHOUSE.Queueitem Request Information table of the request. -- @return #boolean If true, request is granted. function WAREHOUSE:onbeforeRequest(From, Event, To, Request) - --env.info(self.wid..string.format("Warehouse %s requesting %d assets of %s=%s by transport %s", - --Request.warehouse:GetName(), Request.nasset, tostring(Request.assetdesc), tostring(Request.assetdescval), tostring(Request.transporttype))) + self:E({warehouse=self.alias, request=Request}) - -- Distance from warehouse to requesting airbase. - local distance=self.coordinate:Get2DDistance(Request.airbase:GetCoordinate()) + -- Distance from warehouse to requesting warehouse. + local distance=self.coordinate:Get2DDistance(Request.warehouse.coordinate) -- Filter the requested assets. local _assets=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval, Request.nasset) @@ -741,13 +783,20 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request) -- Check if destination is in range for all requested assets. for _,_asset in pairs(_assets) do local asset=_asset --#WAREHOUSE.Stockitem + + -- Check if destination is in range. if asset.range request denied. @@ -823,7 +872,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Self request! Assets are only spawned but not routed or transported anywhere. if self.warehouse:GetName()==Request.warehouse.warehouse:GetName() then env.info("FF selfrequest!") - self:__SelfDelivered(_spawngroups) + self:__SelfRequest(_spawngroups) return end @@ -1209,26 +1258,33 @@ end function WAREHOUSE:onafterUnloaded(From, Event, To, group) -- Debug info. self:E(self.wid..string.format("Cargo %s unloaded!", tostring(group:GetName()))) - group:SmokeWhite() + + if group and group:IsAlive() then - -- Get max speed of group. - local speedmax=group:GetSpeedMax() + -- Debug smoke. + group:SmokeWhite() - if group:IsGround() then - if speedmax>1 then - group:RouteGroundTo(self.spawnzone:GetRandomCoordinate(50), speedmax*0.5, AI.Task.VehicleFormation.RANK, 3) - else - -- Immobile ground unit ==> directly put it into the warehouse. + -- Get max speed of group. + local speedmax=group:GetSpeedMax() + + if group:IsGround() then + if speedmax>1 then + group:RouteGroundTo(self.spawnzone:GetRandomCoordinate(50), speedmax*0.5, AI.Task.VehicleFormation.RANK, 3) + else + -- Immobile ground unit ==> directly put it into the warehouse. + self:Arrived(group) + end + elseif group:IsAir() then + -- Not sure if air units will be allowed as cargo even though it might be possible. Best put them into warehouse immediately. self:Arrived(group) + elseif group:IsShip() then + -- Not sure if naval units will be allowed as cargo even though it might be possible. Best put them into warehouse immediately. + self:Arrived(group) end - elseif group:IsAir() then - -- Not sure if air units will be allowed as cargo even though it might be possible. Best put them into warehouse immediately. - self:Arrived(group) - elseif group:IsShip() then - -- Not sure if naval units will be allowed as cargo even though it might be possible. Best put them into warehouse immediately. - self:Arrived(group) + + else + self:E(self.wid..string.format("ERROR unloaded Cargo group is not alive!")) end - end --- On after "Arrived" event. Triggered when a group has arrived at its destination. @@ -1241,15 +1297,29 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) self:E(self.wid..string.format("Cargo %s arrived!", tostring(group:GetName()))) group:SmokeOrange() - + -- Update pending request. local request=self:_UpdatePending(group) - -- All cargo delivered. - if request and request.cargogroupset:Count()==0 then - self:__Delivered(5, request.cargogroupset, request) - end + if request then + -- Number of cargo assets still in group set. + local ncargo=request.cargogroupset:Count() + + -- Info + self:E(self.wid..string.format("Cargo %d of %d arrived at warehouse %s. Assets still to deliver %d.",request.ndelivered, request.nasset, request.warehouse.alias, ncargo)) + + -- Move asset into new warehouse. + -- TODO: need to figure out which template group name I best take. + request.warehouse:__AddAsset(3, group:GetName(), 1) + + -- All cargo delivered. + if request and ncargo==0 then + self:__Delivered(5, request) + end + + end + end --- Update the pending requests by removing assets that have arrived. @@ -1267,7 +1337,7 @@ function WAREHOUSE:_UpdatePending(group) if request then -- Loop over cargo groups. - for _,_cargogroup in pairs(request.cargogroupset) do + for _,_cargogroup in pairs(request.cargogroupset:GetSetObjects()) do local cargogroup=_cargogroup --Wrapper.Group#GROUP -- IDs of cargo group. @@ -1281,7 +1351,7 @@ function WAREHOUSE:_UpdatePending(group) end end else - self:E(self.wid..string.format("ERROR: pending request could not be updated since request did not exist in pending queue!")) + self:E(self.wid..string.format("WARNING: pending request could not be updated since request did not exist in pending queue!")) end return request @@ -1296,26 +1366,30 @@ end -- @param #WAREHOUSE.Pendingitem request function WAREHOUSE:onafterDelivered(From, Event, To, request) - env.info("FF all assets delivered!") + -- Debug info + self:E(self.wid..string.format("All assets from warehouse %s delivered to warehouse %s!", self.alias, request.warehouse.alias)) - -- Put assets in new warehouse. - for _,_group in pairs(groupset:GetSetObjects()) do - local group=_group --Wrapper.Group#GROUP - request.warehouse:AddAsset(group:GetName(), 1) + -- Fireworks! + for i=1,91 do + local color=math.random(0,3) + request.warehouse.coordinate:Flare(color, i-1) end + -- Remove pending request: + self:_DeleteQueueItem(request, self.pending) + end ---- On after "SelfDelivered" event. Request was initiated to the warehouse itself. Groups are just spawned at the warehouse or the associated airbase. +--- On after "SelfRequest" event. Request was initiated to the warehouse itself. Groups are just spawned at the warehouse or the associated airbase. -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Core.Set#SET_GROUP groupset The set of cargo groups that was delivered. --- @param #WAREHOUSE.Pendingitem request -function WAREHOUSE:onafterSelfDelivered(From, Event, To, groupset, request) +-- @param #WAREHOUSE.Pendingitem request Pending self request. +function WAREHOUSE:onafterSelfRequest(From, Event, To, groupset, request) - env.info("FF all assets delivered!") + self:E(self.wid..string.format("Assets spawned at warehouse %s after self request!", self.alias)) -- Put assets in new warehouse. for _,_group in pairs(groupset:GetSetObjects()) do @@ -1323,7 +1397,8 @@ function WAREHOUSE:onafterSelfDelivered(From, Event, To, groupset, request) group:SmokeGreen() end - --TODO: delete pending queue item. + -- Remove pending request: + self:_DeleteQueueItem(request, self.pending) end @@ -1416,7 +1491,8 @@ function WAREHOUSE:_RouteAir(Aircraft, ToAirbase, Speed) env.info("FF Respawn at current airbase group = "..newAC:GetName().." name after") -- Handle event engine shutdown and trigger delivered event. - newAC:HandleEvent(EVENTS.EngineShutdown, self._OnEventArrived) + -- Not this did not work unless the routine would retrive the state from get/set state! + --newAC:HandleEvent(EVENTS.EngineShutdown, self._OnEventArrived) else self:E(string.format("ERROR: aircraft %s cannot be routed since it does not exist or is not alive %s!", tostring(Aircraft:GetName()), tostring(Aircraft:IsAlive()))) end @@ -1483,11 +1559,40 @@ end -- @param Core.Event#EVENTDATA EventData Event data table. function WAREHOUSE:_OnEventArrived(EventData) - local unit=EventData.IniUnit - unit:SmokeBlue() + if EventData and EventData.IniUnit then - local group=EventData.IniGroup - self:__Arrived(1, group) + -- Unit that arrived. + local unit=EventData.IniUnit + + -- Check if unit is alive and on the ground. Engine shutdown can also be triggered in other situations! + if unit and unit:IsAlive()==true and unit:InAir()==false then + + -- Smoke unit that arrived. + unit:SmokeBlue() + + -- Get group. + local group=EventData.IniGroup + + -- Get unique IDs from group name. + local wid,aid,rid=self:_GetIDsFromGroup(group) + + -- If all IDs are good we can assume it is a warehouse asset. + if wid~=nil and aid~=nil and rid~=nil then + + -- Debug info. + self:E(self.wid..string.format("Air asset group %s arrived.", group:GetName())) + + -- Trigger arrived event for this group. Note that each unit of a group will trigger this event. So the onafterArrived function needs to take care of that. + -- Actually, we only take the first unit of the group that arrives. If it does, we assume the whole group arrived, which might not be the case, since + -- some units might still be taxiing or whatever. Therefore, we add 10 seconds for each additional unit of the group until the first arrived event is triggered. + local nunits=#group:GetUnits() + local dt=10*(nunits-1)+1 -- one unit = 1 sec, two units = 11 sec, three units = 21 sec before we call the group arrived. + self:__Arrived(1, group) + else + self:E(string.format("Group that arrived did not belong to a warehouse. Warehouse ID=%s, Asset ID=%s, Request ID=%s.", tostring(wid), tostring(aid), tostring(rid))) + end + end + end end @@ -1495,7 +1600,7 @@ end -- @param #WAREHOUSE self -- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventBirth(EventData) - self:E(self.wid..string.format("Warehouse %s captured event birth!",self.warehouse:GetName())) + self:E(self.wid..string.format("Warehouse %s captured event birth!",self.alias)) if EventData and EventData.id==world.event.S_EVENT_BIRTH then if EventData.IniGroup then @@ -1509,42 +1614,42 @@ end -- @param #WAREHOUSE self -- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventEngineStartup(EventData) - self:E(self.wid..string.format("Warehouse %s captured event engine startup!",self.warehouse:GetName())) + self:E(self.wid..string.format("Warehouse %s captured event engine startup!",self.alias)) end --- Warehouse event handling function. -- @param #WAREHOUSE self -- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventTakeOff(EventData) - self:E(self.wid..string.format("Warehouse %s captured event takeoff!",self.warehouse:GetName())) + self:E(self.wid..string.format("Warehouse %s captured event takeoff!",self.alias)) end --- Warehouse event handling function. -- @param #WAREHOUSE self -- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventLanding(EventData) - self:E(self.wid..string.format("Warehouse %s captured event landing!",self.warehouse:GetName())) + self:E(self.wid..string.format("Warehouse %s captured event landing!",self.alias)) end --- Warehouse event handling function. -- @param #WAREHOUSE self -- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventEngineShutdown(EventData) - self:E(self.wid..string.format("Warehouse %s captured event engine shutdown!",self.warehouse:GetName())) + self:E(self.wid..string.format("Warehouse %s captured event engine shutdown!",self.alias)) end --- Warehouse event handling function. -- @param #WAREHOUSE self -- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventCrashOrDead(EventData) - self:E(self.wid..string.format("Warehouse %s captured event dead or crash!",self.warehouse:GetName())) + self:E(self.wid..string.format("Warehouse %s captured event dead or crash!",self.alias)) if EventData and EventData.IniUnit then -- Check if warehouse was destroyed. local warehousename=self.warehouse:GetName() if EventData.IniUnitName==warehousename then - env.info(self.wid..string.format("Warehouse %s was destroyed!", warehousename)) + env.info(self.wid..string.format("Warehouse %s alias %s was destroyed!", warehousename, self.alias)) --TODO: Add destroy event. self:__Stop(1) end @@ -1557,7 +1662,7 @@ end -- @param #WAREHOUSE self -- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventBaseCaptured(EventData) - self:E(self.wid..string.format("Warehouse %s captured event base captured!",self.warehouse:GetName())) + self:E(self.wid..string.format("Warehouse %s captured event base captured!",self.alias)) -- This warehouse does not have an airbase and never had one. So i could not be captured. if self.airbasename==nil then @@ -1576,9 +1681,9 @@ function WAREHOUSE:_OnEventBaseCaptured(EventData) -- New coalition of airbase after it was captured. local coalitionAirbase=airbase:GetCoalition() - -- what can happen? - -- warehouse is blue, airbase is blue and belongs to warehouse and red captures it. ==> self.airbase=nil - -- warehouse is blue, airbase is blue self.airbase is nil and blue (re-)captures it ==> self.airbase=Event.Place + -- So what can happen? + -- Warehouse is blue, airbase is blue and belongs to warehouse and red captures it ==> self.airbase=nil + -- Warehouse is blue, airbase is blue self.airbase is nil and blue (re-)captures it ==> self.airbase=Event.Place if self.airbase==nil then -- Warehouse lost this airbase previously and not it was re-captured. if coalitionAirbase == self.coalition then @@ -1622,6 +1727,7 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) for _,_request in pairs(queue) do local request=_request --#WAREHOUSE.Queueitem + -- Let's assume everything is fine. local valid=true --TODO: check that @@ -2113,6 +2219,7 @@ function WAREHOUSE:_GetIDsFromGroup(group) return _wid,_aid,_rid end + self:E({_function="getids", group=group}) if group then -- Group name @@ -2206,7 +2313,8 @@ function WAREHOUSE:_GetAttribute(groupname) if group then -- Get generalized attributes. - -- Transports: Helos, planes and APCs + -- TODO: need to work on ships and trucks and SAMs and ... + -- Also the Yak-52 for example is OTHER since it only has the attribute "Battleplanes". local transportplane=group:HasAttribute("Transports") and group:HasAttribute("Planes") local transporthelo=group:HasAttribute("Transport helicopters") local transportapc=group:HasAttribute("Infantry carriers") @@ -2338,12 +2446,12 @@ end -- @param #table queue Queue to print. -- @param #string name Name of the queue for info reasons. function WAREHOUSE:_PrintQueue(queue, name) - env.info(self.wid..name) + self:E(self.wid..name) for _,_qitem in ipairs(queue) do local qitem=_qitem --#WAREHOUSE.Queueitem - local text=self.wid..string.format("UID=%d, Prio=%d, Warehouse=%s, Airbase=%s (category=%d), Descriptor: %s=%s, Nasssets=%d, Transport=%s, Ntransport=%d", - qitem.uid, qitem.prio, qitem.warehouse:GetName(), qitem.airbase:GetName(),qitem.category, qitem.assetdesc,tostring(qitem.assetdescval),qitem.nasset,qitem.transporttype,qitem.ntransport) - env.info(text) + local text=self.wid..string.format("UID=%d, Prio=%d, Requestor=%s, Airbase=%s (category=%d), Descriptor: %s=%s, Nasssets=%d, Transport=%s, Ntransport=%d", + qitem.uid, qitem.prio, qitem.warehouse.alias, qitem.airbase:GetName(),qitem.category, qitem.assetdesc,tostring(qitem.assetdescval),qitem.nasset,qitem.transporttype,qitem.ntransport) + self:E(text) end end From 22da329fcaae17b9c635ec0abc2f68c534ad318c Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 16 Aug 2018 16:20:36 +0200 Subject: [PATCH 261/420] Warehouse v0.1.8w --- Moose Development/Moose/Core/SpawnStatic.lua | 10 +- Moose Development/Moose/Functional/RAT.lua | 12 +- .../Moose/Functional/Warehouse.lua | 272 +++++++++++++++--- Moose Development/Moose/Wrapper/Static.lua | 5 +- 4 files changed, 248 insertions(+), 51 deletions(-) diff --git a/Moose Development/Moose/Core/SpawnStatic.lua b/Moose Development/Moose/Core/SpawnStatic.lua index 334201e4c..a6295e7cd 100644 --- a/Moose Development/Moose/Core/SpawnStatic.lua +++ b/Moose Development/Moose/Core/SpawnStatic.lua @@ -191,19 +191,17 @@ function SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1 end ---- Creates the original @{Static} at a POINT_VEC2. +--- Respawns the original @{Static}. -- @param #SPAWNSTATIC self --- @param Core.Point#POINT_VEC2 PointVec2 The 2D coordinate where to spawn the static. --- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360. --- @param #string (optional) The name of the new static. +-- @param DCS#country.id (Optional) The country ID of the static after respawning.. -- @return #SPAWNSTATIC -function SPAWNSTATIC:ReSpawn() +function SPAWNSTATIC:ReSpawn(countryid) local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix ) if StaticTemplate then - local CountryID = self.CountryID + local CountryID = countryid or self.CountryID local CountryName = _DATABASE.COUNTRY_NAME[CountryID] StaticTemplate.units = nil diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 932e8e41b..74d5db139 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -2064,8 +2064,9 @@ end -- @param #table _waypoint First waypoint to be used (for continue journey, commute, etc). -- @param Core.Point#COORDINATE _lastpos (Optional) Position where the aircraft will be spawned. -- @param #number _nrespawn Number of already performed respawn attempts (e.g. spawning on runway bug). +-- @param #table parkingdata Explicitly specify the parking spots when spawning at an airport. -- @return #number Spawn index. -function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _livery, _waypoint, _lastpos, _nrespawn) +function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _livery, _waypoint, _lastpos, _nrespawn, parkingdata) self:F({rat=RAT.id, departure=_departure, destination=_destination, takeoff=_takeoff, landing=_landing, livery=_livery, waypoint=_waypoint, lastpos=_lastpos, nrespawn=_nrespawn}) -- Set takeoff type. @@ -5103,9 +5104,10 @@ end -- @param Core.Point#COORDINATE spawnplace (Optional) Place where spawning should happen. If not present, first waypoint is taken. -- @param Wrapper.Airbase#AIRBASE departure Departure airbase or zone. -- @param #number takeoff Takeoff type. +-- @param #table parkingdata Parking data, i.e. parking spot coordinates and terminal ids for all units of the group. -- @return #boolean True if modification was successful or nil if not, e.g. when no parking space was found and spawn in air is disabled. -function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, takeoff) - self:F2({waypoints=waypoints, livery=livery, spawnplace=spawnplace, departure=departure, takeoff=takeoff}) +function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, takeoff, parkingdata) + self:F2({waypoints=waypoints, livery=livery, spawnplace=spawnplace, departure=departure, takeoff=takeoff, parking=parkingdata}) -- The 3D vector of the first waypoint, i.e. where we actually spawn the template group. local PointVec3 = COORDINATE:New(waypoints[1].x, waypoints[1].alt, waypoints[1].y) @@ -5193,6 +5195,10 @@ function RAT:_ModifySpawnTemplate(waypoints, livery, spawnplace, departure, take self:T(RAT.id..string.format("Group %s is spawned on farp/ship/runway %s.", self.alias, departure:GetName())) nfree=departure:GetFreeParkingSpotsNumber(termtype, true) spots=departure:GetFreeParkingSpotsTable(termtype, true) + elseif parkingdata~=nil then + -- Parking data explicitly set by user as input parameter. + nfree=#parkingdata + spots=parkingdata else -- Helo is spawned. if self.category==RAT.cat.heli then diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 786ad6a64..1e14e8d39 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -96,7 +96,7 @@ WAREHOUSE = { zone = nil, airbase = nil, airbasename = nil, - category = -1, + category = -1, coordinate = nil, road = nil, rail = nil, @@ -191,7 +191,7 @@ WAREHOUSE.TransportType = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.1.8" +WAREHOUSE.version="0.1.8w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -204,11 +204,11 @@ WAREHOUSE.version="0.1.8" -- DONE: Switch to AI_CARGO_XXX_DISPATCHER -- DONE: Add queue. -- TODO: Write documentation. --- TODO: Put active groups into the warehouse, e.g. when they were transported to this warehouse. --- TODO: Spawn warehouse assets as uncontrolled or AI off and activate them when requested. +-- DONE: Put active groups into the warehouse, e.g. when they were transported to this warehouse. +-- NOGO: Spawn warehouse assets as uncontrolled or AI off and activate them when requested. -- TODO: Handle cases with immobile units. --- TODO: How to handle multiple units in a transport group? --- DONE: Add phyical object +-- DONE: How to handle multiple units in a transport group? <== Cargo dispatchers. +-- DONE: Add phyical object. -- TODO: If warehouse is destoyed, all asssets are gone. -- TODO: If warehosue is captured, change warehouse and assets to other coalition. -- TODO: Handle cases for aircraft carriers and other ships. Place warehouse on carrier possible? On others probably not - exclude them? @@ -221,7 +221,7 @@ WAREHOUSE.version="0.1.8" -- Constructor(s) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- WAREHOUSE constructor. Creates a new WAREHOUSE object accociated with an airbase. +--- The WAREHOUSE constructor. Creates a new WAREHOUSE object from a static object. Parameters like the coalition and country are taken from the static object structure. -- @param #WAREHOUSE self -- @param Wrapper.Static#STATIC warehouse The physical structure of the warehouse. -- @param #string alias (Optional) Alias of the warehouse, i.e. the name it will be called when sending messages etc. Default is the name of the static @@ -282,11 +282,12 @@ function WAREHOUSE:New(warehouse, alias) self:AddTransition("*", "Unloaded", "*") -- Cargo has been unloaded from the carrier. self:AddTransition("*", "Arrived", "*") -- Cargo group has arrived at destination. self:AddTransition("*", "Delivered", "*") -- All cargo groups of a request have been delivered to the requesting warehouse. - self:AddTransition("*", "SelfRequest", "*") -- Request to warehouse itself. Requested assets are only spawned but not delivered anywhere. + self:AddTransition("Running", "SelfRequest", "*") -- Request to warehouse itself. Requested assets are only spawned but not delivered anywhere. self:AddTransition("Running", "Pause", "Paused") -- TODO Pause the processing of new requests. Still possible to add assets and requests. self:AddTransition("Paused", "Unpause", "Running") -- TODO Unpause the warehouse. Queued requests are processed again. self:AddTransition("*", "Stop", "Stopped") -- TODO Stop the warehouse. self:AddTransition("*", "Save", "*") -- TODO Save the warehouse state to disk. + self:AddTransition("*", "Attacked", "*") -- TODO Warehouse is under attack by enemy coalitin. self:AddTransition("*", "Captured", "*") -- TODO Warehouse was captured by another coalition. self:AddTransition("*", "Destroyed", "*") -- TODO Warehouse was destoryed. All assets are gone and warehouse is stopped. @@ -430,9 +431,18 @@ function WAREHOUSE:SetSpawnZone(zone) return self end +--- Set a warehouse zone. If this zone is captured, the warehouse and all its assets fall into the hands of the enemy. +-- @param #WAREHOUSE self +-- @param Core.Zone#ZONE zone The warehouse zone. Note that this **cannot** be a polygon zone! +-- @return #WAREHOUSE self +function WAREHOUSE:SetWarehouseZone(zone) + self.zone=zone + return self +end --- Set the airbase belonging to this warehouse. --- Be reasonable and do not put it too far from the phyiscal warehouse structure because you troops might have a long way to get to their transports. +-- Note that it has to be of the same coalition as the warehouse. +-- Also, be reasonable and do not put it too far from the phyiscal warehouse structure because you troops might have a long way to get to their transports. -- @param #WAREHOUSE self -- @param Wrapper.Airbase#AIRBASE airbase The airbase object associated to this warehouse. -- @return #WAREHOUSE self @@ -441,6 +451,51 @@ function WAREHOUSE:SetAirbase(airbase) return self end +--- Set the connection of the warehouse to the road. +-- Ground assets spawned in the warehouse spawn zone will first go to this point and from there travel on road to the requesting warehouse. +-- Note that by default the road connection is set to the closest point on road from the center of the spawn zone if it is withing 3000 meters. +-- Also note, that if the parameter "coordinate" is passed as nil, any road connection is disabled and ground assets cannot travel of be transportet on the ground. +-- @param #WAREHOUSE self +-- @param Core.Point#COORDINATE coordinate The road connection. Technically, the closest point on road from this coordinate is determined by DCS API function. So this point must not be exactly on the road. +-- @return #WAREHOUSE self +function WAREHOUSE:SetRoadConnection(coordinate) + if coordinate then + self.road=coordinate:GetClosestPointToRoad() + else + self.road=false + end + return self +end + +--- Set the connection of the warehouse to the railroad. +-- This is the place where train assets or transports will be spawned. +-- @param #WAREHOUSE self +-- @param Core.Point#COORDINATE coordinate The railroad connection. Technically, the closest point on rails from this coordinate is determined by DCS API function. So this point must not be exactly on the a railroad connection. +-- @return #WAREHOUSE self +function WAREHOUSE:SetRailConnection(coordinate) + if coordinate then + self.rail=coordinate:GetClosestPointToRoad(true) + else + self.rail=false + end + return self +end + +--- Check if the warehouse is running. +-- @param #WAREHOUSE self +-- @return #boolean If true, the warehouse is running and requests are processed. +function WAREHOUSE:IsRunning() + return self:is("Running") +end + +--- Check if the warehouse is paused. In this state, requests are not processed. +-- @param #WAREHOUSE self +-- @return #boolean If true, the warehouse is paused. +function WAREHOUSE:IsPaused() + return self:is("Paused") +end + + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- FSM states ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -474,22 +529,36 @@ function WAREHOUSE:onafterStart(From, Event, To) -- Debug mark warehouse & spawn zone. self.zone:BoundZone(30, self.country) self.spawnzone:BoundZone(30, self.country) + --self.spawnzone:GetCoordinate():MarkToAll("Spawnzone of warehouse "..self.alias) - -- Get the closest point on road and rail wrt spawnzone of ground assets. - -- TODO: Make road/rail connection input parameter. + -- Get the closest point on road wrt spawnzone of ground assets. local _road=self.spawnzone:GetCoordinate():GetClosestPointToRoad() - local _rail=self.spawnzone:GetCoordinate():GetClosestPointToRoad(true) - - -- Set connections. - if _road and _road:Get2DDistance(self.coordinate) < 3000 then - self.road=_road - _road:MarkToAll(string.format("%s road connection.", self.alias), true) + if _road and self.road==nil then + -- Set connection to road if distance is less than 3 km. + local _Droad=_road:Get2DDistance(self.spawnzone:GetCoordinate()) + if _Droad < 3000 then + self.road=_road + end end - if _rail and _rail:Get2DDistance(self.coordinate) < 3000 then - self.rail=_rail - _rail:MarkToAll(string.format("%s rail connection.", self.alias), true) - end + -- Mark point at road connection. + if self.road then + self.road:MarkToAll(string.format("%s road connection.", self.alias), true) + end + + -- Get the closest point on railroad wrt spawnzone of ground assets. + local _rail=self.spawnzone:GetCoordinate():GetClosestPointToRoad(true) + if _rail and self.rail==nil then + -- Set rail conection if it is less than 3 km away. + local _Drail=_rail:Get2DDistance(self.spawnzone:GetCoordinate()) + if _Drail < 3000 then + self.rail=_rail + end + end + -- Mark point at rail connection. + if self.rail then + self.rail:MarkToAll(string.format("%s rail connection.", self.alias), true) + end -- Create a zone capture object. self.capturezone=ZONE_CAPTURE_COALITION:New(self.zone, self.coalition) @@ -500,11 +569,20 @@ function WAREHOUSE:onafterStart(From, Event, To) -- Start capturing monitoring. self.capturezone:Start(10, 60) + -- Handle attack. + function self.capturezone:OnEnterAttacked() + local coalition = self:GetCoalition() + self:E(string.format("Warehouse %s is under attack!", tostring(self.warehouse.alias))) + -- Trigger FSM Attacked event. + self.warehouse:Attacked() + end + -- Handle capturing. function self.capturezone:OnEnterCaptured() local coalition = self:GetCoalition() - self:E(string.format("Warehouse %s was captured by coalition %d!", tostring(self.alias), coalition)) + self:E(string.format("Warehouse %s was captured by coalition %d!", tostring(self.warehouse.alias), coalition)) self.warehouse.coalition=coalition --:SetCoalition(coalition) + self.warehouse:Captured(coalition) self:Guard() end @@ -520,6 +598,8 @@ function WAREHOUSE:onafterStart(From, Event, To) -- This event triggers the arrived event for air assets. -- TODO Might need to make this landing or optional! + -- In fact, it would be better if the type could be defined for only for the warehouse which receives stuff, + -- since there will be warehouses with small airbases and little space or other problems! self:HandleEvent(EVENTS.EngineShutdown, self._OnEventArrived) -- Start the status monitoring. @@ -563,7 +643,7 @@ end -- @param #string Event Event. -- @param #string To To state. function WAREHOUSE:onafterUnpause(From, Event, To) - self:E(self.wid..string.format("Warehouse unpaused! Processing of requests is resumed again.")) + self:E(self.wid..string.format("Warehouse %s unpaused! Processing of requests is resumed.", self.alias)) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -615,12 +695,15 @@ function WAREHOUSE:onafterStatus(From, Event, To) self:_PrintQueue(self.pending, "Pending after consistancy:") - -- Check queue and handle requests if possible. - local request=self:_CheckQueue() + -- If warehouse is running than requests can be processed. + if self:IsRunning() then + -- Check queue and handle requests if possible. + local request=self:_CheckQueue() - -- Execute the request. If the request is really executed, it is also deleted from the queue. - if request then - self:Request(request) + -- Execute the request. If the request is really executed, it is also deleted from the queue. + if request then + self:Request(request) + end end -- Print queue. @@ -897,7 +980,9 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) self:_RouteGround(group, ToCoordinate) elseif _cargocategory==Group.Category.AIRPLANE then env.info("FF route plane "..group:GetName()) - self:_RouteAir(group, Request.airbase) + --self:_RouteAir(group, Request.airbase) + -- TEST! + group=self:_RouteAirRat(group, Request.airbase) elseif _cargocategory==Group.Category.HELICOPTER then env.info("FF route helo "..group:GetName()) self:_RouteAir(group, Request.airbase) @@ -1134,7 +1219,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) Pending.transportassets=_transportassets -- Add cargo groups to request. - Pending.transportgroupset=Transportset + Pending.transportgroupset=TransportSet Pending.transportassets=_transportassets Pending.transportattribute=_transporttype Pending.transportcategory=_transportcategory @@ -1402,6 +1487,42 @@ function WAREHOUSE:onafterSelfRequest(From, Event, To, groupset, request) end +--- On after "Attacked" event. Warehouse is under attack by an another coalition. +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function WAREHOUSE:onafterAttacked(From, Event, To) + self:E(self.wid..string.format("Out warehouse is under attack!")) +end + +--- On after "Captured" event. Warehouse has been captured by another coalition. +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param DCS#coalition.side Coalition which captured the warehouse. +function WAREHOUSE:onafterCaptured(From, Event, To, Coalition) + self:E(self.wid..string.format("Our warehouse was captured by coalition %d!", Coalition)) + + --TODO: Need to get a way to get the correct country. + local Country + if Coalition==coalition.side.BLUE then + Country=country.id.USA + elseif Coalition==coalition.side.RED then + Country=country.id.USSR + else + Country=country.id.SWITZERLAND + end + + -- Respawn warehouse with new coalition/country. + self.warehouse:ReSpawn(Country) + self.coalition=Coalition + self.country=Country + self.airbase=nil + self.category=-1 +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Routing functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1433,6 +1554,53 @@ function WAREHOUSE:_RouteGround(Group, Coordinate, Speed) end end +--- Route the airplane from one airbase another. +-- @param #WAREHOUSE self +-- @param Wrapper.Group#GROUP Aircraft Airplane group to be routed. +-- @param Wrapper.Airbase#AIRBASE ToAirbase Destination airbase. +-- @param #number Speed Speed in km/h. Default is 80% of max possible speed the group can do. +-- @return Wrapper.Group#GROUP Group that was spawned by RAT. +function WAREHOUSE:_RouteAirRat(Aircraft, ToAirbase, Speed) + + if Aircraft and Aircraft:IsAlive()~=nil then + -- Get parking data of all units. + local parkingdata={} + local units=Aircraft:GetUnits() + for _,_unit in pairs(units) do + local unit=_unit --Wrapper.Unit#UNIT + local _spot,_terminal,_distance=unit:GetCoordinate():GetClosestOccupiedParkingSpot(self.airbase) + table.insert(parkingdata, {spot=_spot, TerminalID=_terminal}) + end + + -- Create a RAT object to use its flight plan. + local rat=RAT:New(Aircraft) + + -- Init some parameters. + rat:SetDeparture(self.airbase:GetName()) + rat:SetDestination(ToAirbase:GetName()) + --rat:SetCoalitionAircraft(color) + rat:SetCountry(self.country) + rat:NoRespawn() + + -- Init spawn but do not actually spawn. + rat:Spawn(0) + --rat:_SpawnWithRoute(_departure,_destination,_takeoff,_landing,_livery,_waypoint,_lastpos,_nrespawn,parkingdata) + + -- Destroy the original aircraft. + Aircraft:Destroy() + + -- Spawn RAT aircraft at specific parking sports. + local spawnindex=rat:_SpawnWithRoute(self.airbase:GetName(), ToAirbase:GetName(), RAT.wp.cold, nil, nil, nil, nil, nil, parkingdata) + + -- Get the group and check it's name. + local group=rat.ratcraft[spawnindex].group --Wrapper.Group#GROUP + self:E(self.wid..string.format("Spawned new RAT aircraft as group %s", group:GetName())) + + return group + end + +end + --- Route the airplane from one airbase another. -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP Aircraft Airplane group to be routed. @@ -1705,15 +1873,6 @@ end -- Helper functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Count number of troups in spawn zone of the warehouse. --- If only enemy troops are captured. --- @param #WAREHOUSE self -function WAREHOUSE:_CheckSpawnZone() - - --self.spawnzone:IsAllInZoneOfCoalition(Coalition) - -end - --- Checks if the request can be fulfilled in general. If not, it is removed from the queue. -- Check if departure and destination bases are of the right type. -- @param #WAREHOUSE self @@ -1808,7 +1967,9 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) asset_air=asset_helo or asset_plane if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then - + ------------------------------------------- + -- Case where the units go my themselves -- + ------------------------------------------- if asset_air then if asset_plane then @@ -1833,6 +1994,12 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) end + -- All aircraft need an airbase of any type as depature or destination. + if self.airbase==nil or request.airbase==nil then + self:E("ERROR: incorrect request. Either warehouse or requesting warehouse does not have any kind of airbase!") + valid=false + end + elseif asset_ground then -- No ground assets directly to or from ships. @@ -1842,6 +2009,31 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) valid=false end + if asset_train then + -- Check if there is a valid path on rail. + if self.rail and request.warehouse.rail then + local onrail=self.rail:GetPathOnRoad(request.warehouse.rail, false, true) + if onrail==nil then + self:E("ERROR: incorrect request. No valid path on rail for train assets!") + valid=false + end + else + self:E("ERROR: incorrect request. Either warehouse or requesting warehouse have no connection to rail!") + valid=false + end + else + -- Check if there is a valid path on road. + if self.road and request.warehouse.road then + local onroad=self.road:GetPathOnRoad(request.warehouse.road, false, false) + if onroad==nil then + self:E("ERROR: incorrect request. No valid path on road for ground assets!") + valid=false + end + else + self:E("ERROR: incorrect request. Either warehouse or requesting warehouse have no connection to road!") + valid=false + end + end elseif asset_naval then self:E("ERROR: incorrect request. Naval units not supported yet!") diff --git a/Moose Development/Moose/Wrapper/Static.lua b/Moose Development/Moose/Wrapper/Static.lua index a591522af..ef488443c 100644 --- a/Moose Development/Moose/Wrapper/Static.lua +++ b/Moose Development/Moose/Wrapper/Static.lua @@ -141,11 +141,12 @@ end --- Respawn the @{Wrapper.Unit} at the same location with the same properties. -- This is useful to respawn a cargo after it has been destroyed. -- @param #STATIC self -function STATIC:ReSpawn() +-- @param DCS#country.id countryid The country ID used for spawning the new static. +function STATIC:ReSpawn(countryid) local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName ) - SpawnStatic:ReSpawn() + SpawnStatic:ReSpawn(countryid) end From 5a2e1f01b4ceac51bcbc6d8049812324e1acf67d Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 16 Aug 2018 23:10:23 +0200 Subject: [PATCH 262/420] Warehouse 0.1.9 Many bugs spotted --- Moose Development/Moose/Functional/RAT.lua | 2 +- .../Moose/Functional/Warehouse.lua | 22 ++- Moose Development/Moose/Wrapper/Airbase.lua | 178 +++++++++--------- Moose Development/Moose/Wrapper/Unit.lua | 10 +- 4 files changed, 112 insertions(+), 100 deletions(-) diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 74d5db139..95faf9daf 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -2116,7 +2116,7 @@ function RAT:_SpawnWithRoute(_departure, _destination, _takeoff, _landing, _live end -- Modify the spawn template to follow the flight plan. - local successful=self:_ModifySpawnTemplate(waypoints, livery, _lastpos, departure, takeoff) + local successful=self:_ModifySpawnTemplate(waypoints, livery, _lastpos, departure, takeoff, parkingdata) if not successful then return nil end diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 1e14e8d39..0ed459d58 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -191,7 +191,7 @@ WAREHOUSE.TransportType = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.1.8w" +WAREHOUSE.version="0.1.9" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -1118,7 +1118,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) CargoTransport = AI_CARGO_DISPATCHER_HELICOPTER:New(TransportSet, CargoGroups, DeployZoneSet) -- Home zone. - CargoTransport:Setairbase(self.airbase) + --CargoTransport:Setairbase(self.airbase) --CargoTransport:SetHomeZone(self.spawnzone) elseif Request.transporttype==WAREHOUSE.TransportType.APC then @@ -1563,24 +1563,28 @@ end function WAREHOUSE:_RouteAirRat(Aircraft, ToAirbase, Speed) if Aircraft and Aircraft:IsAlive()~=nil then + -- Get parking data of all units. local parkingdata={} + local units=Aircraft:GetUnits() for _,_unit in pairs(units) do local unit=_unit --Wrapper.Unit#UNIT local _spot,_terminal,_distance=unit:GetCoordinate():GetClosestOccupiedParkingSpot(self.airbase) - table.insert(parkingdata, {spot=_spot, TerminalID=_terminal}) + table.insert(parkingdata, {Coordinate=_spot, TerminalID=_terminal}) end + env.info("FF parking data") + self:E(parkingdata) -- Create a RAT object to use its flight plan. - local rat=RAT:New(Aircraft) + local rat=RAT:New(Aircraft:GetName()) -- Init some parameters. rat:SetDeparture(self.airbase:GetName()) rat:SetDestination(ToAirbase:GetName()) --rat:SetCoalitionAircraft(color) - rat:SetCountry(self.country) - rat:NoRespawn() + rat:SetCountry(self.country) + rat:NoRespawn() -- Init spawn but do not actually spawn. rat:Spawn(0) @@ -1590,12 +1594,16 @@ function WAREHOUSE:_RouteAirRat(Aircraft, ToAirbase, Speed) Aircraft:Destroy() -- Spawn RAT aircraft at specific parking sports. - local spawnindex=rat:_SpawnWithRoute(self.airbase:GetName(), ToAirbase:GetName(), RAT.wp.cold, nil, nil, nil, nil, nil, parkingdata) + local spawnindex=rat:_SpawnWithRoute(self.airbase:GetName(), ToAirbase:GetName(), RAT.wp.hot, nil, nil, nil, nil, nil, parkingdata) -- Get the group and check it's name. local group=rat.ratcraft[spawnindex].group --Wrapper.Group#GROUP self:E(self.wid..string.format("Spawned new RAT aircraft as group %s", group:GetName())) + group:SmokeBlue() + -- Activate group. + local bla=group:SetCommand({id='Start', params={}}) + self:E({bla=bla}) return group end diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 1af9548c2..7d45a38b8 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -680,99 +680,101 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE local _termid=parkingspot.TerminalID - -- Very safe uses the DCS getParking() info to check if a spot is free. Unfortunately, the function returns free=false until the aircraft has actually taken-off. - if verysafe and (parkingspot.Free==false or parkingspot.TOAC==true) then - - -- DCS getParking() routine returned that spot is not free. - self:E(string.format("%s: Parking spot id %d NOT free (or aircraft has not taken off yet). Free=%s, TOAC=%s.", airport, parkingspot.TerminalID, tostring(parkingspot.Free), tostring(parkingspot.TOAC))) - - else + if AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) then + + -- Very safe uses the DCS getParking() info to check if a spot is free. Unfortunately, the function returns free=false until the aircraft has actually taken-off. + if verysafe and (parkingspot.Free==false or parkingspot.TOAC==true) then - -- Scan a radius of 50 meters around the spot. - local _,_,_,_units,_statics,_sceneries=_spot:ScanObjects(scanradius, scanunits, scanstatics, scanscenery) + -- DCS getParking() routine returned that spot is not free. + self:E(string.format("%s: Parking spot id %d NOT free (or aircraft has not taken off yet). Free=%s, TOAC=%s.", airport, parkingspot.TerminalID, tostring(parkingspot.Free), tostring(parkingspot.TOAC))) - -- Loop over objects within scan radius. - local occupied=false - - -- Check all units. - for _,unit in pairs(_units) do - - local _vec3=unit:getPoint() - local _coord=COORDINATE:NewFromVec3(_vec3) - local _dist=_coord:Get2DDistance(_spot) - local _safe=_overlap(aircraft, true, unit, false,_dist) - - if markobstacles then - local l,x,y,z=_GetObjectSize(unit) - _coord:MarkToAll(string.format("Unit %s\nx=%.1f y=%.1f z=%.1f\nl=%.1f d=%.1f\nspot %d safe=%s", unit:getName(),x,y,z,l,_dist, _termid, tostring(_safe))) - end - - if scanunits and not _safe then - occupied=true - end - end - - -- Check all statics. - for _,static in pairs(_statics) do - local _vec3=static:getPoint() - local _coord=COORDINATE:NewFromVec3(_vec3) - local _dist=_coord:Get2DDistance(_spot) - local _safe=_overlap(aircraft, true, static, false,_dist) - - if markobstacles then - local l,x,y,z=_GetObjectSize(static) - _coord:MarkToAll(string.format("Static %s\nx=%.1f y=%.1f z=%.1f\nl=%.1f d=%.1f\nspot %d safe=%s", static:getName(),x,y,z,l,_dist, _termid, tostring(_safe))) - end - - if scanstatics and not _safe then - occupied=true - end - end - - -- Check all scenery. - for _,scenery in pairs(_sceneries) do - local _vec3=scenery:getPoint() - local _coord=COORDINATE:NewFromVec3(_vec3) - local _dist=_coord:Get2DDistance(_spot) - local _safe=_overlap(aircraft, true, scenery, false,_dist) - - if markobstacles then - local l,x,y,z=_GetObjectSize(scenery) - _coord:MarkToAll(string.format("Scenery %s\nx=%.1f y=%.1f z=%.1f\nl=%.1f d=%.1f\nspot %d safe=%s", scenery:getTypeName(),x,y,z,l,_dist, _termid, tostring(_safe))) - end - - if scanscenery and not _safe then - occupied=true - end - end - - -- Now check the already given spots so that we do not put a large aircraft next to one we already assigned a nearby spot. - for _,_takenspot in pairs(validspots) do - local _dist=_takenspot.Coordinate:Get2DDistance(_spot) - local _safe=_overlap(aircraft, true, aircraft, true,_dist) - if not _safe then - occupied=true - end - end - - --_spot:MarkToAll(string.format("Parking spot %d free=%s", parkingspot.TerminalID, tostring(not occupied))) - if occupied then - self:T(string.format("%s: Parking spot id %d occupied.", airport, _termid)) else - self:E(string.format("%s: Parking spot id %d free.", airport, _termid)) - if nvalid<_nspots then - table.insert(validspots, {Coordinate=_spot, TerminalID=_termid}) - end - nvalid=nvalid+1 - end + + -- Scan a radius of 50 meters around the spot. + local _,_,_,_units,_statics,_sceneries=_spot:ScanObjects(scanradius, scanunits, scanstatics, scanscenery) - end -- loop over units - - -- We found enough spots. - if nvalid>=_nspots then - return validspots - end + -- Loop over objects within scan radius. + local occupied=false + -- Check all units. + for _,unit in pairs(_units) do + + local _vec3=unit:getPoint() + local _coord=COORDINATE:NewFromVec3(_vec3) + local _dist=_coord:Get2DDistance(_spot) + local _safe=_overlap(aircraft, true, unit, false,_dist) + + if markobstacles then + local l,x,y,z=_GetObjectSize(unit) + _coord:MarkToAll(string.format("Unit %s\nx=%.1f y=%.1f z=%.1f\nl=%.1f d=%.1f\nspot %d safe=%s", unit:getName(),x,y,z,l,_dist, _termid, tostring(_safe))) + end + + if scanunits and not _safe then + occupied=true + end + end + + -- Check all statics. + for _,static in pairs(_statics) do + local _vec3=static:getPoint() + local _coord=COORDINATE:NewFromVec3(_vec3) + local _dist=_coord:Get2DDistance(_spot) + local _safe=_overlap(aircraft, true, static, false,_dist) + + if markobstacles then + local l,x,y,z=_GetObjectSize(static) + _coord:MarkToAll(string.format("Static %s\nx=%.1f y=%.1f z=%.1f\nl=%.1f d=%.1f\nspot %d safe=%s", static:getName(),x,y,z,l,_dist, _termid, tostring(_safe))) + end + + if scanstatics and not _safe then + occupied=true + end + end + + -- Check all scenery. + for _,scenery in pairs(_sceneries) do + local _vec3=scenery:getPoint() + local _coord=COORDINATE:NewFromVec3(_vec3) + local _dist=_coord:Get2DDistance(_spot) + local _safe=_overlap(aircraft, true, scenery, false,_dist) + + if markobstacles then + local l,x,y,z=_GetObjectSize(scenery) + _coord:MarkToAll(string.format("Scenery %s\nx=%.1f y=%.1f z=%.1f\nl=%.1f d=%.1f\nspot %d safe=%s", scenery:getTypeName(),x,y,z,l,_dist, _termid, tostring(_safe))) + end + + if scanscenery and not _safe then + occupied=true + end + end + + -- Now check the already given spots so that we do not put a large aircraft next to one we already assigned a nearby spot. + for _,_takenspot in pairs(validspots) do + local _dist=_takenspot.Coordinate:Get2DDistance(_spot) + local _safe=_overlap(aircraft, true, aircraft, true,_dist) + if not _safe then + occupied=true + end + end + + --_spot:MarkToAll(string.format("Parking spot %d free=%s", parkingspot.TerminalID, tostring(not occupied))) + if occupied then + self:T(string.format("%s: Parking spot id %d occupied.", airport, _termid)) + else + self:E(string.format("%s: Parking spot id %d free.", airport, _termid)) + if nvalid<_nspots then + table.insert(validspots, {Coordinate=_spot, TerminalID=_termid}) + end + nvalid=nvalid+1 + end + + end -- loop over units + + -- We found enough spots. + if nvalid>=_nspots then + return validspots + end + end -- check terminal type end -- Retrun spots we found, even if there were not enough. diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 653c7bf5d..e992a3ebd 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -119,10 +119,12 @@ end -- @param DCS#Unit DCSUnit An existing DCS Unit object reference. -- @return #UNIT self function UNIT:Find( DCSUnit ) - - local UnitName = DCSUnit:getName() - local UnitFound = _DATABASE:FindUnit( UnitName ) - return UnitFound + if DCSUnit then + local UnitName = DCSUnit:getName() + local UnitFound = _DATABASE:FindUnit( UnitName ) + return UnitFound + end + return nil end --- Find a UNIT in the _DATABASE using the name of an existing DCS Unit. From a02d3c195013fff3f79d87a22db46f768b8605e5 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Fri, 17 Aug 2018 07:59:53 +0200 Subject: [PATCH 263/420] Cargo Dispatcher APC - Multiple Cargo Groups and Weight in APC --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 97 +++++++++++++++---- .../Moose/AI/AI_Cargo_Dispatcher.lua | 5 + .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 4 +- .../Moose/AI/AI_Cargo_Helicopter.lua | 5 +- Moose Development/Moose/Cargo/Cargo.lua | 3 +- Moose Development/Moose/Cargo/CargoGroup.lua | 13 ++- .../Moose/Wrapper/Positionable.lua | 1 - 7 files changed, 97 insertions(+), 31 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 13f88324e..fbe2dd1f4 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -96,7 +96,7 @@ function AI_CARGO_APC:New( APC, CargoSet, CombatRadius ) self:AddTransition( "Loaded", "Deploy", "*" ) self:AddTransition( "*", "Load", "Boarding" ) - self:AddTransition( "Boarding", "Board", "Boarding" ) + self:AddTransition( { "Boarding", "Loaded" }, "Board", "Boarding" ) self:AddTransition( "Boarding", "Loaded", "Loaded" ) self:AddTransition( "Loaded", "Unload", "Unboarding" ) self:AddTransition( "Unboarding", "Unboard", "Unboarding" ) @@ -194,8 +194,20 @@ function AI_CARGO_APC:New( APC, CargoSet, CombatRadius ) self:__Monitor( 1 ) - self:SetCarrier( APC ) + + for _, APCUnit in pairs( APC:GetUnits() ) do + local Desc = APCUnit:GetDesc() + self:F({Desc=Desc}) +-- local Box = CargoUnit:GetBoundingBox() + local VolumeUnit = ( Desc.box.max.x - Desc.box.min.x ) * ( Desc.box.max.y - Desc.box.min.y ) * ( Desc.box.max.z - Desc.box.min.z ) + self:F({VolumeUnit=VolumeUnit}) + local CargoBayWeightLimit = 1250 + APCUnit:SetCargoBayWeightLimit( CargoBayWeightLimit ) + self:F({CargoBayWeightLimit=CargoBayWeightLimit}) + --Airplane:SetCargoBayVolumeLimit( 15 ) + end + self.Transporting = false self.Relocating = false @@ -411,7 +423,6 @@ function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To ) self:F( { APC, From, Event, To } ) local Boarding = false - self.BoardingCount = 0 if APC and APC:IsAlive() then self.APC_Cargo = {} @@ -427,11 +438,11 @@ function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To ) --Cargo:Ungroup() Cargo:Board( APCUnit, 25 ) self:__Board( 1, Cargo ) - Boarding = true - + -- So now this APCUnit has Cargo that is being loaded. -- This will be used further in the logic to follow and to check cargo status. self.APC_Cargo[APCUnit] = Cargo + Boarding = true break end end @@ -458,7 +469,31 @@ function AI_CARGO_APC:onafterBoard( APC, From, Event, To, Cargo ) if not Cargo:IsLoaded() then self:__Board( 10, Cargo ) else - self:__Loaded( 1 ) + for _, APCUnit in pairs( APC:GetUnits() ) do + local APCUnit = APCUnit -- Wrapper.Unit#UNIT + for _, Cargo in pairs( self.CargoSet:GetSet() ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + if Cargo:IsUnLoaded() then + if Cargo:IsInLoadRadius( APCUnit:GetCoordinate() ) then + local CargoBayFreeWeight = APCUnit:GetCargoBayFreeWeight() + local CargoWeight = Cargo:GetWeight() + + self:F({CargoBayFreeWeight=CargoBayFreeWeight}) + + -- Only when there is space within the bay to load the next cargo item! + if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then + Cargo:Board( APCUnit, 25 ) + self:__Board( 10, Cargo ) + -- So now this APCUnit has Cargo that is being loaded. + -- This will be used further in the logic to follow and to check cargo status. + self.APC_Cargo[APCUnit] = Cargo + return + end + end + end + end + end + self:__Loaded( 5, Cargo ) end end @@ -471,7 +506,7 @@ end -- @param #string Event Event. -- @param #string To To state. -- @return #boolean Cargo loaded. -function AI_CARGO_APC:onbeforeLoaded( APC, From, Event, To ) +function AI_CARGO_APC:onbeforeLoaded( APC, From, Event, To, Cargo ) self:F( { APC, From, Event, To } ) local Loaded = true @@ -484,7 +519,6 @@ function AI_CARGO_APC:onbeforeLoaded( APC, From, Event, To ) Loaded = false end end - end if Loaded == true then @@ -496,6 +530,9 @@ function AI_CARGO_APC:onbeforeLoaded( APC, From, Event, To ) end + + + --- On after Unload event. -- @param #AI_CARGO_APC self -- @param Wrapper.Group#GROUP APC @@ -512,6 +549,7 @@ function AI_CARGO_APC:onafterUnload( APC, From, Event, To, Deployed ) APC:RouteStop() for _, Cargo in pairs( APCUnit:GetCargo() ) do Cargo:UnBoard() + Cargo:SetDeployed( true ) self:__Unboard( 10, Cargo, Deployed ) end end @@ -534,6 +572,17 @@ function AI_CARGO_APC:onafterUnboard( APC, From, Event, To, Cargo, Deployed ) if not Cargo:IsUnLoaded() then self:__Unboard( 10, Cargo, Deployed ) else + for _, APCUnit in pairs( APC:GetUnits() ) do + local APCUnit = APCUnit -- Wrapper.Unit#UNIT + for _, Cargo in pairs( APCUnit:GetCargo() ) do + if Cargo:IsLoaded() then + Cargo:UnBoard() + Cargo:SetDeployed( true ) + self:__Unboard( 10, Cargo, Deployed ) + return + end + end + end self:__Unloaded( 1, Cargo, Deployed ) end end @@ -559,22 +608,16 @@ function AI_CARGO_APC:onbeforeUnloaded( APC, From, Event, To, Cargo, Deployed ) if APC and APC:IsAlive() then for _, APCUnit in pairs( APC:GetUnits() ) do local APCUnit = APCUnit -- Wrapper.Unit#UNIT - local CargoCheck = self.APC_Cargo[APCUnit] - if CargoCheck then - self:F( { CargoCheck:GetName(), IsUnLoaded = CargoCheck:IsUnLoaded() } ) - if CargoCheck:IsUnLoaded() == false then - AllUnloaded = false - break - end + local IsEmpty = APCUnit:IsCargoEmpty() + self:I({ IsEmpty = IsEmpty }) + if not IsEmpty then + AllUnloaded = false + break end end if AllUnloaded == true then if Deployed == true then - for APCUnit, Cargo in pairs( self.APC_Cargo ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - Cargo:SetDeployed( true ) - end self.APC_Cargo = {} end self:Guard() @@ -587,6 +630,21 @@ function AI_CARGO_APC:onbeforeUnloaded( APC, From, Event, To, Cargo, Deployed ) end +--- On after Unloaded event. +-- @param #AI_CARGO_APC self +-- @param Wrapper.Group#GROUP APC +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #string Cargo.Cargo#CARGO Cargo Cargo object. +-- @param #boolean Deployed Cargo is deployed. +-- @return #boolean All cargo unloaded. +function AI_CARGO_APC:onafterUnloaded( APC, From, Event, To, Cargo, Deployed ) + self:F( { APC, From, Event, To, Cargo:GetName(), Deployed = Deployed } ) + + self.Transporting = false + +end --- On after Follow event. -- @param #AI_CARGO_APC self @@ -632,7 +690,6 @@ function AI_CARGO_APC._Deploy( APC, self ) if APC:IsAlive() then self:Unload( true ) - self.Transporting = false self.Relocating = false end end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 933556713..9bbfe26a8 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -416,6 +416,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() self:F( { Cargo = Cargo:GetName(), UnLoaded = Cargo:IsUnLoaded(), Deployed = Cargo:IsDeployed(), PickupCargo = self.PickupCargo[Carrier] ~= nil } ) if Cargo:IsUnLoaded() == true and Cargo:IsDeployed() == false then local CargoCoordinate = Cargo:GetCoordinate() + self:F({CargoCoordinate = CargoCoordinate }) local CoordinateFree = true for CarrierPickup, Coordinate in pairs( self.PickupCargo ) do if CarrierPickup:IsAlive() == true then @@ -427,6 +428,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() self.PickupCargo[CarrierPickup] = nil end end + self:F({CoordinateFree = CoordinateFree}) if CoordinateFree == true then self.PickupCargo[Carrier] = CargoCoordinate PickupCargo = Cargo @@ -435,6 +437,8 @@ function AI_CARGO_DISPATCHER:onafterMonitor() end end + self:F( { PickupCargo = PickupCargo} ) + if PickupCargo then self.CarrierHome[Carrier] = nil local PickupCoordinate = PickupCargo:GetCoordinate():GetRandomCoordinateInRadius( self.PickupOuterRadius, self.PickupInnerRadius ) @@ -567,6 +571,7 @@ function AI_CARGO_DISPATCHER:OnAfterLoaded( From, Event, To, Carrier, Cargo ) end end + self:F({Carrier=Carrier}) self.PickupCargo[Carrier] = nil end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index 413ca7c26..1612ce2c7 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -101,9 +101,9 @@ AI_CARGO_DISPATCHER_APC = { -- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() -- AICargoDispatcher = AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZone, 500 ) -- -function AI_CARGO_DISPATCHER_APC:NewWithZones( SetAPC, SetCargo, SetDeployZone, CombatRadius ) +function AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZone, CombatRadius ) - local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( SetAPC, SetCargo, SetDeployZone ) ) -- #AI_CARGO_DISPATCHER_APC + local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithZones( SetAPC, SetCargo, SetDeployZone ) ) -- #AI_CARGO_DISPATCHER_APC self.CombatRadius = CombatRadius or 500 diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index faa85affc..df6865545 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -395,7 +395,7 @@ function AI_CARGO_HELICOPTER:onbeforeLoad( Helicopter, From, Event, To) for _, Cargo in pairs( self.CargoSet:GetSet() ) do local Cargo = Cargo -- Cargo.Cargo#CARGO self:F( { IsUnLoaded = Cargo:IsUnLoaded() } ) - if Cargo:IsUnLoaded() then + if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then if Cargo:IsInLoadRadius( HelicopterUnit:GetCoordinate() ) then self:F( { "In radius", HelicopterUnit:GetName() } ) --Cargo:Ungroup() @@ -440,7 +440,6 @@ function AI_CARGO_HELICOPTER:onafterBoard( Helicopter, From, Event, To, Cargo ) -- Only when there is space within the bay to load the next cargo item! if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then - Cargo:Board( HelicopterUnit, 25 ) self:__Board( 10, Cargo ) return @@ -455,7 +454,7 @@ function AI_CARGO_HELICOPTER:onafterBoard( Helicopter, From, Event, To, Cargo ) end ---- On before Loaded event. Check if cargo is loaded. +--- On after Loaded event. Check if cargo is loaded. -- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -- @param #string From From state. diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 50cade298..3311a1bff 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -779,7 +779,8 @@ do -- CARGO local Distance = 0 if self:IsUnLoaded() then - Distance = Coordinate:Get2DDistance( self.CargoObject:GetCoordinate() ) + local CargoCoordinate = self.CargoObject:GetCoordinate() + Distance = Coordinate:Get2DDistance( CargoCoordinate ) self:T( Distance ) if Distance <= self.LoadRadius then return true diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 9a0294855..63f9203d1 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -500,8 +500,6 @@ do -- CARGO_GROUP -- @return Core.Point#COORDINATE The current Coordinate of the first Cargo of the CargoGroup. -- @return #nil There is no valid Cargo in the CargoGroup. function CARGO_GROUP:GetCoordinate() - self:F() - local Cargo = self:GetFirstAlive() -- Cargo.Cargo#CARGO if Cargo then @@ -635,12 +633,19 @@ do -- CARGO_GROUP if Cargo then local Distance = 0 + local CargoCoordinate if Cargo:IsLoaded() then - Distance = Coordinate:Get2DDistance( Cargo.CargoCarrier:GetCoordinate() ) + CargoCoordinate = Cargo.CargoCarrier:GetCoordinate() else - Distance = Coordinate:Get2DDistance( Cargo.CargoObject:GetCoordinate() ) + CargoCoordinate = Cargo.CargoObject:GetCoordinate() end +-- if CargoCoordinate then + Distance = Coordinate:Get2DDistance( CargoCoordinate ) +-- else +-- return false +-- end + self:F( { Distance = Distance, LoadRadius = self.LoadRadius } ) if Distance <= self.LoadRadius then return true diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 017c5f70f..287338c2b 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -879,7 +879,6 @@ do -- Cargo function POSITIONABLE:GetCargoBayFreeWeight() local CargoWeight = 0 for CargoName, Cargo in pairs( self.__.Cargo ) do - self:F( { Cargo = Cargo } ) CargoWeight = CargoWeight + Cargo:GetWeight() end return self.__.CargoBayWeightLimit - CargoWeight From 6c586d69ac2f3832933129fbd597ab47cf14bb99 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Fri, 17 Aug 2018 16:36:16 +0200 Subject: [PATCH 264/420] Warehouse 0.1.9w --- Moose Development/Moose/Core/Point.lua | 65 ++- Moose Development/Moose/Functional/RAT.lua | 36 +- .../Moose/Functional/Warehouse.lua | 438 ++++++++++++++++-- Moose Development/Moose/Utilities/Utils.lua | 69 ++- Moose Development/Moose/Wrapper/Group.lua | 17 + Moose Development/Moose/Wrapper/Unit.lua | 2 +- 6 files changed, 543 insertions(+), 84 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index ebd9237e4..d2d9884a6 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -729,6 +729,20 @@ do -- COORDINATE return nil end + --- Returns the heading from this to another coordinate. + -- @param #COORDINATE self + -- @param #COORDINATE ToCoordinate + -- @return #number Heading in degrees. + function COORDINATE:HeadingTo(ToCoordinate) + local dz=ToCoordinate.z-self.z + local dx=ToCoordinate.x-self.x + local heading=math.deg(math.atan2(dz, dx)) + if heading < 0 then + heading = 360 + heading + end + return heading + end + --- Returns the wind direction (from) and strength. -- @param #COORDINATE self -- @param height (Optional) parameter specifying the height ASL. The minimum height will be always be the land height since the wind is zero below the ground. @@ -949,21 +963,53 @@ do -- COORDINATE -- @param #COORDINATE.WaypointAction Action The route point action. -- @param DCS#Speed Speed Airspeed in km/h. Default is 500 km/h. -- @param #boolean SpeedLocked true means the speed is locked. + -- @param Wrapper.Airbase#AIRBASE airbase The airbase for takeoff and landing points. + -- @param #table DCSTasks A table of DCS#Task items which are executed at the waypoint. + -- @param #string description A text description of the waypoint, which will be shown on the F10 map. -- @return #table The route point. - function COORDINATE:WaypointAir( AltType, Type, Action, Speed, SpeedLocked ) + function COORDINATE:WaypointAir( AltType, Type, Action, Speed, SpeedLocked, airbase, DCSTasks, description ) self:F2( { AltType, Type, Action, Speed, SpeedLocked } ) - + + -- Defaults + AltType=AltType or "RADIO" + if SpeedLocked==nil then + SpeedLocked=true + end + Speed=Speed or 500 + + -- Waypoint array. local RoutePoint = {} + + -- Coordinates. RoutePoint.x = self.x RoutePoint.y = self.z + -- Altitude. RoutePoint.alt = self.y - RoutePoint.alt_type = AltType or "RADIO" - + RoutePoint.alt_type = AltType + -- Waypoint type. RoutePoint.type = Type or nil RoutePoint.action = Action or nil - - RoutePoint.speed = ( Speed and Speed / 3.6 ) or ( 500 / 3.6 ) - RoutePoint.speed_locked = true + -- Set speed/ETA. + RoutePoint.speed = Speed/3.6 + RoutePoint.speed_locked = SpeedLocked + RoutePoint.ETA=nil + RoutePoint.ETA_locked = false + -- Waypoint description. + RoutePoint.name=description + -- Airbase parameters for takeoff and landing points. + if airbase then + local AirbaseID = airbase:GetID() + local AirbaseCategory = airbase:GetDesc().category + if AirbaseCategory == Airbase.Category.SHIP or AirbaseCategory == Airbase.Category.HELIPAD then + RoutePoint.linkUnit = AirbaseID + RoutePoint.helipadId = AirbaseID + elseif AirbaseCategory == Airbase.Category.AIRDROME then + RoutePoint.airdromeId = AirbaseID + else + self:T("ERROR: Unknown airbase category in COORDINATE:WaypointAir()!") + end + end + -- ["task"] = -- { @@ -976,12 +1022,11 @@ do -- COORDINATE -- }, -- end of ["params"] -- }, -- end of ["task"] - + -- Waypoint tasks. RoutePoint.task = {} RoutePoint.task.id = "ComboTask" RoutePoint.task.params = {} - RoutePoint.task.params.tasks = {} - + RoutePoint.task.params.tasks = DCSTasks or {} return RoutePoint end diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 95faf9daf..75bfe545d 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -2459,7 +2459,7 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) local VxCruiseMin = math.min(VxCruiseMax*0.70, 166) -- Cruise speed (randomized). Expectation value at midpoint between min and max. - local VxCruise = self:_Random_Gaussian((VxCruiseMax-VxCruiseMin)/2+VxCruiseMin, (VxCruiseMax-VxCruiseMax)/4, VxCruiseMin, VxCruiseMax) + local VxCruise = UTILS.RandomGaussian((VxCruiseMax-VxCruiseMin)/2+VxCruiseMin, (VxCruiseMax-VxCruiseMax)/4, VxCruiseMin, VxCruiseMax) -- Climb speed 90% ov Vmax but max 720 km/h. local VxClimb = math.min(self.aircraft.Vmax*0.90, 200) @@ -2817,7 +2817,7 @@ function RAT:_SetRoute(takeoff, landing, _departure, _destination, _waypoint) end -- Set cruise altitude. Selected from Gaussian distribution but limited to FLmin and FLmax. - local FLcruise=self:_Random_Gaussian(FLcruise_expect, math.abs(FLmax-FLmin)/4, FLmin, FLmax) + local FLcruise=UTILS.RandomGaussian(FLcruise_expect, math.abs(FLmax-FLmin)/4, FLmin, FLmax) -- Overrule setting if user specified a flight level explicitly. if self.FLuser then @@ -5014,38 +5014,6 @@ function RAT:_Randomize(value, fac, lower, upper) return r end ---- Generate Gaussian pseudo-random numbers. --- @param #number x0 Expectation value of distribution. --- @param #number sigma (Optional) Standard deviation. Default 10. --- @param #number xmin (Optional) Lower cut-off value. --- @param #number xmax (Optional) Upper cut-off value. --- @return #number Gaussian random number. -function RAT:_Random_Gaussian(x0, sigma, xmin, xmax) - - -- Standard deviation. Default 10 if not given. - sigma=sigma or 10 - - local r - local gotit=false - local i=0 - while not gotit do - - -- Uniform numbers in [0,1). We need two. - local x1=math.random() - local x2=math.random() - - -- Transform to Gaussian exp(-(x-x0)²/(2*sigma²). - r = math.sqrt(-2*sigma*sigma * math.log(x1)) * math.cos(2*math.pi * x2) + x0 - - i=i+1 - if (r>=xmin and r<=xmax) or i>100 then - gotit=true - end - end - - return r - -end --- Place markers of the waypoints. Note we assume a very specific number and type of waypoints here. -- @param #RAT self diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 0ed459d58..d9eea2a1f 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -140,11 +140,16 @@ WAREHOUSE = { --- Item of the warehouse pending queue table. -- @type WAREHOUSE.Pendingitem -- @extends #WAREHOUSE.Queueitem --- @field #table assetlist Table of assets to be delivered. Each element of the table is a @{#WAREHOUSE.Stockitem}. --- @field #number ndelivered Number of groups delivered to destination. Is managed automatically. --- @field #number ntransporthome Number of transports back home. Is managed automatically. --- @field Core.Set#SET_GROUP cargogroupset Set of cargo groups do be delivered. Is managed automatically. --- @field Core.Set#SET_GROUP transportgroupset Set of cargo transport groups. Is managed automatically. +-- @field #table cargoassets Table of assets to be delivered. Each element of the table is a @{#WAREHOUSE.Stockitem}. +-- @field Core.Set#SET_GROUP cargogroupset Set of cargo groups do be delivered. +-- @field #number ndelivered Number of groups delivered to destination. +-- @field #number cargoattribute Attribute of cargo assets of type @{#WAREHOUSE.Attribute}. +-- @field #number cargocategory Category of cargo assets of type @{#WAREHOUSE.Category}. +-- @field #table transportassets Table of assets to be delivered. Each element of the table is a @{#WAREHOUSE.Stockitem}. +-- @field Core.Set#SET_GROUP transportgroupset Set of cargo transport groups. +-- @field #number transportattribute Attribute of transport assets of type @{#WAREHOUSE.Attribute}. +-- @field #number transportcategory Category of transport assets of type @{#WAREHOUSE.Category}. +-- @field #number ntransporthome Number of transports back home. transportattribute --- Descriptors enumerator describing the type of the asset in stock. -- @type WAREHOUSE.Descriptor @@ -191,7 +196,7 @@ WAREHOUSE.TransportType = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.1.9" +WAREHOUSE.version="0.1.9w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -272,24 +277,25 @@ function WAREHOUSE:New(warehouse, alias) self:SetStartState("Stopped") -- Add FSM transitions. - self:AddTransition("Stopped", "Load", "Stopped") -- TODO Load the warehouse state. No sure if it should be in stopped state. - self:AddTransition("Stopped", "Start", "Running") -- Start the warehouse. - self:AddTransition("Running", "Status", "*") -- Status update in running mode. Requests are processed. - self:AddTransition("Paused", "Status", "*") -- TODO Status update in paused mode. Requests are not processed. - self:AddTransition("*", "AddAsset", "*") -- Add asset to warehouse stock. - self:AddTransition("*", "AddRequest", "*") -- New request from other warehouse. - self:AddTransition("Running", "Request", "*") -- Process a request. Only in running mode. - self:AddTransition("*", "Unloaded", "*") -- Cargo has been unloaded from the carrier. - self:AddTransition("*", "Arrived", "*") -- Cargo group has arrived at destination. - self:AddTransition("*", "Delivered", "*") -- All cargo groups of a request have been delivered to the requesting warehouse. - self:AddTransition("Running", "SelfRequest", "*") -- Request to warehouse itself. Requested assets are only spawned but not delivered anywhere. - self:AddTransition("Running", "Pause", "Paused") -- TODO Pause the processing of new requests. Still possible to add assets and requests. - self:AddTransition("Paused", "Unpause", "Running") -- TODO Unpause the warehouse. Queued requests are processed again. - self:AddTransition("*", "Stop", "Stopped") -- TODO Stop the warehouse. - self:AddTransition("*", "Save", "*") -- TODO Save the warehouse state to disk. - self:AddTransition("*", "Attacked", "*") -- TODO Warehouse is under attack by enemy coalitin. - self:AddTransition("*", "Captured", "*") -- TODO Warehouse was captured by another coalition. - self:AddTransition("*", "Destroyed", "*") -- TODO Warehouse was destoryed. All assets are gone and warehouse is stopped. + self:AddTransition("Stopped", "Load", "Stopped") -- TODO Load the warehouse state. No sure if it should be in stopped state. + self:AddTransition("Stopped", "Start", "Running") -- Start the warehouse. + self:AddTransition("Running", "Status", "*") -- Status update in running mode. Requests are processed. + self:AddTransition("Paused", "Status", "*") -- TODO Status update in paused mode. Requests are not processed. + self:AddTransition("*", "AddAsset", "*") -- Add asset to warehouse stock. + self:AddTransition("*", "AddRequest", "*") -- New request from other warehouse. + self:AddTransition("Running", "Request", "*") -- Process a request. Only in running mode. + self:AddTransition("*", "Unloaded", "*") -- Cargo has been unloaded from the carrier. + self:AddTransition("*", "Arrived", "*") -- Cargo group has arrived at destination. + self:AddTransition("*", "Delivered", "*") -- All cargo groups of a request have been delivered to the requesting warehouse. + self:AddTransition("Running", "SelfRequest", "*") -- Request to warehouse itself. Requested assets are only spawned but not delivered anywhere. + self:AddTransition("Running", "Pause", "Paused") -- TODO Pause the processing of new requests. Still possible to add assets and requests. + self:AddTransition("Paused", "Unpause", "Running") -- TODO Unpause the warehouse. Queued requests are processed again. + self:AddTransition("*", "Stop", "Stopped") -- TODO Stop the warehouse. + self:AddTransition("*", "Save", "*") -- TODO Save the warehouse state to disk. + self:AddTransition("*", "Attacked", "Attacked") -- TODO Warehouse is under attack by enemy coalition. + self:AddTransition("Attacked", "Defeated", "Running") -- TODO Attack by other coalition was defeated! + self:AddTransition("Attacked", "Captured", "Running") -- TODO Warehouse was captured by another coalition. It must have been attacked first. + self:AddTransition("*", "Destroyed", "*") -- TODO Warehouse was destoryed. All assets in stock are gone and warehouse is stopped. -- Pseudo Functions @@ -495,6 +501,13 @@ function WAREHOUSE:IsPaused() return self:is("Paused") end +--- Check if the warehouse is under attack by another coalition. +-- @param #WAREHOUSE self +-- @return #boolean If true, the warehouse is attacked. +function WAREHOUSE:IsAttacked() + return self:is("Attacked") +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- FSM states @@ -1492,8 +1505,21 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -function WAREHOUSE:onafterAttacked(From, Event, To) +-- @param DCS#coalition.side Coalition which is attacking the warehouse. +-- @param DCS#country.id Country which is attacking the warehouse. +function WAREHOUSE:onafterAttacked(From, Event, To, Coalition, Country) self:E(self.wid..string.format("Out warehouse is under attack!")) + --TODO: Spawn all ground units in the spawnzone? +end + +--- On after "Defeated" event. Warehouse defeated an attack by another coalition. +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function WAREHOUSE:onafterDefeated(From, Event, To) + self:E(self.wid..string.format("Attack was defeated!")) + --TODO Put all ground assets back in stock? How to remember which? Request id. Don't delete from pending? end --- On after "Captured" event. Warehouse has been captured by another coalition. @@ -1502,25 +1528,17 @@ end -- @param #string Event Event. -- @param #string To To state. -- @param DCS#coalition.side Coalition which captured the warehouse. -function WAREHOUSE:onafterCaptured(From, Event, To, Coalition) +-- @param DCS#country.id Country which has captured the warehouse. +function WAREHOUSE:onafterCaptured(From, Event, To, Coalition, Country) self:E(self.wid..string.format("Our warehouse was captured by coalition %d!", Coalition)) - --TODO: Need to get a way to get the correct country. - local Country - if Coalition==coalition.side.BLUE then - Country=country.id.USA - elseif Coalition==coalition.side.RED then - Country=country.id.USSR - else - Country=country.id.SWITZERLAND - end - -- Respawn warehouse with new coalition/country. self.warehouse:ReSpawn(Country) self.coalition=Coalition self.country=Country self.airbase=nil self.category=-1 + end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1540,6 +1558,8 @@ function WAREHOUSE:_RouteGround(Group, Coordinate, Speed) local _speed=Speed or Group:GetSpeedMax()*0.6 -- Create task. + -- TODO: It might be necessary to ALWAYS route the group to the road connection first. + -- At the moment, the random spawn point might give another first road point which could also be a dead end like in Kobuliti(?). local Waypoints, canroad = Group:TaskGroundOnRoad(Coordinate, _speed, "Off Road", true) -- Task function triggering the arrived event. @@ -1630,6 +1650,9 @@ function WAREHOUSE:_RouteAir(Aircraft, ToAirbase, Speed) return end + local Waypoints,Coordinates=self:_GetFlightplan(Aircraft,self.airbase,ToAirbase) + + --[[ -- Waypoints of the route. local Points={} @@ -1660,7 +1683,11 @@ function WAREHOUSE:_RouteAir(Aircraft, ToAirbase, Speed) -- Second point of the route. First point is done in RespawnAtCurrentAirbase() routine. Template.route.points[2] = ToWaypoint - + ]] + + -- Set waypoints. + Template.route.points=Waypoints + -- Respawn group at the current airbase. env.info("FF Respawn at current airbase group = "..Aircraft:GetName().." name before") local newAC=Aircraft:RespawnAtCurrentAirbase(Template, Takeoff, false) @@ -1881,8 +1908,100 @@ end -- Helper functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Checks if the warehouse zone was conquered by antoher coalition. +-- @param #WAREHOUSE self +function WAREHOUSE:_CheckConquered() + + local coord=self.zone:GetCoordinate() + local radius=self.zone:GetRadius() + + -- Scan units in zone. + --TODO: need to check if scan radius does what it should! + local gotunits,_,_,units,_,_=coord:ScanObjects(radius, true, false, false) + + local Nblue=0 + local Nred=0 + local Nneutral=0 + + local CountryBlue=nil + local CountryRed=nil + local CountryNeutral=nil + + if gotunits then + -- Loop over all units. + for _,_unit in pairs(units) do + local unit=_unit --Wrapper.Unit#UNIT + + -- Get coalition and country. + local _coalition=unit:GetCoalition() + local _country=unit:GetCountry() + + if _coalition==coalition.side.BLUE then + Nblue=Nblue+1 + CountryBlue=_country + elseif _coalition==coalition.side.RED then + Nred=Nred+1 + CountryRed=_country + else + Nneutral=Nneutral+1 + CountryNeutral=_country + end + + end + end + + + -- Figure out the new coalition if any. + -- Condition is that only units of one coalition are within the zone. + local newcoalition=self.coalition + local newcountry=self.country + if Nblue>0 and Nred==0 and Nneutral==0 then + -- Only blue units in zone ==> Zone goes to blue. + newcoalition=coalition.side.BLUE + newcountry=CountryBlue + elseif Nblue==0 and Nred>0 and Nneutral==0 then + -- Only red units in zone ==> Zone goes to red. + newcoalition=coalition.side.RED + newcountry=CountryRed + elseif Nblue==0 and Nred==0 and Nneutral>0 then + -- Only neutral units in zone but neutrals do not attack or even capture! + --newcoalition=coalition.side.NEUTRAL + newcountry=CountryNeutral + end + + -- Coalition has changed ==> warehouse was captured! + if self:IsAttacked() and newcoalition ~= self.coalition then + self:Captured(newcoalition, newcountry) + end + + -- Before a warehouse can be captured, it has to be attacked. + -- That is, even if only enemy units are present it is not immediately captured in order to spawn all ground assets for defence. + if self.coalition==coalition.side.BLUE then + -- Blue warehouse is running and we have red units in the zone. + if self:IsRunning() and Nred>0 then + self:__Attacked(coalition.side.RED, CountryRed) + end + -- Blue warehouse was under attack by blue but no more blue units in zone. + if self:IsAttacked() and Nred==0 then + self:Defeated() + end + elseif self.coalition==coalition.side.RED then + -- Red Warehouse is running and we have blue units in the zone. + if self:IsRunning() and Nblue>0 then + self:__Attacked(coalition.side.BLUE, CountryBlue) + end + -- Red warehouse was under attack by blue but no more blue units in zone. + if self:IsAttacked() and Nblue==0 then + self:Defeated() + end + elseif self.coalition==coalition.side.NEUTRAL then + -- Neutrals dont attack! + end + +end + --- Checks if the request can be fulfilled in general. If not, it is removed from the queue. --- Check if departure and destination bases are of the right type. +-- Check if departure and destination bases are of the right type. -- @param #WAREHOUSE self -- @param #table queue The queue which is holding the requests to check. -- @return #boolean If true, request can be executed. If false, something is not right. @@ -2655,6 +2774,249 @@ function WAREHOUSE:_PrintQueue(queue, name) end end + + +--- Make a flight plan from a departure to a destination airport. +-- @param #WAREHOUSE self +-- @param Wrapper.Group#GROUP group +-- @param Wrapper.Airbase#AIRBASE _departure Departure airbase. +-- @param Wrapper.Airbase#AIRBASE _destination Destination airbase. +-- @return #table Table of flightplan waypoints. +-- @return #table Table of flightplan coordinates. +function WAREHOUSE:_GetFlightplan(group,_departure,_destination) + + -- Group parameters. + local Vmax=group:GetSpeedMax()/3.6 + local Range=group:GetRange() + local _category=group:GetCategory() + local DCSDesc=group:GetDCSDesc() + local ceiling=DCSDesc.Hmax + local Vymax=DCSDesc.VyMax + + -- Max cruise speed 90% of max speed. + local VxCruiseMax=0.90*Vmax + + -- Min cruise speed 70% of max cruise or 600 km/h whichever is lower. + local VxCruiseMin = math.min(VxCruiseMax*0.70, 166) + + -- Cruise speed (randomized). Expectation value at midpoint between min and max. + local VxCruise = UTILS.RandomGaussian((VxCruiseMax-VxCruiseMin)/2+VxCruiseMin, (VxCruiseMax-VxCruiseMax)/4, VxCruiseMin, VxCruiseMax) + + -- Climb speed 90% ov Vmax but max 720 km/h. + local VxClimb = math.min(Vmax*0.90, 200) + + -- Descent speed 60% of Vmax but max 500 km/h. + local VxDescent = math.min(Vmax*0.60, 140) + + -- Holding speed is 90% of descent speed. + local VxHolding = VxDescent*0.9 + + -- Final leg is 90% of holding speed. + local VxFinal = VxHolding*0.9 + + -- Reasonably civil climb speed Vy=1500 ft/min = 7.6 m/s but max aircraft specific climb rate. + local VyClimb=math.min(7.6, Vymax) + + -- Climb angle in rad. + local AlphaClimb=math.asin(VyClimb/VxClimb) + + -- Descent angle in rad. Moderate 4 degrees. + local AlphaDescent=math.rad(4) + + -- Expected cruise level (peak of Gaussian distribution) + local FLcruise_expect=200*RAT.unit.FL2m + + --- DEPARTURE AIRPORT + + -- Coordinates of departure point. + local Pdeparture=_departure:GetCoordinate() + + -- Height ASL of departure point. + local H_departure=Pdeparture.y + + --- DESTINATION AIRPORT + + -- Position of destination airport. + local Pdestination=_destination:GetCoordinate() + + -- Height ASL of destination airport/zone. + local H_destination=Pdestination.y + + --- DESCENT/HOLDING POINT + + -- Get a random point between 5 and 20 km away from the destination. + local Rhmin=8000 + local Rhmax=20000 + if _category==Group.Category.HELICOPTER then + -- For helos we set a distance between 500 to 1000 m. + Rhmin=500 + Rhmax=1000 + end + + -- Coordinates of the holding point. y is the land height at that point. + local Vholding=Pdestination:GetRandomVec2InRadius(Rhmax, Rhmin) + local Pholding=COORDINATE:NewFromVec2(Vholding) + + -- AGL height of holding point. + local H_holding=Pholding.y + + -- Holding point altitude. For planes between 1600 and 2400 m AGL. For helos 160 to 240 m AGL. + local h_holding=1200 + if _category==Group.Category.HELICOPTER then + h_holding=150 + end + h_holding=UTILS.Randomize(h_holding, 0.2) + + -- This is the actual height ASL of the holding point we want to fly to + local Hh_holding=H_holding+h_holding + + -- Distance from holding point to final destination. + local d_holding=Pholding:Get2DDistance(Pdestination) + + -- GENERAL + local heading=Pdeparture:HeadingTo(Pdestination) + local d_total=Pdeparture:Get2DDistance(Pholding) + + -------------------------------------------- + + -- Height difference between departure and destination. + local deltaH=math.abs(H_departure-Hh_holding) + + -- Slope between departure and destination. + local phi = math.atan(deltaH/d_total) + + -- Adjusted climb/descent angles. + local phi_climb + local phi_descent + if (H_departure > Hh_holding) then + phi_climb=AlphaClimb+phi + phi_descent=AlphaDescent-phi + else + phi_climb=AlphaClimb-phi + phi_descent=AlphaDescent+phi + end + + -- Total distance including slope. + local D_total=math.sqrt(deltaH*deltaH+d_total*d_total) + + -- SSA triangle for sloped case. + local gamma=math.rad(180)-phi_climb-phi_descent + local a = D_total*math.sin(phi_climb)/math.sin(gamma) + local b = D_total*math.sin(phi_descent)/math.sin(gamma) + local hphi_max = b*math.sin(phi_climb) + local hphi_max2 = a*math.sin(phi_descent) + + -- Height of triangle. + local h_max1 = b*math.sin(AlphaClimb) + local h_max2 = a*math.sin(AlphaDescent) + + -- Max height relative to departure or destination. + local h_max + if (H_departure > Hh_holding) then + h_max=math.min(h_max1, h_max2) + else + h_max=math.max(h_max1, h_max2) + end + + -- Max flight level aircraft can reach for given angles and distance. + local FLmax = h_max+H_departure + + --CRUISE + -- Min cruise alt is just above holding point at destination or departure height, whatever is larger. + local FLmin=math.max(H_departure, Hh_holding) + + -- For helicopters we take cruise alt between 50 to 1000 meters above ground. Default cruise alt is ~150 m. + if _category==Group.Category.HELICOPTER then + FLmin=math.max(H_departure, H_destination)+50 + FLmax=math.max(H_departure, H_destination)+1000 + end + + -- Ensure that FLmax not above its service ceiling. + FLmax=math.min(FLmax, ceiling) + + -- If the route is very short we set FLmin a bit lower than FLmax. + if FLmin>FLmax then + FLmin=FLmax + end + + -- Expected cruise altitude - peak of gaussian distribution. + if FLcruise_expectFLmax then + FLcruise_expect=FLmax + end + + -- Set cruise altitude. Selected from Gaussian distribution but limited to FLmin and FLmax. + local FLcruise=UTILS.RandomGaussian(FLcruise_expect, math.abs(FLmax-FLmin)/4, FLmin, FLmax) + + -- Climb and descent heights. + local h_climb = FLcruise - H_departure + local h_descent = FLcruise - Hh_holding + + -- Distances. + local d_climb = h_climb/math.tan(AlphaClimb) + local d_descent = h_descent/math.tan(AlphaDescent) + local d_cruise = d_total-d_climb-d_descent + + -- Ensure that cruise distance is positve. Can be slightly negative in special cases. And we don't want to turn back. + if d_cruise<0 then + d_cruise=100 + end + + -- Waypoints and coordinates + local wp={} + local c={} + + --- Departure/Take-off + c[#c+1]=Pdeparture + wp[#wp+1]=Pdeparture:WaypointAir("RADIO", COORDINATE.WaypointType.TakeOffParking, COORDINATE.WaypointAction.FromParkingArea, VxClimb, true,_departure, nil, "Departure") + --wp[#wp+1]=self:_Waypoint(#wp+1, "Departure", takeoff, c[#wp+1], VxClimb, H_departure, departure) + + --- Climb + local Pclimb=Pdeparture:Translate(d_climb/2, heading) + Pclimb.y=H_departure+(FLcruise-H_departure)/2 + c[#c+1]=Pclimb + wp[#wp+1]=Pclimb:WaypointAir("BARO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, VxClimb, true, nil, nil, "Climb") + --wp[#wp+1]=self:_Waypoint(#wp+1, "Climb", RAT.wp.climb, c[#wp+1], VxClimb, ) + + --- Begin of Cruise + local Pcruise1=Pclimb:Translate(d_climb/2, heading) + Pcruise1.y=FLcruise + c[#c+1]=Pcruise1 + wp[#wp+1]=Pcruise1:WaypointAir("BARO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, VxCruise, true, nil, nil, "Begin of Cruise") + --wp[#wp+1]=self:_Waypoint(#wp+1, "Begin of Cruise", RAT.wp.cruise, c[#wp+1], VxCruise, FLcruise) + + --- End of Cruise + local Pcruise2=Pcruise1:Translate(d_cruise, heading) + Pcruise2.y=FLcruise + c[#c+1]=Pcruise2 + wp[#wp+1]=Pcruise2:WaypointAir("BARO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, VxCruise, true, nil, nil, "End of Cruise") + --wp[#wp+1]=self:_Waypoint(#wp+1, "End of Cruise", RAT.wp.cruise, c[#wp+1], VxCruise, FLcruise) + + --- Descent + local Pdescent=Pcruise2:Translate(d_descent/2, heading) + Pdescent.y=FLcruise-(FLcruise-(h_holding+H_holding))/2 + c[#c+1]=Pdescent + wp[#wp+1]=Pcruise2:WaypointAir("BARO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, VxDescent, true, nil, nil, "Descent") + --wp[#wp+1]=self:_Waypoint(#wp+1, "Descent", RAT.wp.descent, c[#wp+1], VxDescent, FLcruise-(FLcruise-(h_holding+H_holding))/2) + + --- Holding point + Pholding.y=H_holding+h_holding + c[#c+1]=Pholding + wp[#wp+1]=Pholding:WaypointAir("BARO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, VxHolding, true, nil, nil, "Holding") + --wp[#wp+1]=self:_Waypoint(#wp+1, "Holding Point", RAT.wp.holding, c[#wp+1], VxHolding, H_holding+h_holding) + + --- Final destination. + c[#c+1]=Pdestination + wp[#wp+1]=Pcruise2:WaypointAir("BARO", COORDINATE.WaypointType.Land, COORDINATE.WaypointAction.Landing, VxFinal, true, nil, nil, "Final Destination") + --wp[#wp+1]=self:_Waypoint(#wp+1, "Final Destination", landing, c[#wp+1], VxFinal, H_destination, destination) + + return wp,c +end + + + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 3ac8ca74e..1ccee1d39 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -591,4 +591,71 @@ function UTILS.DisplayMissionTime(duration) local local_time=UTILS.SecondsToClock(Tnow) local text=string.format("Time: %s - %02d:%02d", local_time, mission_time_minutes, mission_time_seconds) MESSAGE:New(text, duration):ToAll() -end \ No newline at end of file +end + + +--- Generate a Gaussian pseudo-random number. +-- @param #number x0 Expectation value of distribution. +-- @param #number sigma (Optional) Standard deviation. Default 10. +-- @param #number xmin (Optional) Lower cut-off value. +-- @param #number xmax (Optional) Upper cut-off value. +-- @param #number imax (Optional) Max number of tries to get a value between xmin and xmax (if specified). Default 100. +-- @return #number Gaussian random number. +function UTILS.RandomGaussian(x0, sigma, xmin, xmax, imax) + + -- Standard deviation. Default 10 if not given. + sigma=sigma or 10 + + -- Max attempts. + imax=imax or 100 + + local r + local gotit=false + local i=0 + while not gotit do + + -- Uniform numbers in [0,1). We need two. + local x1=math.random() + local x2=math.random() + + -- Transform to Gaussian exp(-(x-x0)²/(2*sigma²). + r = math.sqrt(-2*sigma*sigma * math.log(x1)) * math.cos(2*math.pi * x2) + x0 + + i=i+1 + if (r>=xmin and r<=xmax) or i>imax then + gotit=true + end + end + + return r +end + +--- Randomize a value by a certain amount. +-- @param #number value The value which should be randomized +-- @param #number fac Randomization factor. +-- @param #number lower (Optional) Lower limit of the returned value. +-- @param #number upper (Optional) Upper limit of the returned value. +-- @return #number Randomized value. +-- @usage UTILS.Randomize(100, 0.1) returns a value between 90 and 110, i.e. a plus/minus ten percent variation. +-- @usage UTILS.Randomize(100, 0.5, nil, 120) returns a value between 50 and 120, i.e. a plus/minus fivty percent variation with upper bound 120. +function UTILS.Randomize(value, fac, lower, upper) + local min + if lower then + min=math.max(value-value*fac, lower) + else + min=value-value*fac + end + local max + if upper then + max=math.min(value+value*fac, upper) + else + max=value+value*fac + end + + local r=math.random(min, max) + + return r +end + + + diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 96b44357d..10174dd5b 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1620,6 +1620,23 @@ function GROUP:InAir() return nil end +--- Returns the DCS descriptor table of the nth unit of the group. +-- @param #GROUP self +-- @param #number n (Optional) The number of the unit for which the dscriptor is returned. +-- @return DCS#Object.Desc The descriptor of the first unit of the group or #nil if the group does not exist any more. +function GROUP:GetDCSDesc(n) + -- Default. + n=n or 1 + + local unit=self:GetUnit(n) + if unit and unit:IsAlive()~=nil then + local desc=unit:GetDesc() + return desc + end + + return nil +end + do -- Route methods --- (AIR) Return the Group to an @{Wrapper.Airbase#AIRBASE}. diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index e992a3ebd..303bb1fdd 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -428,7 +428,7 @@ function UNIT:GetRange() local Desc = self:GetDesc() if Desc then - local Range = Desc.range --This is in nautical miles for some reason. + local Range = Desc.range --This is in nautical miles for some reason. But should check again! if Range then Range=UTILS.NMToMeters(Range) else From 76ce28cdccb26af19cdfb79760b4a7e5af65e7d2 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sat, 18 Aug 2018 01:29:19 +0200 Subject: [PATCH 265/420] Warehouse 0.20 Capture update --- Moose Development/Moose/Core/Point.lua | 6 +- .../Moose/Functional/Warehouse.lua | 322 +++++++++++------- 2 files changed, 195 insertions(+), 133 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index d2d9884a6..77aedec5c 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -397,7 +397,7 @@ do -- COORDINATE local gotunits=false local gotscenery=false - local function EvaluateZone( ZoneObject ) + local function EvaluateZone(ZoneObject) if ZoneObject then @@ -408,7 +408,7 @@ do -- COORDINATE --if (ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive()) then if (ObjectCategory == Object.Category.UNIT and ZoneObject:isExist()) then - table.insert(Units, ZoneObject) + table.insert(Units, UNIT:Find(ZoneObject)) gotunits=true elseif (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then @@ -432,7 +432,7 @@ do -- COORDINATE world.searchObjects(scanobjects, SphereSearch, EvaluateZone) for _,unit in pairs(Units) do - self:T(string.format("Scan found unit %s", unit:getName())) + self:T(string.format("Scan found unit %s", unit:GetName())) end for _,static in pairs(Statics) do self:T(string.format("Scan found static %s", static:getName())) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index d9eea2a1f..ac3aa3cff 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -34,10 +34,10 @@ -- @field Core.Point#COORDINATE road Closest point to warehouse on road. -- @field Core.Point#COORDINATE rail Closest point to warehouse on rail. -- @field Core.Zone#ZONE spawnzone Zone in which assets are spawned. --- @field Functional.ZoneCaptureCoalition#ZONE_CAPTURE_COALITION capturezone Zone capture object handling the capturing of the warehouse spawn zone. -- @field #string wid Identifier of the warehouse printed before other output to DCS.log file. -- @field #number uid Unit identifier of the warehouse. Derived from the associated airbase. -- @field #number markerid ID of the warehouse marker at the airbase. +-- @field #number dTstatus Time interval in seconds of updating the warehouse status and processing new events. Default 30 seconds. -- @field #number assetid Unique id of asset items in stock. Essentially a running number starting at one and incremented when a new asset is added. -- @field #table stock Table holding all assets in stock. Table entries are of type @{#WAREHOUSE.Stockitem}. -- @field #table queue Table holding all queued requests. Table entries are of type @{#WAREHOUSE.Queueitem}. @@ -88,28 +88,28 @@ WAREHOUSE = { ClassName = "WAREHOUSE", Debug = false, - Report = true, - warehouse = nil, - coalition = nil, - country = nil, - alias = nil, - zone = nil, - airbase = nil, - airbasename = nil, - category = -1, - coordinate = nil, - road = nil, - rail = nil, - spawnzone = nil, - capturezone = nil, - wid = nil, - uid = nil, - markerid = nil, - assetid = 0, - queueid = 0, - stock = {}, - queue = {}, - pending = {}, + Report = true, + warehouse = nil, + coalition = nil, + country = nil, + alias = nil, + zone = nil, + airbase = nil, + airbasename = nil, + category = -1, + coordinate = nil, + road = nil, + rail = nil, + spawnzone = nil, + wid = nil, + uid = nil, + markerid = nil, + dTstatus = 30, + assetid = 0, + queueid = 0, + stock = {}, + queue = {}, + pending = {}, } --- Item of the warehouse stock table. @@ -196,7 +196,7 @@ WAREHOUSE.TransportType = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.1.9w" +WAREHOUSE.version="0.2.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -230,8 +230,6 @@ WAREHOUSE.version="0.1.9w" -- @param #WAREHOUSE self -- @param Wrapper.Static#STATIC warehouse The physical structure of the warehouse. -- @param #string alias (Optional) Alias of the warehouse, i.e. the name it will be called when sending messages etc. Default is the name of the static --- @param Core.Zone#ZONE spawnzone (Optional) The zone in which units are spawned and despawned when they leave or arrive the warehouse. Default is a zone of 200 meters around the warehouse. --- @param Wrapper.Airbase#AIRBASE airbase (Optional) The airbase belonging to the warehouse. Default is the closest airbase to the warehouse structure as long as it within a range of 3 km. -- @return #WAREHOUSE self function WAREHOUSE:New(warehouse, alias) BASE:E({warehouse=warehouse:GetName()}) @@ -279,15 +277,16 @@ function WAREHOUSE:New(warehouse, alias) -- Add FSM transitions. self:AddTransition("Stopped", "Load", "Stopped") -- TODO Load the warehouse state. No sure if it should be in stopped state. self:AddTransition("Stopped", "Start", "Running") -- Start the warehouse. - self:AddTransition("Running", "Status", "*") -- Status update in running mode. Requests are processed. - self:AddTransition("Paused", "Status", "*") -- TODO Status update in paused mode. Requests are not processed. + self:AddTransition("*", "Status", "*") -- Status update. self:AddTransition("*", "AddAsset", "*") -- Add asset to warehouse stock. self:AddTransition("*", "AddRequest", "*") -- New request from other warehouse. self:AddTransition("Running", "Request", "*") -- Process a request. Only in running mode. + self:AddTransition("Attacked", "Request", "*") -- Process a request. Only in running mode. self:AddTransition("*", "Unloaded", "*") -- Cargo has been unloaded from the carrier. self:AddTransition("*", "Arrived", "*") -- Cargo group has arrived at destination. self:AddTransition("*", "Delivered", "*") -- All cargo groups of a request have been delivered to the requesting warehouse. self:AddTransition("Running", "SelfRequest", "*") -- Request to warehouse itself. Requested assets are only spawned but not delivered anywhere. + self:AddTransition("Attacked", "SelfRequest", "*") -- Request to warehouse itself. Also possible when warehouse is under attack! self:AddTransition("Running", "Pause", "Paused") -- TODO Pause the processing of new requests. Still possible to add assets and requests. self:AddTransition("Paused", "Unpause", "Running") -- TODO Unpause the warehouse. Queued requests are processed again. self:AddTransition("*", "Stop", "Stopped") -- TODO Stop the warehouse. @@ -428,6 +427,15 @@ end -- User functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Set interval of status updates +-- @param #WAREHOUSE self +-- @param #number timeinterval Time interval in seconds. +-- @return #WAREHOUSE self +function WAREHOUSE:SetSpawnZone(timeinterval) + self.dTstatus=timeinterval + return self +end + --- Set a zone where the (ground) assets of the warehouse are spawned once requested. -- @param #WAREHOUSE self -- @param Core.Zone#ZONE zone The spawn zone. @@ -572,32 +580,6 @@ function WAREHOUSE:onafterStart(From, Event, To) if self.rail then self.rail:MarkToAll(string.format("%s rail connection.", self.alias), true) end - - -- Create a zone capture object. - self.capturezone=ZONE_CAPTURE_COALITION:New(self.zone, self.coalition) - - -- Add warehouse to zone capture object. Does this work? - self.capturezone.warehouse=self - - -- Start capturing monitoring. - self.capturezone:Start(10, 60) - - -- Handle attack. - function self.capturezone:OnEnterAttacked() - local coalition = self:GetCoalition() - self:E(string.format("Warehouse %s is under attack!", tostring(self.warehouse.alias))) - -- Trigger FSM Attacked event. - self.warehouse:Attacked() - end - - -- Handle capturing. - function self.capturezone:OnEnterCaptured() - local coalition = self:GetCoalition() - self:E(string.format("Warehouse %s was captured by coalition %d!", tostring(self.warehouse.alias), coalition)) - self.warehouse.coalition=coalition --:SetCoalition(coalition) - self.warehouse:Captured(coalition) - self:Guard() - end -- Handle events: self:HandleEvent(EVENTS.Birth, self._OnEventBirth) @@ -616,7 +598,7 @@ function WAREHOUSE:onafterStart(From, Event, To) self:HandleEvent(EVENTS.EngineShutdown, self._OnEventArrived) -- Start the status monitoring. - self:__Status(5) + self:__Status(1) end --- On after "Stop" event. Stops the warehouse, unhandles all events. @@ -637,8 +619,6 @@ function WAREHOUSE:onafterStop(From, Event, To) self:UnHandleEvent(EVENTS.Dead) self:UnHandleEvent(EVENTS.BaseCaptured) - -- Stop capture zone FSM. - self.capturezone:Stop() end --- On after "Pause" event. Pauses the warehouse, i.e. no requests are processed. However, new requests and new assets can be added in this state. @@ -669,32 +649,11 @@ end function WAREHOUSE:onafterStatus(From, Event, To) self:E(self.wid..string.format("Checking warehouse status of %s", self.alias)) - -- Print queue. - self:_PrintQueue(self.queue, "Queue0:") - self:_PrintQueue(self.pending, "Pending0:") - - -- Create a mark with the current assets in stock. - if self.markerid~=nil then - trigger.action.removeMark(self.markerid) - end - local marktext="Warehouse stock:\n" - local text="Warehouse stock:\n" - - local _data=self:GetStockInfo(self.stock) - for _attribute,_count in pairs(_data) do - marktext=marktext..string.format("%s=%d, ", _attribute,_count) -- Dont use \n because too many make DCS crash! - text=text..string.format("%s = %d\n", _attribute,_count) - end - self.markerid=self.coordinate:MarkToCoalition(marktext, self.coalition, true) - - -- Debug output. - self:E(self.wid..text) - MESSAGE:New(text, 10):ToAllIf(self.Debug) - - -- Display complete list of stock itmes. - if self.Debug then - --self:_DisplayStockItems(self.stock) - end + -- Print status. + self:_DisplayStatus() + + -- Check if warehouse is being attacked or has even been captured. + self:_CheckConquered() -- Print queue. self:_PrintQueue(self.queue, "Queue:") @@ -702,14 +661,9 @@ function WAREHOUSE:onafterStatus(From, Event, To) -- Check if requests are valid and remove invalid one. self:_CheckRequestConsistancy(self.queue) - - -- Print queue. - self:_PrintQueue(self.queue, "Queue after consitancy:") - self:_PrintQueue(self.pending, "Pending after consistancy:") - - + -- If warehouse is running than requests can be processed. - if self:IsRunning() then + if self:IsRunning() or self:IsAttacked() then -- Check queue and handle requests if possible. local request=self:_CheckQueue() @@ -719,12 +673,16 @@ function WAREHOUSE:onafterStatus(From, Event, To) end end - -- Print queue. - self:_PrintQueue(self.queue, "Queue after request:") - self:_PrintQueue(self.pending, "Pending after request:") + -- Update warhouse marker on F10 map. + self:_UpdateWarehouseMarkText() + + -- Display complete list of stock itmes. + if self.Debug then + --self:_DisplayStockItems(self.stock) + end - -- Call status again in 30 sec. - self:__Status(30) + -- Call status again in ~30 sec (user choice). + self:__Status(self.dTstatus) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -874,7 +832,7 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request) local distance=self.coordinate:Get2DDistance(Request.warehouse.coordinate) -- Filter the requested assets. - local _assets=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval, Request.nasset) + local _assets,_nasset,_enough=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval, Request.nasset) -- Check if destination is in range for all requested assets. for _,_asset in pairs(_assets) do @@ -896,8 +854,8 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request) end -- Asset is not in stock ==> request denied. - if #_assets < Request.nasset then - local text=string.format("Request denied! Not enough assets currently in stock. Requested %d < %d in stock.", Request.nasset, #_assets) + if not _enough then + local text=string.format("Request denied! Not enough assets currently in stock. Requested %s < %d in stock.", tostring(Request.nasset), _nasset) MESSAGE:New(text, 10):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:E(self.wid..text) return false @@ -967,8 +925,8 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Self request! Assets are only spawned but not routed or transported anywhere. if self.warehouse:GetName()==Request.warehouse.warehouse:GetName() then - env.info("FF selfrequest!") - self:__SelfRequest(_spawngroups) + self:E(self.wid..string.format("Selfrequest! Current status %s", self:GetState())) + self:__SelfRequest(1,_spawngroups, Pending) return end @@ -1254,10 +1212,10 @@ end function WAREHOUSE:_SpawnAssetRequest(Request) -- Filter the requested cargo assets. - local _assetstock=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval, Request.nasset) + local _assetstock,_nasset,_enough=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval, Request.nasset) -- No assets in stock :( - if #_assetstock==0 then + if not _enough then return nil,nil,nil end @@ -1287,7 +1245,7 @@ function WAREHOUSE:_SpawnAssetRequest(Request) local _assets={} -- Loop over cargo requests. - for i=1,Request.nasset do + for i=1,#_assetstock do -- Get stock item. local _assetitem=_assetstock[i] --#WAREHOUSE.Stockitem @@ -1405,7 +1363,7 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) local ncargo=request.cargogroupset:Count() -- Info - self:E(self.wid..string.format("Cargo %d of %d arrived at warehouse %s. Assets still to deliver %d.",request.ndelivered, request.nasset, request.warehouse.alias, ncargo)) + self:E(self.wid..string.format("Cargo %d of %s arrived at warehouse %s. Assets still to deliver %d.",request.ndelivered, tostring(request.nasset), request.warehouse.alias, ncargo)) -- Move asset into new warehouse. -- TODO: need to figure out which template group name I best take. @@ -1508,8 +1466,9 @@ end -- @param DCS#coalition.side Coalition which is attacking the warehouse. -- @param DCS#country.id Country which is attacking the warehouse. function WAREHOUSE:onafterAttacked(From, Event, To, Coalition, Country) - self:E(self.wid..string.format("Out warehouse is under attack!")) + self:E(self.wid..string.format("Our warehouse is under attack!")) --TODO: Spawn all ground units in the spawnzone? + self:AddRequest(self, WAREHOUSE.Descriptor.CATEGORY, Group.Category.GROUND, "all", nil, nil , 0) end --- On after "Defeated" event. Warehouse defeated an attack by another coalition. @@ -1911,6 +1870,7 @@ end --- Checks if the warehouse zone was conquered by antoher coalition. -- @param #WAREHOUSE self function WAREHOUSE:_CheckConquered() + env.info("FF checking conq") local coord=self.zone:GetCoordinate() local radius=self.zone:GetRadius() @@ -1949,6 +1909,9 @@ function WAREHOUSE:_CheckConquered() end end + + -- Debug info. + self:E(self.wid..string.format("Troops at warehouse: blue=%d, red=%d, neutral=%d", Nblue, Nred, Nneutral)) -- Figure out the new coalition if any. @@ -1979,7 +1942,7 @@ function WAREHOUSE:_CheckConquered() if self.coalition==coalition.side.BLUE then -- Blue warehouse is running and we have red units in the zone. if self:IsRunning() and Nred>0 then - self:__Attacked(coalition.side.RED, CountryRed) + self:Attacked(coalition.side.RED, CountryRed) end -- Blue warehouse was under attack by blue but no more blue units in zone. if self:IsAttacked() and Nred==0 then @@ -1988,7 +1951,7 @@ function WAREHOUSE:_CheckConquered() elseif self.coalition==coalition.side.RED then -- Red Warehouse is running and we have blue units in the zone. if self:IsRunning() and Nblue>0 then - self:__Attacked(coalition.side.BLUE, CountryBlue) + self:Attacked(coalition.side.BLUE, CountryBlue) end -- Red warehouse was under attack by blue but no more blue units in zone. if self:IsAttacked() and Nblue==0 then @@ -2248,10 +2211,10 @@ function WAREHOUSE:_CheckRequestNow(request) local okay=true -- Check if number of requested assets is in stock. - local _assets=self:_FilterStock(self.stock, request.assetdesc, request.assetdescval, request.nasset) + local _assets,_nassets,_enough=self:_FilterStock(self.stock, request.assetdesc, request.assetdescval, request.nasset) -- Nothing in stock. - if #_assets==0 then + if _nassets==0 then local text=string.format("Request denied! No assets for this request currently available.") MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:E(self.wid..text) @@ -2263,7 +2226,7 @@ function WAREHOUSE:_CheckRequestNow(request) local _assetcategory=_assets[1].category -- Check if enough assets are in stock. - if request.nasset > #_assets then + if not _enough then local text=string.format("Request denied! Not enough assets currently available.") MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:E(self.wid..text) @@ -2566,40 +2529,55 @@ end -- @param #table stock Table holding all assets in stock of the warehouse. Each entry is of type @{#WAREHOUSE.Stockitem}. -- @param #string item Descriptor -- @param value Value of the descriptor. --- @param #number nmax (Optional) Maximum number of items that will be returned. Default is all matching items are returned. +-- @param #number nmax (Optional) Maximum number of items that will be returned. Default nmax=nil is all matching items are returned. -- @return #table Filtered stock items table. +-- @return #number Total number of (requested) assets available. +-- @return #boolean Enough assets are available. function WAREHOUSE:_FilterStock(stock, item, value, nmax) + -- Default all. + nmax=nmax or "all" + -- Filtered array. local filtered={} + -- Count total number in stock. + local ntot=0 + for _,_stock in ipairs(stock) do + if _stock[item]==value then + ntot=ntot+1 + end + end + + -- Handle string input for nmax. + if type(nmax)=="string" then + if nmax:lower()=="all" then + nmax=ntot + elseif nmax:lower()=="half" then + nmax=ntot/2 + elseif nmax:lower()=="third" then + nmax=ntot/3 + elseif namx:lower()=="quarter" then + nmax=ntot/4 + elseif namx:lower()=="fivth" then + nmax=ntot/5 + else + nmax=math.min(1,ntot) + end + end + -- Loop over stock items. for _i,_stock in ipairs(stock) do if _stock[item]==value then _stock.pos=_i table.insert(filtered, _stock) if nmax~=nil and #filtered>=nmax then - return filtered + return filtered, ntot, true end end end - return filtered -end - ---- Filter stock assets by table entry. --- @param #WAREHOUSE self --- @param #table stock Table holding all assets in stock of the warehouse. Each entry is of type @{#WAREHOUSE.Stockitem}. -function WAREHOUSE:_DisplayStockItems(stock) - - local text=self.wid..string.format("Warehouse %s stock assets:\n", self.airbase:GetName()) - for _,_stock in pairs(stock) do - local mystock=_stock --#WAREHOUSE.Stockitem - text=text..string.format("template = %s, category = %d, unittype = %s, attribute = %s\n", mystock.templatename, mystock.category, mystock.unittype, mystock.attribute) - end - - env.info(text) - MESSAGE:New(text, 10):ToAll() + return filtered, ntot, nmax>=ntot end --- Check if a group has a generalized attribute. @@ -2768,13 +2746,97 @@ function WAREHOUSE:_PrintQueue(queue, name) self:E(self.wid..name) for _,_qitem in ipairs(queue) do local qitem=_qitem --#WAREHOUSE.Queueitem - local text=self.wid..string.format("UID=%d, Prio=%d, Requestor=%s, Airbase=%s (category=%d), Descriptor: %s=%s, Nasssets=%d, Transport=%s, Ntransport=%d", - qitem.uid, qitem.prio, qitem.warehouse.alias, qitem.airbase:GetName(),qitem.category, qitem.assetdesc,tostring(qitem.assetdescval),qitem.nasset,qitem.transporttype,qitem.ntransport) + local text=self.wid..string.format("UID=%d, Prio=%d, Requestor=%s, Airbase=%s (category=%d), Descriptor: %s=%s, Nasssets=%s, Transport=%s, Ntransport=%d", + qitem.uid, qitem.prio, qitem.warehouse.alias, qitem.airbase:GetName(),qitem.category, qitem.assetdesc,tostring(qitem.assetdescval), tostring(qitem.nasset),qitem.transporttype,qitem.ntransport) self:E(text) end end +--- Display status of warehouse. +-- @param #WAREHOUSE self +function WAREHOUSE:_DisplayStatus() + -- Set airbase name. + local airbasename="none" + if self.airbase then + airbasename=self.airbase:GetName() + end + + local text=string.format("\n------------------------------------------------------\n") + text=text..string.format("Warehouse %s status:\n", self.alias) + text=text..string.format("------------------------------------------------------\n") + text=text..string.format("Current status = %s\n", self:GetState()) + text=text..string.format("Coalition side = %d\n", self.coalition) + text=text..string.format("Country name = %d\n", self.country) + text=text..string.format("Airbase name = %s\n", airbasename) + text=text..string.format("Queued requests = %d\n", #self.queue) + text=text..string.format("Pending requests = %d\n", #self.pending) + text=text..string.format("------------------------------------------------------\n") + text=text..self:_GetStockAssetsText() + env.info(text) + --TODO: number of ground, air, naval assets. +end + +--- Get text about warehouse stock. +-- @param #WAREHOUSE self +-- @param #boolean messagetoall If true, send message to all. +-- @return #string Text about warehouse stock +function WAREHOUSE:_GetStockAssetsText(messagetoall) + + -- Get assets in stock. + local _data=self:GetStockInfo(self.stock) + + -- Text. + local text="Stock:\n" + for _attribute,_count in pairs(_data) do + text=text..string.format("%s = %d\n", _attribute,_count) + end + text=text..string.format("------------------------------------------------------\n") + + -- Send message? + MESSAGE:New(text, 10):ToAllIf(messagetoall) + + return text +end + +--- Create or update mark text at warehouse, which is displayed in F10 map. +-- Only the coaliton of the warehouse owner is able to see it. +-- @param #WAREHOUSE self +-- @return #string Text about warehouse stock +function WAREHOUSE:_UpdateWarehouseMarkText() + + -- Create a mark with the current assets in stock. + if self.markerid~=nil then + trigger.action.removeMark(self.markerid) + end + + -- Get assets in stock. + local _data=self:GetStockInfo(self.stock) + + -- Create mark text. + local marktext="Warehouse stock:\n" + for _attribute,_count in pairs(_data) do + marktext=marktext..string.format("%s=%d, ", _attribute,_count) -- Dont use \n because too many make DCS crash! + end + + -- Create/update marker at warehouse in F10 map. + self.markerid=self.coordinate:MarkToCoalition(marktext, self.coalition, true) +end + +--- Display stock items of warehouse. +-- @param #WAREHOUSE self +-- @param #table stock Table holding all assets in stock of the warehouse. Each entry is of type @{#WAREHOUSE.Stockitem}. +function WAREHOUSE:_DisplayStockItems(stock) + + local text=self.wid..string.format("Warehouse %s stock assets:\n", self.airbase:GetName()) + for _,_stock in pairs(stock) do + local mystock=_stock --#WAREHOUSE.Stockitem + text=text..string.format("template = %s, category = %d, unittype = %s, attribute = %s\n", mystock.templatename, mystock.category, mystock.unittype, mystock.attribute) + end + + env.info(text) + MESSAGE:New(text, 10):ToAll() +end --- Make a flight plan from a departure to a destination airport. -- @param #WAREHOUSE self From 8bcdbef4262a997dfe31d2871db6485d2f1ea042 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sun, 19 Aug 2018 00:19:55 +0200 Subject: [PATCH 266/420] Warehouse v0.2.1 --- Moose Development/Moose/Core/Point.lua | 42 +- Moose Development/Moose/Core/Spawn.lua | 3 +- .../Moose/Functional/Warehouse.lua | 524 +++++++++++++----- Moose Development/Moose/Wrapper/Airbase.lua | 9 +- 4 files changed, 440 insertions(+), 138 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 77aedec5c..b67ed1bef 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1275,9 +1275,12 @@ do -- COORDINATE -- @param #COORDINATE ToCoord Coordinate of destination. -- @param #boolean IncludeEndpoints (Optional) Include the coordinate itself and the ToCoordinate in the path. -- @param #boolean Railroad (Optional) If true, path on railroad is returned. Default false. + -- @param #boolean MarkPath (Optional) If true, place markers on F10 map along the path. + -- @param #boolean SmokePath (Optional) If true, put (green) smoke along the -- @return #table Table of coordinates on road. If no path on road can be found, nil is returned or just the endpoints. - -- @return #number The length of the total path. - function COORDINATE:GetPathOnRoad(ToCoord, IncludeEndpoints, Railroad) + -- @return #number Tonal length of path. + -- @return #boolean If true a valid path on road/rail was found. If false, only the direct way is possible. + function COORDINATE:GetPathOnRoad(ToCoord, IncludeEndpoints, Railroad, MarkPath, SmokePath) -- Set road type. local RoadType="roads" @@ -1296,20 +1299,43 @@ do -- COORDINATE if IncludeEndpoints then Path[1]=self end + + -- Assume we could get a valid path. + local GotPath=true -- Check that DCS routine actually returned a path. There are situations where this is not the case. if path then -- Include all points on road. - for _,_vec2 in ipairs(path) do - Path[#Path+1]=COORDINATE:NewFromVec2(_vec2) - --COORDINATE:NewFromVec2(_vec2):SmokeGreen() + for _i,_vec2 in ipairs(path) do + + local coord=COORDINATE:NewFromVec2(_vec2) + + Path[#Path+1]=coord + + if MarkPath then + coord:MarkToAll(string.format("Path segment %d.", _i)) + end + if SmokePath then + coord:SmokeGreen() + end + end + + -- Mark/smoke endpoints + if IncludeEndpoints then + if MarkPath then + COORDINATE:NewFromVec2(path[1]):MarkToAll("Path Initinal Point") + COORDINATE:NewFromVec2(path[1]):MarkToAll("Path Final Point") + end + if SmokePath then + COORDINATE:NewFromVec2(path[1]):SmokeBlue() + COORDINATE:NewFromVec2(path[#path]):SmokeBlue() + end end - --COORDINATE:NewFromVec2(path[1]):SmokeBlue() - --COORDINATE:NewFromVec2(path[#path]):SmokeBlue() else self:E("Path is nil. No valid path on road could be found.") + GotPath=false end -- Include end point, which might not be on road. @@ -1327,7 +1353,7 @@ do -- COORDINATE return nil,nil end - return Path, Way + return Path, Way, GotPath end --- Gets the surface type at the coordinate. diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index bd2f4653c..563009ed1 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1613,13 +1613,14 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT else - self:T(string.format("Group %s spawning at airbase %s on parking spot id %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), parkingindex[UnitID])) + self:E(string.format("Group %s spawning at airbase %s on parking spot id %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), parkingindex[UnitID])) -- Get coordinates of parking spot. SpawnTemplate.units[UnitID].x = parkingspots[UnitID].x SpawnTemplate.units[UnitID].y = parkingspots[UnitID].z SpawnTemplate.units[UnitID].alt = parkingspots[UnitID].y + parkingspots[UnitID]:MarkToAll(string.format("Group %s spawning at airbase %s on parking spot id %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), parkingindex[UnitID])) end else diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index ac3aa3cff..70375574b 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -41,7 +41,7 @@ -- @field #number assetid Unique id of asset items in stock. Essentially a running number starting at one and incremented when a new asset is added. -- @field #table stock Table holding all assets in stock. Table entries are of type @{#WAREHOUSE.Stockitem}. -- @field #table queue Table holding all queued requests. Table entries are of type @{#WAREHOUSE.Queueitem}. --- @field #table pending Table holding all pending requests, i.e. those that are currently in progress. Table entries are of type @{#WAREHOUSE.Queueitem}. +-- @field #table pending Table holding all pending requests, i.e. those that are currently in progress. Table entries are of type @{#WAREHOUSE.Pendingitem}. -- @extends Core.Fsm#FSM --- Manages ground assets of an airbase and offers the possibility to transport them to another airbase or warehouse. @@ -196,7 +196,7 @@ WAREHOUSE.TransportType = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.2.0" +WAREHOUSE.version="0.2.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -431,7 +431,7 @@ end -- @param #WAREHOUSE self -- @param #number timeinterval Time interval in seconds. -- @return #WAREHOUSE self -function WAREHOUSE:SetSpawnZone(timeinterval) +function WAREHOUSE:SetStatusUpdate(timeinterval) self.dTstatus=timeinterval return self end @@ -516,6 +516,25 @@ function WAREHOUSE:IsAttacked() return self:is("Attacked") end +--- Check if the warehouse has a road connection to another warehouse. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE warehouse The remote warehose to where the connection is checked. +-- @param #boolean markpath If true, place markers of path segments on the F10 map. +-- @param #boolean smokepath If true, put green smoke on path segments. +-- @return #boolean If true, the two warehouses are connected by road. +-- @return #number Path length in meters. +function WAREHOUSE:HasConnectionRoad(warehouse, markpath, smokepath) + if warehouse then + if self.road and warehouse.road then + local _,length,gotpath=self.road:GetPathOnRoad(warehouse.road, false, false, markpath, smokepath) + return gotpath, length or 0 + else + -- At least one of the warehouses has no road connection. + return false, 0 + end + end + return nil +end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- FSM states @@ -714,6 +733,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, templategroupname, ngroups) local DCStype=DCSunit:getTypeName() local SpeedMax=group:GetSpeedMax() local RangeMin=group:GetRange() + group:GetCategory() env.info(string.format("New asset for warehouse %s:", self.alias)) env.info(string.format("Group name = %s", group:GetName())) @@ -764,6 +784,221 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, templategroupname, ngroups) end +--- On after "AddAsset" event. Add a group to the warehouse stock. If the group is alive, it is destroyed. +-- @param #WAREHOUSE self +-- @param #string templategroupname Name of the late activated template group as defined in the mission editor. +-- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1. +function WAREHOUSE:_AddAssetFromZombie(group, ngroups) + +end + + +--- Spawn a ground asset in the spawnzone of the warehouse. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE.Stockitem asset Ground asset that will be spawned. +-- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. +-- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned. +function WAREHOUSE:_SpawnGroundAsset(asset, request) + + if asset and asset.category==Group.Category.GROUND then + + -- Prepare spawn template. + local template=self:_SpawnAssetPrepareTemplate(asset, request) + + -- Initial spawn point. + template.route.points[1] = {} + + -- Get a random coordinate in the spawn zone. + local coord=self.spawnzone:GetRandomCoordinate() + + -- Translate the position of the units. + for i=1,#template.units do + + -- Unit template. + local unit = template.units[i] + + -- Translate position. + local SX = unit.x or 0 + local SY = unit.y or 0 + local BX = asset.template.route.points[1].x + local BY = asset.template.route.points[1].y + local TX = coord.x + (SX-BX) + local TY = coord.z + (SY-BY) + + template.units[i].x = TX + template.units[i].y = TY + + end + + template.route.points[1].x = coord.x + template.route.points[1].y = coord.z + + template.x = coord.x + template.y = coord.z + template.alt = coord.y + + -- Spawn group. + local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP + + -- Activate group. + group:Activate() + + return group + end + + return nil +end + +--- Spawn an aircraft asset (plane or helo) at the airbase associated with the warehouse. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE.Stockitem asset Ground asset that will be spawned. +-- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. +-- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned. +function WAREHOUSE:_SpawnAssetAircraft(asset, request) + + if asset and asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then + + -- Prepare spawn template. + local template=self:_SpawnAssetPrepareTemplate(asset, request) + + -- Set and empty route. + if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then + --template.route.points=self:_GetFlightplan(group,_departure,_destination) + template.route.points[1] = {} + else + template.route.points[1] = {} + end + + -- Link. + local spawnpoint=template.route.points[1] + + -- Set initial waypoint type/action ==> cold start. + spawnpoint.type = COORDINATE.WaypointType.TakeOffParking + spawnpoint.action = COORDINATE.WaypointAction.FromParkingArea + + -- Get airbase ID and category. + local AirbaseID = self.airbase:GetID() + local AirbaseCategory = self.category + + -- Set airbase ID. + if AirbaseCategory == Airbase.Category.HELIPAD or AirbaseCategory == Airbase.Category.SHIP then + spawnpoint.helipadId = AirbaseID + spawnpoint.linkUnit = AirbaseID + spawnpoint.airdromeId = nil + elseif AirbaseCategory == Airbase.Category.AIRDROME then + spawnpoint.airdromeId = AirbaseID + spawnpoint.linkUnit = nil + spawnpoint.helipadId = nil + end + + -- Get the right terminal type for this kind of aircraft. + local terminaltype=self:_GetTerminal(asset.attribute) + + -- Get parking data. + local parking=self.airbase:GetFreeParkingSpotsTable(terminaltype) + + if #parking<#template.units then + self:E("ERROR: Not enough parking!") + return nil + end + + -- Position the units. + for i=1,#template.units do + + -- Unit template. + local unit = template.units[i] + + -- Nillify the unit ID. + unit.unitId=nil + + -- Set unit name. + unit.name=string.format("%s-%02d", template.name , i) + + local coord=parking[i].Coordinate --Core.Point#COORDINATE + local terminal=parking[i].TerminalID --#number + + coord:MarkToAll(string.format("spawnplace terminal %d", terminal)) + + unit.x=coord.x + unit.y=coord.z + unit.alt=coord.y + + unit.parking_id = nil + unit.parking = terminal + end + + -- Set general spawnpoint position. + local abc=self.airbase:GetCoordinate() + spawnpoint.x = template.units[1].x + spawnpoint.y = template.units[1].y + spawnpoint.alt = template.units[1].alt + + -- And template position. + template.x = template.units[1].x + template.y = template.units[1].y + + -- Uncontrolled spawning. + template.uncontrolled=true + self:E({airtemplate=template}) + + + -- Spawn group. + local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP + + -- Activate group. + group:Activate() + + return group + end + + return nil +end + + +--- Prepare a spawn template for the asset. Deep copy of asset template, adjusting template and unit names, nillifying group and unit ids. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE.Stockitem asset Ground asset that will be spawned. +-- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. +-- @return #template Prepared new spawn template. +function WAREHOUSE:_SpawnAssetPrepareTemplate(asset, request) + + -- Create an own copy of the template! + local template=UTILS.DeepCopy(asset.template) + + -- Set unique name. + template.name=self:_Alias(asset, request) + + -- Set current(!) coalition and country. + template.CoalitionID=self.coalition + template.CountryID=self.country + + -- Nillify the group ID. + template.groupId=nil + + -- No late activation. + template.lateActivation=false + + -- Set and empty route. + template.route = {} + template.route.points = {} + + -- Handle units. + for i=1,#template.units do + + -- Unit template. + local unit = template.units[i] + + -- Nillify the unit ID. + unit.unitId=nil + + -- Set unit name. + unit.name=string.format("%s-%02d", template.name , i) + + end + + return template +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- On after "AddRequest" event. Add a request to the warehouse queue, which is processed when possible. @@ -877,61 +1112,50 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Cargo assets. ------------------------------------------------------------------------------------------------------------------------------------ - -- Pending request. - local Pending=Request --#WAREHOUSE.Pendingitem - - -- Spawn assets. + -- Spawn assets of this request. local _spawngroups,_cargoassets=self:_SpawnAssetRequest(Request) --Core.Set#SET_GROUP + -- Check if any group was spawned. If not, delete the request. + if _spawngroups:Count()==0 then + -- Delete request from queue. + self:_DeleteQueueItem(Request, self.queue) + self:E(self.wid..string.format("ERROR: Groups or request %d could not be spawned. Request is rejected and deleted from queue!", Request.uid)) + return + end + -- General type and category. local _cargotype=_cargoassets[1].attribute --#WAREHOUSE.Attribute local _cargocategory=_cargoassets[1].category --DCS#Group.Category - - -- Add cargo groups to request. + + -- Pending request. Add cargo groups to request. + local Pending=Request --#WAREHOUSE.Pendingitem Pending.cargogroupset=_spawngroups Pending.cargoassets=_cargoassets Pending.cargoattribute=_cargotype Pending.cargocategory=_cargocategory - - -- Add groups to cargo if they don't go by themselfs. - local CargoGroups --Core.Set#SET_CARGO - if Request.transporttype ~= WAREHOUSE.TransportType.SELFPROPELLED then - - --TODO: make nearradius depended on transport type and asset type. - local _loadradius=5000 - local _nearradius=35 - - if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then - _loadradius=5000 - elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then - _loadradius=500 - elseif Request.transporttype==WAREHOUSE.TransportType.APC then - _loadradius=100 - end - - -- Empty cargo group set. - CargoGroups = SET_CARGO:New() - - -- Add cargo groups to set. - for _i,_group in pairs(_spawngroups:GetSetObjects()) do - local group=_group --Wrapper.Group#GROUP - local _wid,_aid,_rid=self:_GetIDsFromGroup(group) - local _alias=self:_alias(group:GetTypeName(),_wid,_aid,_rid) - local cargogroup = CARGO_GROUP:New(_group, _cargotype,_alias,_loadradius,_nearradius) - CargoGroups:AddCargo(cargogroup) - end - - end + ------------------------------------------------------------------------------------------------------------------------------------ + -- Self request: assets are spawned at warehouse but not transported anywhere. + ------------------------------------------------------------------------------------------------------------------------------------ + -- Self request! Assets are only spawned but not routed or transported anywhere. if self.warehouse:GetName()==Request.warehouse.warehouse:GetName() then self:E(self.wid..string.format("Selfrequest! Current status %s", self:GetState())) + + -- Add request to pending queue. + table.insert(self.pending, Pending) + + -- Delete request from queue. + self:_DeleteQueueItem(Request, self.queue) + + -- Start self request. self:__SelfRequest(1,_spawngroups, Pending) + return - end - + end + ------------------------------------------------------------------------------------------------------------------------------------ - -- Self propelled assets. + -- Self propelled: assets go to the requesting warehouse by themselfs. ------------------------------------------------------------------------------------------------------------------------------------ -- No transport unit requested. Assets go by themselfes. @@ -977,9 +1201,40 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- No cargo transport necessary. return end + + ------------------------------------------------------------------------------------------------------------------------------------ + -- Prepare cargo groups for transport + ------------------------------------------------------------------------------------------------------------------------------------ + + -- Add groups to cargo if they don't go by themselfs. + local CargoGroups --Core.Set#SET_CARGO + + --TODO: make nearradius depended on transport type and asset type. + local _loadradius=5000 + local _nearradius=35 + + if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then + _loadradius=5000 + elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then + _loadradius=500 + elseif Request.transporttype==WAREHOUSE.TransportType.APC then + _loadradius=100 + end + + -- Empty cargo group set. + CargoGroups = SET_CARGO:New() + + -- Add cargo groups to set. + for _i,_group in pairs(_spawngroups:GetSetObjects()) do + local group=_group --Wrapper.Group#GROUP + local _wid,_aid,_rid=self:_GetIDsFromGroup(group) + local _alias=self:_alias(group:GetTypeName(),_wid,_aid,_rid) + local cargogroup = CARGO_GROUP:New(_group, _cargotype,_alias,_loadradius,_nearradius) + CargoGroups:AddCargo(cargogroup) + end ------------------------------------------------------------------------------------------------------------------------------------ - -- Transport assets and dispachers + -- Transport assets and dispatchers ------------------------------------------------------------------------------------------------------------------------------------ -- Set of cargo carriers. @@ -1204,7 +1459,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) end ---- Spawns requested asset at warehouse or associated airbase. +--- Spawns requested assets at warehouse or associated airbase. -- @param #WAREHOUSE self -- @param #WAREHOUSE.Queueitem Request Information table of the request. -- @return Core.Set#SET_GROUP Set of groups that were spawned. @@ -1258,7 +1513,7 @@ function WAREHOUSE:_SpawnAssetRequest(Request) -- Spawn object. Spawn with ALIAS here or DCS crashes! --local _spawn=SPAWN:NewFromTemplate(_assetitem.template,_assetitem.templatename,_alias):InitCoalition(self.coalition):InitCountry(self.country) - local _spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias):InitCoalition(self.coalition):InitCountry(self.country):InitUnControlled(UnControlled):InitAIOnOff(AIOnOff) + --local _spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias):InitCoalition(self.coalition):InitCountry(self.country):InitUnControlled(UnControlled):InitAIOnOff(AIOnOff) local _group=nil --Wrapper.Group#GROUP local _attribute=_assetitem.attribute @@ -1266,14 +1521,16 @@ function WAREHOUSE:_SpawnAssetRequest(Request) if _assetitem.category==Group.Category.GROUND then -- Spawn ground troops. - _group=_spawn:SpawnFromCoordinate(spawncoord) + --_group=_spawn:SpawnFromCoordinate(spawncoord) + _group=self:_SpawnGroundAsset(_assetitem, Request) elseif _assetitem.category==Group.Category.AIRPLANE or _assetitem.category==Group.Category.HELICOPTER then --TODO: spawn only so many groups as there are parking spots. Adjust request and create a new one with the reduced number! -- Spawn air units. - _group=_spawn:SpawnAtAirbase(self.airbase, SPAWN.Takeoff.Cold, nil, nil, true, Parking[i]) + --_group=_spawn:SpawnAtAirbase(self.airbase, SPAWN.Takeoff.Cold, nil, nil, true, Parking[i]) + _group=self:_SpawnAssetAircraft(_assetitem, Request) elseif _assetitem.category==Group.Category.TRAIN then @@ -1286,7 +1543,6 @@ function WAREHOUSE:_SpawnAssetRequest(Request) end if _group then - --_spawngroups[i]=_group _groupset:AddGroup(_group) table.insert(_assets, _assetitem) else @@ -1365,9 +1621,14 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) -- Info self:E(self.wid..string.format("Cargo %d of %s arrived at warehouse %s. Assets still to deliver %d.",request.ndelivered, tostring(request.nasset), request.warehouse.alias, ncargo)) + -- Route mobile ground group to the warehouse. Group has 60 seconds to get there or it is despawned and added as asset to the new warehouse regardless. + if group:IsGround() and group:GetSpeedMax()>1 then + group:RouteGroundTo(request.warehouse.coordinate, group:GetSpeedMax()*0.3, "Off Road") + end + -- Move asset into new warehouse. -- TODO: need to figure out which template group name I best take. - request.warehouse:__AddAsset(3, group:GetName(), 1) + request.warehouse:__AddAsset(60, group:GetName(), 1) -- All cargo delivered. if request and ncargo==0 then @@ -1447,10 +1708,13 @@ function WAREHOUSE:onafterSelfRequest(From, Event, To, groupset, request) self:E(self.wid..string.format("Assets spawned at warehouse %s after self request!", self.alias)) + env.info("FF spawned groupas at self request") -- Put assets in new warehouse. for _,_group in pairs(groupset:GetSetObjects()) do local group=_group --Wrapper.Group#GROUP - group:SmokeGreen() + local text=string.format("Group name = %s, IsAlive=%s.", tostring(group:GetName()), tostring(group:IsAlive())) + env.info(text) + --group:SmokeGreen() end -- Remove pending request: @@ -1870,8 +2134,8 @@ end --- Checks if the warehouse zone was conquered by antoher coalition. -- @param #WAREHOUSE self function WAREHOUSE:_CheckConquered() - env.info("FF checking conq") + -- Get coordinate and radius to check. local coord=self.zone:GetCoordinate() local radius=self.zone:GetRadius() @@ -1892,26 +2156,30 @@ function WAREHOUSE:_CheckConquered() for _,_unit in pairs(units) do local unit=_unit --Wrapper.Unit#UNIT - -- Get coalition and country. - local _coalition=unit:GetCoalition() - local _country=unit:GetCountry() - - if _coalition==coalition.side.BLUE then - Nblue=Nblue+1 - CountryBlue=_country - elseif _coalition==coalition.side.RED then - Nred=Nred+1 - CountryRed=_country - else - Nneutral=Nneutral+1 - CountryNeutral=_country - end + -- Filter only groud units. + if unit:IsGround() then + -- Get coalition and country. + local _coalition=unit:GetCoalition() + local _country=unit:GetCountry() + + if _coalition==coalition.side.BLUE then + Nblue=Nblue+1 + CountryBlue=_country + elseif _coalition==coalition.side.RED then + Nred=Nred+1 + CountryRed=_country + else + Nneutral=Nneutral+1 + CountryNeutral=_country + end + + end end end -- Debug info. - self:E(self.wid..string.format("Troops at warehouse: blue=%d, red=%d, neutral=%d", Nblue, Nred, Nneutral)) + self:E(self.wid..string.format("Ground troops in warehouse zone: blue=%d, red=%d, neutral=%d", Nblue, Nred, Nneutral)) -- Figure out the new coalition if any. @@ -1929,12 +2197,13 @@ function WAREHOUSE:_CheckConquered() elseif Nblue==0 and Nred==0 and Nneutral>0 then -- Only neutral units in zone but neutrals do not attack or even capture! --newcoalition=coalition.side.NEUTRAL - newcountry=CountryNeutral + --newcountry=CountryNeutral end - -- Coalition has changed ==> warehouse was captured! + -- Coalition has changed ==> warehouse was captured! This should be before the attack check. if self:IsAttacked() and newcoalition ~= self.coalition then - self:Captured(newcoalition, newcountry) + self:Captured(newcoalition, newcountry) + return end -- Before a warehouse can be captured, it has to be attacked. @@ -2202,7 +2471,7 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) end --- Checks if the request can be fullfilled right now. --- Check for current parking situation, number of assets and transports currently in stock +-- Check for current parking situation, number of assets and transports currently in stock. -- @param #WAREHOUSE self -- @param #WAREHOUSE.Queueitem request The request to be checked. -- @return #boolean If true, request can be executed. If false, something is not right. @@ -2213,70 +2482,70 @@ function WAREHOUSE:_CheckRequestNow(request) -- Check if number of requested assets is in stock. local _assets,_nassets,_enough=self:_FilterStock(self.stock, request.assetdesc, request.assetdescval, request.nasset) - -- Nothing in stock. - if _nassets==0 then - local text=string.format("Request denied! No assets for this request currently available.") - MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:E(self.wid..text) - return false - end - - -- Get the attibute of the requested asset. - local _assetattribute=_assets[1].attribute - local _assetcategory=_assets[1].category - -- Check if enough assets are in stock. if not _enough then - local text=string.format("Request denied! Not enough assets currently available.") + local text=string.format("Request denied! Not enough (cargo) assets currently available.") MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:E(self.wid..text) okay=false end - -- Check available parking for asset units. - local Parkingdata - local Parking - if self.airbase and (_assetcategory==Group.Category.AIRPLANE or _assetcategory==Group.Category.HELICOPTER) then - Parkingdata=self.airbase:GetParkingSpotsTable() - Parking, Parkingdata=self:_GetParkingForAssets(_assets, Parkingdata) - if Parking==nil then - local text=string.format("Request denied! Not enough free parking spots for all assets at the moment.") - MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:E(self.wid..text) - okay=false - end - end - - - -- Check that a transport units. - if request.transporttype~=WAREHOUSE.TransportType.SELFPROPELLED then - - -- Transports in stock. - local _transports=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, request.transporttype, request.ntransport) - - -- Get the attibute of the transport units. - local _transportattribute=_transports[1].attribute - local _transportcategory=_transports[1].category - - -- Check if enough transport units are available. - if request.ntransport > #_transports then - local text=string.format("Request denied! Not enough transport units currently available.") - MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:E(self.wid..text) - okay=false - end + -- Check if at least one transport asset is available. + if _nassets>0 then - -- Check available parking for transport units. - if self.airbase and (_transportcategory==Group.Category.AIRPLANE or _transportcategory==Group.Category.HELICOPTER) then - Parking, Parkingdata=self:_GetParkingForAssets(_transports, Parkingdata) + -- Get the attibute of the requested asset. + local _assetattribute=_assets[1].attribute + local _assetcategory=_assets[1].category + + -- Check available parking for asset units. + local Parkingdata + local Parking + if self.airbase and (_assetcategory==Group.Category.AIRPLANE or _assetcategory==Group.Category.HELICOPTER) then + Parkingdata=self.airbase:GetParkingSpotsTable() + Parking, Parkingdata=self:_GetParkingForAssets(_assets, Parkingdata) if Parking==nil then - local text=string.format("Request denied! Not enough free parking spots for all transports at the moment.") + local text=string.format("Request denied! Not enough free parking spots for all assets at the moment.") MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:E(self.wid..text) okay=false end end - + + end + + -- Check that a transport units. + if request.transporttype ~= WAREHOUSE.TransportType.SELFPROPELLED then + + -- Transports in stock. + local _transports,_ntransports,_enough=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, request.transporttype, request.ntransport) + + -- Check if enough transport units are available. + if not _enough then + local text=string.format("Request denied! Not enough transport units currently available.") + MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:E(self.wid..text) + okay=false + end + + -- Check if at least one transport asset is available. + if _ntransports>0 then + + -- Get the attibute of the transport units. + local _transportattribute=_transports[1].attribute + local _transportcategory=_transports[1].category + + -- Check available parking for transport units. + if self.airbase and (_transportcategory==Group.Category.AIRPLANE or _transportcategory==Group.Category.HELICOPTER) then + Parking, Parkingdata=self:_GetParkingForAssets(_transports, Parkingdata) + if Parking==nil then + local text=string.format("Request denied! Not enough free parking spots for all transports at the moment.") + MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:E(self.wid..text) + okay=false + end + end + + end else -- self propelled case. @@ -2397,7 +2666,9 @@ function WAREHOUSE:_GetParkingForAssets(assetlist, parkingdata) for _,spot in pairs(spots) do if spot then local coord=spot.Coordinate --Core.Point#COORDINATE - coord:MarkToAll("Parking spot for "..asset.templatename.." id "..asset.uid.." terminal id "..spot.TerminalID) + local text=string.format("Parking spot for %s:\nAsset id=%d, Terminal id=%d", asset.templatename, asset.uid, spot.TerminalID) + coord:MarkToAll(text) + self:E(self.wid..text) end end @@ -2532,7 +2803,7 @@ end -- @param #number nmax (Optional) Maximum number of items that will be returned. Default nmax=nil is all matching items are returned. -- @return #table Filtered stock items table. -- @return #number Total number of (requested) assets available. --- @return #boolean Enough assets are available. +-- @return #boolean If true, enough assets are available. function WAREHOUSE:_FilterStock(stock, item, value, nmax) -- Default all. @@ -2557,9 +2828,9 @@ function WAREHOUSE:_FilterStock(stock, item, value, nmax) nmax=ntot/2 elseif nmax:lower()=="third" then nmax=ntot/3 - elseif namx:lower()=="quarter" then + elseif nmax:lower()=="quarter" then nmax=ntot/4 - elseif namx:lower()=="fivth" then + elseif nmax:lower()=="fivth" then nmax=ntot/5 else nmax=math.min(1,ntot) @@ -2577,7 +2848,7 @@ function WAREHOUSE:_FilterStock(stock, item, value, nmax) end end - return filtered, ntot, nmax>=ntot + return filtered, ntot, ntot>=nmax end --- Check if a group has a generalized attribute. @@ -2718,9 +2989,12 @@ end -- @param #WAREHOUSE.Queueitem qitem Item of queue to be removed. -- @param #table queue The queue from which the item should be deleted. function WAREHOUSE:_DeleteQueueItem(qitem, queue) + self:F({qitem=qitem, queue=queue}) + for i=1,#queue do local _item=queue[i] --#WAREHOUSE.Queueitem if _item.uid==qitem.uid then + self:E(self.wid..string.format("Deleting queue item %d.", qitem.uid)) table.remove(queue,i) break end diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 7d45a38b8..7c9da217c 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -698,11 +698,12 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius, -- Check all units. for _,unit in pairs(_units) do - - local _vec3=unit:getPoint() - local _coord=COORDINATE:NewFromVec3(_vec3) + -- Unis are now returned as MOOSE units not DCS units! + --local _vec3=unit:getPoint() + --local _coord=COORDINATE:NewFromVec3(_vec3) + local _coord=unit:GetCoordinate() local _dist=_coord:Get2DDistance(_spot) - local _safe=_overlap(aircraft, true, unit, false,_dist) + local _safe=_overlap(aircraft, true, unit, true,_dist) if markobstacles then local l,x,y,z=_GetObjectSize(unit) From 7a2dee41624ea9d4e7690455c2bac9624d2b639f Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sun, 19 Aug 2018 07:26:50 +0200 Subject: [PATCH 267/420] OK. Revised cargo for AI is working. --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 74 ++++++++++++++----- .../Moose/AI/AI_Cargo_Dispatcher.lua | 8 -- .../Moose/AI/AI_Cargo_Helicopter.lua | 58 +++++++++++++-- 3 files changed, 107 insertions(+), 33 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index fbe2dd1f4..acf565f81 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -198,14 +198,41 @@ function AI_CARGO_APC:New( APC, CargoSet, CombatRadius ) for _, APCUnit in pairs( APC:GetUnits() ) do local Desc = APCUnit:GetDesc() - self:F({Desc=Desc}) --- local Box = CargoUnit:GetBoundingBox() + local VolumeUnit = ( Desc.box.max.x - Desc.box.min.x ) * ( Desc.box.max.y - Desc.box.min.y ) * ( Desc.box.max.z - Desc.box.min.z ) - self:F({VolumeUnit=VolumeUnit}) - local CargoBayWeightLimit = 1250 + + local Weights = { + ["M1126 Stryker ICV"] = 9, + ["M-113"] = 9, + ["AAV7"] = 25, + ["M2A1_halftrack"] = 9, + ["BMD-1"] = 9, + ["BMP-1"] = 8, + ["BMP-2"] = 7, + ["BMP-3"] = 8, + ["Boman"] = 25, + ["BTR-80"] = 9, + ["BTR_D"] = 12, + ["Cobra"] = 8, + ["LAV-25"] = 6, + ["M-2 Bradley"] = 6, + ["M1043 HMMWV Armament"] = 4, + ["M1045 HMMWV TOW"] = 4, + ["M1126 Stryker ICV"] = 9, + ["M1134 Stryker ATGM"] = 9, + ["Marder"] = 6, + ["MCV-80"] = 9, + ["MLRS FDDM"] = 4, + ["MTLB"] = 25, + ["TPZ"] = 10, + } + + local CargoBayWeightLimit = ( Weights[Desc.typeName] or 0 ) * 70 + APCUnit:SetCargoBayWeightLimit( CargoBayWeightLimit ) - self:F({CargoBayWeightLimit=CargoBayWeightLimit}) --Airplane:SetCargoBayVolumeLimit( 15 ) + + self:F( {TypeName = Desc.typeName, Desc = Desc, WeightLimit = CargoBayWeightLimit } ) end self.Transporting = false @@ -434,16 +461,25 @@ function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To ) if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then if Cargo:IsInLoadRadius( APCUnit:GetCoordinate() ) then self:F( { "In radius", APCUnit:GetName() } ) - APC:RouteStop() - --Cargo:Ungroup() - Cargo:Board( APCUnit, 25 ) - self:__Board( 1, Cargo ) + + local CargoBayFreeWeight = APCUnit:GetCargoBayFreeWeight() + local CargoWeight = Cargo:GetWeight() + + self:F({CargoBayFreeWeight=CargoBayFreeWeight}) - -- So now this APCUnit has Cargo that is being loaded. - -- This will be used further in the logic to follow and to check cargo status. - self.APC_Cargo[APCUnit] = Cargo - Boarding = true - break + -- Only when there is space within the bay to load the next cargo item! + if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then + APC:RouteStop() + --Cargo:Ungroup() + Cargo:Board( APCUnit, 25 ) + self:__Board( 1, Cargo ) + + -- So now this APCUnit has Cargo that is being loaded. + -- This will be used further in the logic to follow and to check cargo status. + self.APC_Cargo[APCUnit] = Cargo + Boarding = true + break + end end end end @@ -548,10 +584,12 @@ function AI_CARGO_APC:onafterUnload( APC, From, Event, To, Deployed ) local APCUnit = APCUnit -- Wrapper.Unit#UNIT APC:RouteStop() for _, Cargo in pairs( APCUnit:GetCargo() ) do - Cargo:UnBoard() - Cargo:SetDeployed( true ) - self:__Unboard( 10, Cargo, Deployed ) - end + if Cargo:IsLoaded() then + Cargo:UnBoard() + Cargo:SetDeployed( true ) + self:__Unboard( 10, Cargo, Deployed ) + end + end end end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 9bbfe26a8..602868607 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -416,7 +416,6 @@ function AI_CARGO_DISPATCHER:onafterMonitor() self:F( { Cargo = Cargo:GetName(), UnLoaded = Cargo:IsUnLoaded(), Deployed = Cargo:IsDeployed(), PickupCargo = self.PickupCargo[Carrier] ~= nil } ) if Cargo:IsUnLoaded() == true and Cargo:IsDeployed() == false then local CargoCoordinate = Cargo:GetCoordinate() - self:F({CargoCoordinate = CargoCoordinate }) local CoordinateFree = true for CarrierPickup, Coordinate in pairs( self.PickupCargo ) do if CarrierPickup:IsAlive() == true then @@ -428,7 +427,6 @@ function AI_CARGO_DISPATCHER:onafterMonitor() self.PickupCargo[CarrierPickup] = nil end end - self:F({CoordinateFree = CoordinateFree}) if CoordinateFree == true then self.PickupCargo[Carrier] = CargoCoordinate PickupCargo = Cargo @@ -437,8 +435,6 @@ function AI_CARGO_DISPATCHER:onafterMonitor() end end - self:F( { PickupCargo = PickupCargo} ) - if PickupCargo then self.CarrierHome[Carrier] = nil local PickupCoordinate = PickupCargo:GetCoordinate():GetRandomCoordinateInRadius( self.PickupOuterRadius, self.PickupInnerRadius ) @@ -571,10 +567,6 @@ function AI_CARGO_DISPATCHER:OnAfterLoaded( From, Event, To, Carrier, Cargo ) end end - self:F({Carrier=Carrier}) self.PickupCargo[Carrier] = nil end - - - diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index df6865545..1341129f6 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -20,7 +20,8 @@ -- @field #AI_CARGO_HELICOPTER AI_CARGO_HELICOPTER = { ClassName = "AI_CARGO_HELICOPTER", - Coordinate = nil -- Core.Point#COORDINATE, + Coordinate = nil, -- Core.Point#COORDINATE, + Helicopter_Cargo = {}, } AI_CARGO_QUEUE = {} @@ -148,7 +149,6 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) local Desc = HelicopterUnit:GetDesc() self:F({Desc=Desc}) HelicopterUnit:SetCargoBayWeightLimit( Desc.massMax - ( Desc.massEmpty + Desc.fuelMassMax ) ) - --Airplane:SetCargoBayVolumeLimit( 15 ) end self.Relocating = false @@ -398,11 +398,22 @@ function AI_CARGO_HELICOPTER:onbeforeLoad( Helicopter, From, Event, To) if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then if Cargo:IsInLoadRadius( HelicopterUnit:GetCoordinate() ) then self:F( { "In radius", HelicopterUnit:GetName() } ) - --Cargo:Ungroup() - Cargo:Board( HelicopterUnit, 25 ) - self:__Board( 1, Cargo ) - Boarding = true - break + + local CargoBayFreeWeight = HelicopterUnit:GetCargoBayFreeWeight() + local CargoWeight = Cargo:GetWeight() + + self:F({CargoBayFreeWeight=CargoBayFreeWeight}) + + -- Only when there is space within the bay to load the next cargo item! + if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then + + --Cargo:Ungroup() + Cargo:Board( HelicopterUnit, 25 ) + self:__Board( 1, Cargo ) + self.Helicopter_Cargo[HelicopterUnit] = Cargo + Boarding = true + break + end end end end @@ -438,10 +449,13 @@ function AI_CARGO_HELICOPTER:onafterBoard( Helicopter, From, Event, To, Cargo ) local CargoBayFreeWeight = HelicopterUnit:GetCargoBayFreeWeight() local CargoWeight = Cargo:GetWeight() + self:F({CargoBayFreeWeight=CargoBayFreeWeight}) + -- Only when there is space within the bay to load the next cargo item! if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then Cargo:Board( HelicopterUnit, 25 ) self:__Board( 10, Cargo ) + self.Helicopter_Cargo[HelicopterUnit] = Cargo return end end @@ -454,6 +468,36 @@ function AI_CARGO_HELICOPTER:onafterBoard( Helicopter, From, Event, To, Cargo ) end + +--- On before Loaded event. +-- @param #AI_CARGO_HELICOPTER self +-- @param Wrapper.Group#GROUP Helicopter +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @return #boolean Cargo loaded. +function AI_CARGO_HELICOPTER:onbeforeLoaded( Helicopter, From, Event, To, Cargo ) + self:F( { Helicopter, From, Event, To } ) + + local Loaded = true + + if Helicopter and Helicopter:IsAlive() then + for HelicopterUnit, Cargo in pairs( self.Helicopter_Cargo ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + self:F( { IsLoaded = Cargo:IsLoaded(), IsDestroyed = Cargo:IsDestroyed(), Cargo:GetName(), Helicopter:GetName() } ) + if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then + Loaded = false + end + end + end + + return Loaded + +end + + + + --- On after Loaded event. Check if cargo is loaded. -- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter From d1aa5d5de3768501e170ab23dab16749695c3d3a Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 20 Aug 2018 00:08:19 +0200 Subject: [PATCH 268/420] Warehouse v0.2.2 --- Moose Development/Moose/Core/Database.lua | 4 +- Moose Development/Moose/Core/Point.lua | 1 + .../Moose/Functional/Warehouse.lua | 400 ++++++++++-------- Moose Development/Moose/Wrapper/Group.lua | 26 +- 4 files changed, 229 insertions(+), 202 deletions(-) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 3b0fb0337..85e78482a 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -533,8 +533,8 @@ end -- SpawnCountryID, SpawnCategoryID -- This method is used by the SPAWN class. -- @param #DATABASE self --- @param #table SpawnTemplate --- @return #DATABASE self +-- @param #table SpawnTemplate Template of the group to spawn. +-- @return Wrapper.Group#GROUP Spawned group. function DATABASE:Spawn( SpawnTemplate ) self:F( SpawnTemplate.name ) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index b67ed1bef..c899a48c6 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1028,6 +1028,7 @@ do -- COORDINATE RoutePoint.task.params = {} RoutePoint.task.params.tasks = DCSTasks or {} + self:E({RoutePoint=RoutePoint}) return RoutePoint end diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 70375574b..a602549a3 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -140,12 +140,11 @@ WAREHOUSE = { --- Item of the warehouse pending queue table. -- @type WAREHOUSE.Pendingitem -- @extends #WAREHOUSE.Queueitem --- @field #table cargoassets Table of assets to be delivered. Each element of the table is a @{#WAREHOUSE.Stockitem}. +-- @field #table assets Table of assets - cargo or transport. Each element of the table is a @{#WAREHOUSE.Stockitem}. -- @field Core.Set#SET_GROUP cargogroupset Set of cargo groups do be delivered. -- @field #number ndelivered Number of groups delivered to destination. -- @field #number cargoattribute Attribute of cargo assets of type @{#WAREHOUSE.Attribute}. -- @field #number cargocategory Category of cargo assets of type @{#WAREHOUSE.Category}. --- @field #table transportassets Table of assets to be delivered. Each element of the table is a @{#WAREHOUSE.Stockitem}. -- @field Core.Set#SET_GROUP transportgroupset Set of cargo transport groups. -- @field #number transportattribute Attribute of transport assets of type @{#WAREHOUSE.Attribute}. -- @field #number transportcategory Category of transport assets of type @{#WAREHOUSE.Category}. @@ -196,7 +195,7 @@ WAREHOUSE.TransportType = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.2.1" +WAREHOUSE.version="0.2.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -516,24 +515,24 @@ function WAREHOUSE:IsAttacked() return self:is("Attacked") end ---- Check if the warehouse has a road connection to another warehouse. +--- Check if the warehouse has a road connection to another warehouse. Both warehouses need to be running! -- @param #WAREHOUSE self -- @param #WAREHOUSE warehouse The remote warehose to where the connection is checked. -- @param #boolean markpath If true, place markers of path segments on the F10 map. -- @param #boolean smokepath If true, put green smoke on path segments. -- @return #boolean If true, the two warehouses are connected by road. --- @return #number Path length in meters. +-- @return #number Path length in meters. Negative distance -1 meter indicates no connection. function WAREHOUSE:HasConnectionRoad(warehouse, markpath, smokepath) if warehouse then if self.road and warehouse.road then local _,length,gotpath=self.road:GetPathOnRoad(warehouse.road, false, false, markpath, smokepath) - return gotpath, length or 0 + return gotpath, length or -1 else -- At least one of the warehouses has no road connection. - return false, 0 + return false, -1 end end - return nil + return nil, -1 end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -711,66 +710,36 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param #string templategroupname Name of the late activated template group as defined in the mission editor. +-- @param Wrapper.Group#GROUP group Group or template group to be added to the warehouse stock. -- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1. -function WAREHOUSE:onafterAddAsset(From, Event, To, templategroupname, ngroups) +function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups) -- Set default. local n=ngroups or 1 + + local asset --Wrapper.Group#GROUP + if type(group)=="string" then + env.info("New asset passed as string") + asset=GROUP:FindByName(group) + elseif type(group)=="table" and group.aid~=nil then + env.info("New asset passed as asset") + self:_AddExistingAsset(group) + return + elseif group:IsInstanceOf("GROUP") then + env.info("New asset passed as string") + asset=group + else + self:E("ERROR: Invalid asset added!") + return nil + end - -- Get MOOSE group. - local group=GROUP:FindByName(templategroupname) -- Check if group exists and has a DCS object. -- TODO: Need to check this carefully if this words with CARGO etc. - if group and group:IsAlive()~=nil and group:GetDCSObject() then + if asset and asset:IsAlive()~=nil and asset:GetDCSObject() then - local DCSgroup=group:GetDCSObject() - local DCSunit=DCSgroup:getUnit(1) - local DCSdesc=DCSunit:getDesc() - local DCSdisplay=DCSdesc.displayName - local DCScategory=DCSgroup:getCategory() - local DCStype=DCSunit:getTypeName() - local SpeedMax=group:GetSpeedMax() - local RangeMin=group:GetRange() - group:GetCategory() - - env.info(string.format("New asset for warehouse %s:", self.alias)) - env.info(string.format("Group name = %s", group:GetName())) - env.info(string.format("Display name = %s", DCSdisplay)) - env.info(string.format("Category = %s", DCScategory)) - env.info(string.format("Type = %s", DCStype)) - env.info(string.format("Speed max = %s km/h", tostring(SpeedMax))) - env.info(string.format("Range min = %s m", tostring(RangeMin))) - self:E({fullassetdesc=DCSdesc}) - - -- Get the generalized attribute. - local attribute=self:_GetAttribute(templategroupname) - - -- Add this n times to the table. - for i=1,n do - local stockitem={} --#WAREHOUSE.Stockitem - - -- Increase asset unique id counter. - self.assetid=self.assetid+1 - - -- Set parameters. - stockitem.uid=self.assetid - stockitem.templatename=templategroupname - stockitem.template=UTILS.DeepCopy(_DATABASE.Templates.Groups[templategroupname].Template) - stockitem.category=DCScategory - stockitem.unittype=DCStype - stockitem.attribute=attribute - stockitem.range=RangeMin - stockitem.speedmax=SpeedMax - - -- Modify the template so that the group is spawned with the right coalition. - -- TODO: somehow this is now acknoleged properly. Found a workaround however with SPAWN AIP functions. - stockitem.template.CoalitionID=self.coalition - stockitem.template.CountryID=self.country - - table.insert(self.stock, stockitem) - end + -- Add new asset. + self:_AddNewAsset(asset, ngroups) -- Destroy group if it is alive. if group:IsAlive()==true then @@ -784,6 +753,73 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, templategroupname, ngroups) end +--- Add an existing asset to the warehouse stock. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE.Stockitem asset The asset to be added. +function WAREHOUSE:_AddExistingAsset(asset) + table.insert(self.stock, asset) +end + +--- Add a completely new asset to the warehouse. +-- @param #WAREHOUSE self +-- @param Wrapper.Group#GROUP group The group that will be added to the warehouse stock. +-- @param #number ngroups Number of groups to be added. +function WAREHOUSE:_AddNewAsset(group, ngroups) + + -- Set default. + local n=ngroups or 1 + + local templategroupname=group:GetName() + + local DCSgroup=group:GetDCSObject() + local DCSunit=DCSgroup:getUnit(1) + local DCSdesc=DCSunit:getDesc() + local DCSdisplay=DCSdesc.displayName + local DCScategory=DCSgroup:getCategory() + local DCStype=DCSunit:getTypeName() + local SpeedMax=group:GetSpeedMax() + local RangeMin=group:GetRange() + group:GetCategory() + + env.info(string.format("New asset for warehouse %s:", self.alias)) + env.info(string.format("Group name = %s", group:GetName())) + env.info(string.format("Display name = %s", DCSdisplay)) + env.info(string.format("Category = %s", DCScategory)) + env.info(string.format("Type = %s", DCStype)) + env.info(string.format("Speed max = %s km/h", tostring(SpeedMax))) + env.info(string.format("Range min = %s m", tostring(RangeMin))) + self:E({fullassetdesc=DCSdesc}) + + -- Get the generalized attribute. + local attribute=self:_GetAttribute(templategroupname) + + -- Add this n times to the table. + for i=1,n do + local stockitem={} --#WAREHOUSE.Stockitem + + -- Increase asset unique id counter. + self.assetid=self.assetid+1 + + -- Set parameters. + stockitem.uid=self.assetid + stockitem.templatename=templategroupname + stockitem.template=UTILS.DeepCopy(_DATABASE.Templates.Groups[templategroupname].Template) + stockitem.category=DCScategory + stockitem.unittype=DCStype + stockitem.attribute=attribute + stockitem.range=RangeMin + stockitem.speedmax=SpeedMax + + -- Modify the template so that the group is spawned with the right coalition. + -- TODO: somehow this is now acknoleged properly. Found a workaround however with SPAWN AIP functions. + stockitem.template.CoalitionID=self.coalition + stockitem.template.CountryID=self.country + + table.insert(self.stock, stockitem) + end + +end + --- On after "AddAsset" event. Add a group to the warehouse stock. If the group is alive, it is destroyed. -- @param #WAREHOUSE self -- @param #string templategroupname Name of the late activated template group as defined in the mission editor. @@ -798,7 +834,7 @@ end -- @param #WAREHOUSE.Stockitem asset Ground asset that will be spawned. -- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. -- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned. -function WAREHOUSE:_SpawnGroundAsset(asset, request) +function WAREHOUSE:_SpawnAssetGround(asset, request) if asset and asset.category==Group.Category.GROUND then @@ -853,8 +889,9 @@ end -- @param #WAREHOUSE self -- @param #WAREHOUSE.Stockitem asset Ground asset that will be spawned. -- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. +-- @param #table parking Parking data for this asset. -- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned. -function WAREHOUSE:_SpawnAssetAircraft(asset, request) +function WAREHOUSE:_SpawnAssetAircraft(asset, request, parking) if asset and asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then @@ -892,39 +929,56 @@ function WAREHOUSE:_SpawnAssetAircraft(asset, request) end -- Get the right terminal type for this kind of aircraft. - local terminaltype=self:_GetTerminal(asset.attribute) + --local terminaltype=self:_GetTerminal(asset.attribute) -- Get parking data. - local parking=self.airbase:GetFreeParkingSpotsTable(terminaltype) + --local parking=self.airbase:GetFreeParkingSpotsTable(terminaltype) - if #parking<#template.units then - self:E("ERROR: Not enough parking!") - return nil + if AirbaseCategory == Airbase.Category.HELIPAD or AirbaseCategory == Airbase.Category.SHIP then + --TODO Figure out what's necessary in this case. + + else + + if #parking<#template.units then + local text=string.format("ERROR: Not enough parking! Free parking = %d < %d aircraft to be spawned.", #parking, #template.units) + self:E(text) + return nil + end end - + -- Position the units. for i=1,#template.units do -- Unit template. local unit = template.units[i] - - -- Nillify the unit ID. - unit.unitId=nil - - -- Set unit name. - unit.name=string.format("%s-%02d", template.name , i) - - local coord=parking[i].Coordinate --Core.Point#COORDINATE - local terminal=parking[i].TerminalID --#number - - coord:MarkToAll(string.format("spawnplace terminal %d", terminal)) - - unit.x=coord.x - unit.y=coord.z - unit.alt=coord.y - unit.parking_id = nil - unit.parking = terminal + if AirbaseCategory == Airbase.Category.HELIPAD or AirbaseCategory == Airbase.Category.SHIP then + + -- Helipads we take the position of the airbase location, since the exact location of the spawn point does not make sense. + local coord=self.airbase:GetCoordinate() + + unit.x=coord.x + unit.y=coord.z + unit.alt=coord.y + + unit.parking_id = nil + unit.parking = nil + + else + + local coord=parking[i].Coordinate --Core.Point#COORDINATE + local terminal=parking[i].TerminalID --#number + + coord:MarkToAll(string.format("spawnplace unit %s terminal %d", unit.name, terminal)) + + unit.x=coord.x + unit.y=coord.z + unit.alt=coord.y + + unit.parking_id = nil + unit.parking = terminal + + end end -- Set general spawnpoint position. @@ -939,6 +993,8 @@ function WAREHOUSE:_SpawnAssetAircraft(asset, request) -- Uncontrolled spawning. template.uncontrolled=true + + -- Debug info. self:E({airtemplate=template}) @@ -1111,9 +1167,13 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) ------------------------------------------------------------------------------------------------------------------------------------ -- Cargo assets. ------------------------------------------------------------------------------------------------------------------------------------ + + -- Pending request. Add cargo groups to request. + local Pending=Request --#WAREHOUSE.Pendingitem + Pending.assets={} -- Spawn assets of this request. - local _spawngroups,_cargoassets=self:_SpawnAssetRequest(Request) --Core.Set#SET_GROUP + local _spawngroups,_cargoassets=self:_SpawnAssetRequest(Pending) --Core.Set#SET_GROUP -- Check if any group was spawned. If not, delete the request. if _spawngroups:Count()==0 then @@ -1123,14 +1183,11 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) return end - -- General type and category. + -- General type and category. local _cargotype=_cargoassets[1].attribute --#WAREHOUSE.Attribute local _cargocategory=_cargoassets[1].category --DCS#Group.Category - -- Pending request. Add cargo groups to request. - local Pending=Request --#WAREHOUSE.Pendingitem Pending.cargogroupset=_spawngroups - Pending.cargoassets=_cargoassets Pending.cargoattribute=_cargotype Pending.cargocategory=_cargocategory @@ -1175,9 +1232,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) self:_RouteGround(group, ToCoordinate) elseif _cargocategory==Group.Category.AIRPLANE then env.info("FF route plane "..group:GetName()) - --self:_RouteAir(group, Request.airbase) - -- TEST! - group=self:_RouteAirRat(group, Request.airbase) + self:_RouteAir(group, Request.airbase) elseif _cargocategory==Group.Category.HELICOPTER then env.info("FF route helo "..group:GetName()) self:_RouteAir(group, Request.airbase) @@ -1291,6 +1346,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Add group to transportset. TransportSet:AddGroup(spawngroup) + Pending.assets[_assetitem.uid]=_assetitem table.insert(_transportassets,_assetitem) end end @@ -1329,6 +1385,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Add group to transportset. TransportSet:AddGroup(spawngroup) + Pending.assets[_assetitem.uid]=_assetitem table.insert(_transportassets,_assetitem) else self:E(self.wid.."ERROR: spawngroup helo transport does not exist!") @@ -1370,6 +1427,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Add group to transportset. TransportSet:AddGroup(spawngroup) + Pending.assets[_assetitem.uid]=_assetitem table.insert(_transportassets,_assetitem) end end @@ -1440,13 +1498,9 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Start dispatcher. CargoTransport:__Start(5) - - -- Add transportassets to pending queue item. - Pending.transportassets=_transportassets - + -- Add cargo groups to request. Pending.transportgroupset=TransportSet - Pending.transportassets=_transportassets Pending.transportattribute=_transporttype Pending.transportcategory=_transportcategory @@ -1481,7 +1535,7 @@ function WAREHOUSE:_SpawnAssetRequest(Request) -- Now we try to find all parking spots for all cargo groups in advance. Due to the for loop, the parking spots do not get updated while spawning. local Parking={} if _cargocategory==Group.Category.AIRPLANE or _cargocategory==Group.Category.HELICOPTER then - Parking=self:_GetParkingForAssets(_assetstock) + Parking=self:_GetParkingForAssets(_assetstock) end -- Spawn aircraft in uncontrolled state if request comes from the same warehouse. @@ -1492,11 +1546,10 @@ function WAREHOUSE:_SpawnAssetRequest(Request) AIOnOff=false end - -- Create an empty set. + -- Create an empty group set. local _groupset=SET_GROUP:New():FilterDeads() - -- Spawn the assets. - local _spawngroups={} + -- Table for all spawned assets. local _assets={} -- Loop over cargo requests. @@ -1522,7 +1575,7 @@ function WAREHOUSE:_SpawnAssetRequest(Request) -- Spawn ground troops. --_group=_spawn:SpawnFromCoordinate(spawncoord) - _group=self:_SpawnGroundAsset(_assetitem, Request) + _group=self:_SpawnAssetGround(_assetitem, Request) elseif _assetitem.category==Group.Category.AIRPLANE or _assetitem.category==Group.Category.HELICOPTER then @@ -1530,7 +1583,7 @@ function WAREHOUSE:_SpawnAssetRequest(Request) -- Spawn air units. --_group=_spawn:SpawnAtAirbase(self.airbase, SPAWN.Takeoff.Cold, nil, nil, true, Parking[i]) - _group=self:_SpawnAssetAircraft(_assetitem, Request) + _group=self:_SpawnAssetAircraft(_assetitem, Request, Parking[i]) elseif _assetitem.category==Group.Category.TRAIN then @@ -1542,6 +1595,7 @@ function WAREHOUSE:_SpawnAssetRequest(Request) end + -- Add group to group set and asset list. if _group then _groupset:AddGroup(_group) table.insert(_assets, _assetitem) @@ -1552,8 +1606,10 @@ function WAREHOUSE:_SpawnAssetRequest(Request) end -- Delete spawned items from warehouse stock. - for _,_item in pairs(_assets) do - self:_DeleteStockItem(_item) + for _,_asset in pairs(_assets) do + local asset=_asset --#WAREHOUSE.Stockitem + Request.assets[asset.uid]=asset + self:_DeleteStockItem(asset) end return _groupset,_assets @@ -1580,8 +1636,9 @@ function WAREHOUSE:onafterUnloaded(From, Event, To, group) local speedmax=group:GetSpeedMax() if group:IsGround() then + -- Route group to spawn zone. if speedmax>1 then - group:RouteGroundTo(self.spawnzone:GetRandomCoordinate(50), speedmax*0.5, AI.Task.VehicleFormation.RANK, 3) + group:RouteGroundTo(self.spawnzone:GetRandomCoordinate(), speedmax*0.5, AI.Task.VehicleFormation.RANK, 3) else -- Immobile ground unit ==> directly put it into the warehouse. self:Arrived(group) @@ -1626,9 +1683,8 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) group:RouteGroundTo(request.warehouse.coordinate, group:GetSpeedMax()*0.3, "Off Road") end - -- Move asset into new warehouse. - -- TODO: need to figure out which template group name I best take. - request.warehouse:__AddAsset(60, group:GetName(), 1) + -- Move asset from pending queue into new warehouse. + request.warehouse:__AddAsset(60, self:_GetAssetFromGroupRequest(group,request), 1) -- All cargo delivered. if request and ncargo==0 then @@ -1639,6 +1695,20 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) end +--- Get asset from group and request. +-- @param #WAREHOUSE self +-- @param Wrapper.Group#GROUP group The group that has arrived at its destination. +-- @param #WAREHOUSE.Pendingitem request Pending request. +-- @return #WAREHOUSE.Stockitem The asset. +function WAREHOUSE:_GetAssetFromGroupRequest(group,request) + + -- Get the IDs for this group. In particular, we use the asset ID to figure out which group was delivered. + local wid,aid,rid=self:_GetIDsFromGroup(group) + + -- Retrieve asset from request. + local asset=request.assets[aid] +end + --- Update the pending requests by removing assets that have arrived. -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group The group that has arrived at its destination. @@ -1671,7 +1741,7 @@ function WAREHOUSE:_UpdatePending(group) self:E(self.wid..string.format("WARNING: pending request could not be updated since request did not exist in pending queue!")) end - return request + return request,wid,aid,rid end @@ -1797,60 +1867,6 @@ function WAREHOUSE:_RouteGround(Group, Coordinate, Speed) end end ---- Route the airplane from one airbase another. --- @param #WAREHOUSE self --- @param Wrapper.Group#GROUP Aircraft Airplane group to be routed. --- @param Wrapper.Airbase#AIRBASE ToAirbase Destination airbase. --- @param #number Speed Speed in km/h. Default is 80% of max possible speed the group can do. --- @return Wrapper.Group#GROUP Group that was spawned by RAT. -function WAREHOUSE:_RouteAirRat(Aircraft, ToAirbase, Speed) - - if Aircraft and Aircraft:IsAlive()~=nil then - - -- Get parking data of all units. - local parkingdata={} - - local units=Aircraft:GetUnits() - for _,_unit in pairs(units) do - local unit=_unit --Wrapper.Unit#UNIT - local _spot,_terminal,_distance=unit:GetCoordinate():GetClosestOccupiedParkingSpot(self.airbase) - table.insert(parkingdata, {Coordinate=_spot, TerminalID=_terminal}) - end - env.info("FF parking data") - self:E(parkingdata) - - -- Create a RAT object to use its flight plan. - local rat=RAT:New(Aircraft:GetName()) - - -- Init some parameters. - rat:SetDeparture(self.airbase:GetName()) - rat:SetDestination(ToAirbase:GetName()) - --rat:SetCoalitionAircraft(color) - rat:SetCountry(self.country) - rat:NoRespawn() - - -- Init spawn but do not actually spawn. - rat:Spawn(0) - --rat:_SpawnWithRoute(_departure,_destination,_takeoff,_landing,_livery,_waypoint,_lastpos,_nrespawn,parkingdata) - - -- Destroy the original aircraft. - Aircraft:Destroy() - - -- Spawn RAT aircraft at specific parking sports. - local spawnindex=rat:_SpawnWithRoute(self.airbase:GetName(), ToAirbase:GetName(), RAT.wp.hot, nil, nil, nil, nil, nil, parkingdata) - - -- Get the group and check it's name. - local group=rat.ratcraft[spawnindex].group --Wrapper.Group#GROUP - self:E(self.wid..string.format("Spawned new RAT aircraft as group %s", group:GetName())) - - group:SmokeBlue() - -- Activate group. - local bla=group:SetCommand({id='Start', params={}}) - self:E({bla=bla}) - return group - end - -end --- Route the airplane from one airbase another. -- @param #WAREHOUSE self @@ -1916,9 +1932,6 @@ function WAREHOUSE:_RouteAir(Aircraft, ToAirbase, Speed) local newAC=Aircraft:RespawnAtCurrentAirbase(Template, Takeoff, false) env.info("FF Respawn at current airbase group = "..newAC:GetName().." name after") - -- Handle event engine shutdown and trigger delivered event. - -- Not this did not work unless the routine would retrive the state from get/set state! - --newAC:HandleEvent(EVENTS.EngineShutdown, self._OnEventArrived) else self:E(string.format("ERROR: aircraft %s cannot be routed since it does not exist or is not alive %s!", tostring(Aircraft:GetName()), tostring(Aircraft:IsAlive()))) end @@ -2653,12 +2666,12 @@ function WAREHOUSE:_GetParkingForAssets(assetlist, parkingdata) local nunits=#group:GetUnits() local terminal=self:_GetTerminal(asset.attribute) - --[[ + env.info("asset name = "..tostring(asset.templatename)) env.info("asset attribute = "..tostring(asset.attribute)) env.info("terminal type = "..tostring(terminal)) + env.info("parking spots = "..tostring(nunits)) env.info("parking spots = "..tostring(#parkingdata)) - ]] -- Find appropiate parking spots for this group. local spots=self.airbase:FindFreeParkingSpotForAircraft(group, terminal, nil, nil, nil, nil, nil, nil, parkingdata) @@ -2667,7 +2680,7 @@ function WAREHOUSE:_GetParkingForAssets(assetlist, parkingdata) if spot then local coord=spot.Coordinate --Core.Point#COORDINATE local text=string.format("Parking spot for %s:\nAsset id=%d, Terminal id=%d", asset.templatename, asset.uid, spot.TerminalID) - coord:MarkToAll(text) + --coord:MarkToAll(text) self:E(self.wid..text) end end @@ -2925,14 +2938,14 @@ function WAREHOUSE:_GetAttribute(groupname) attribute=WAREHOUSE.Attribute.TANKER elseif awacs then attribute=WAREHOUSE.Attribute.AWACS + elseif bomber then + attribute=WAREHOUSE.Attribute.BOMBER elseif artillery then attribute=WAREHOUSE.Attribute.ARTILLERY elseif infantry then attribute=WAREHOUSE.Attribute.INFANTRY elseif attackhelicopter then attribute=WAREHOUSE.Attribute.ATTACKHELICOPTER - elseif bomber then - attribute=WAREHOUSE.Attribute.BOMBER elseif tank then attribute=WAREHOUSE.Attribute.TANK elseif truck then @@ -3160,7 +3173,7 @@ function WAREHOUSE:_GetFlightplan(group,_departure,_destination) local AlphaDescent=math.rad(4) -- Expected cruise level (peak of Gaussian distribution) - local FLcruise_expect=200*RAT.unit.FL2m + local FLcruise_expect=150*RAT.unit.FL2m --- DEPARTURE AIRPORT @@ -3180,9 +3193,9 @@ function WAREHOUSE:_GetFlightplan(group,_departure,_destination) --- DESCENT/HOLDING POINT - -- Get a random point between 5 and 20 km away from the destination. - local Rhmin=8000 - local Rhmax=20000 + -- Get a random point between 5 and 10 km away from the destination. + local Rhmin=5000 + local Rhmax=10000 if _category==Group.Category.HELICOPTER then -- For helos we set a distance between 500 to 1000 m. Rhmin=500 @@ -3190,8 +3203,9 @@ function WAREHOUSE:_GetFlightplan(group,_departure,_destination) end -- Coordinates of the holding point. y is the land height at that point. - local Vholding=Pdestination:GetRandomVec2InRadius(Rhmax, Rhmin) - local Pholding=COORDINATE:NewFromVec2(Vholding) + --local Vholding=Pdestination:GetRandomVec2InRadius(Rhmax, Rhmin) + --local Pholding=COORDINATE:NewFromVec2(Vholding) + local Pholding=Pdestination:GetRandomCoordinateInRadius(Rhmax, Rhmin) -- AGL height of holding point. local H_holding=Pholding.y @@ -3293,8 +3307,26 @@ function WAREHOUSE:_GetFlightplan(group,_departure,_destination) -- Distances. local d_climb = h_climb/math.tan(AlphaClimb) local d_descent = h_descent/math.tan(AlphaDescent) - local d_cruise = d_total-d_climb-d_descent + local d_cruise = d_total-d_climb-d_descent + -- Debug. + local text=string.format("Flight plan:\n") + text=text..string.format("Vx max = %d\n", Vmax) + text=text..string.format("Vx climb = %d\n", VxClimb) + text=text..string.format("Vx cruise = %d\n", VxCruise) + text=text..string.format("Vx descent = %d\n", VxDescent) + text=text..string.format("Vx holding = %d\n", VxHolding) + text=text..string.format("Vx final = %d\n", VxFinal) + text=text..string.format("Dist climb = %d\n", d_climb) + text=text..string.format("Dist cruise = %d\n", d_cruise) + text=text..string.format("Dist descent = %d\n", d_descent) + text=text..string.format("Dist total = %d\n", d_total) + text=text..string.format("FL min = %d\n", FLmin) + text=text..string.format("FL cruise * = %d\n", FLcruise) + text=text..string.format("FL max = %d\n", FLmax) + text=text..string.format("Ceiling = %d\n", ceiling) + env.info(text) + -- Ensure that cruise distance is positve. Can be slightly negative in special cases. And we don't want to turn back. if d_cruise<0 then d_cruise=100 @@ -3307,52 +3339,44 @@ function WAREHOUSE:_GetFlightplan(group,_departure,_destination) --- Departure/Take-off c[#c+1]=Pdeparture wp[#wp+1]=Pdeparture:WaypointAir("RADIO", COORDINATE.WaypointType.TakeOffParking, COORDINATE.WaypointAction.FromParkingArea, VxClimb, true,_departure, nil, "Departure") - --wp[#wp+1]=self:_Waypoint(#wp+1, "Departure", takeoff, c[#wp+1], VxClimb, H_departure, departure) --- Climb local Pclimb=Pdeparture:Translate(d_climb/2, heading) Pclimb.y=H_departure+(FLcruise-H_departure)/2 c[#c+1]=Pclimb wp[#wp+1]=Pclimb:WaypointAir("BARO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, VxClimb, true, nil, nil, "Climb") - --wp[#wp+1]=self:_Waypoint(#wp+1, "Climb", RAT.wp.climb, c[#wp+1], VxClimb, ) --- Begin of Cruise local Pcruise1=Pclimb:Translate(d_climb/2, heading) Pcruise1.y=FLcruise c[#c+1]=Pcruise1 wp[#wp+1]=Pcruise1:WaypointAir("BARO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, VxCruise, true, nil, nil, "Begin of Cruise") - --wp[#wp+1]=self:_Waypoint(#wp+1, "Begin of Cruise", RAT.wp.cruise, c[#wp+1], VxCruise, FLcruise) --- End of Cruise local Pcruise2=Pcruise1:Translate(d_cruise, heading) Pcruise2.y=FLcruise c[#c+1]=Pcruise2 wp[#wp+1]=Pcruise2:WaypointAir("BARO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, VxCruise, true, nil, nil, "End of Cruise") - --wp[#wp+1]=self:_Waypoint(#wp+1, "End of Cruise", RAT.wp.cruise, c[#wp+1], VxCruise, FLcruise) --- Descent local Pdescent=Pcruise2:Translate(d_descent/2, heading) Pdescent.y=FLcruise-(FLcruise-(h_holding+H_holding))/2 c[#c+1]=Pdescent wp[#wp+1]=Pcruise2:WaypointAir("BARO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, VxDescent, true, nil, nil, "Descent") - --wp[#wp+1]=self:_Waypoint(#wp+1, "Descent", RAT.wp.descent, c[#wp+1], VxDescent, FLcruise-(FLcruise-(h_holding+H_holding))/2) --- Holding point Pholding.y=H_holding+h_holding c[#c+1]=Pholding wp[#wp+1]=Pholding:WaypointAir("BARO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, VxHolding, true, nil, nil, "Holding") - --wp[#wp+1]=self:_Waypoint(#wp+1, "Holding Point", RAT.wp.holding, c[#wp+1], VxHolding, H_holding+h_holding) --- Final destination. c[#c+1]=Pdestination - wp[#wp+1]=Pcruise2:WaypointAir("BARO", COORDINATE.WaypointType.Land, COORDINATE.WaypointAction.Landing, VxFinal, true, nil, nil, "Final Destination") - --wp[#wp+1]=self:_Waypoint(#wp+1, "Final Destination", landing, c[#wp+1], VxFinal, H_destination, destination) + wp[#wp+1]=Pcruise2:WaypointAir("RADIO", COORDINATE.WaypointType.Land, COORDINATE.WaypointAction.Landing, VxFinal, true, _destination, nil, "Final Destination") return wp,c end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 10174dd5b..34d76c792 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1453,7 +1453,7 @@ function GROUP:RespawnAtCurrentAirbase(SpawnTemplate, Takeoff, Uncontrolled) -- SpawnPoint.airdromeId = AirbaseID end - SpawnPoint.alt = AirbaseCoord:GetLandHeight() + SpawnPoint.type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type SpawnPoint.action = GROUPTEMPLATE.Takeoff[Takeoff][2] -- action @@ -1474,7 +1474,7 @@ function GROUP:RespawnAtCurrentAirbase(SpawnTemplate, Takeoff, Uncontrolled) -- -- Get unit coordinates for respawning position. local uc=unit:GetCoordinate() - + uc:MarkToAll(string.format("re-spawnplace %s terminal %d", unit:GetName(), TermialID)) SpawnTemplate.units[UnitID].x = uc.x --Parkingspot.x SpawnTemplate.units[UnitID].y = uc.z --Parkingspot.z @@ -1483,24 +1483,26 @@ function GROUP:RespawnAtCurrentAirbase(SpawnTemplate, Takeoff, Uncontrolled) -- SpawnTemplate.units[UnitID].parking = TermialID SpawnTemplate.units[UnitID].parking_id = nil - if UnitID==1 then - x=uc.x - y=uc.z - end - + --SpawnTemplate.units[UnitID].unitId=nil end - SpawnPoint.x = x --AirbaseCoord.x - SpawnPoint.y = y --AirbaseCoord.z + --SpawnTemplate.groupId=nil - SpawnTemplate.x = x --AirbaseCoord.x - SpawnTemplate.y = y --AirbaseCoord.z + SpawnPoint.x = SpawnTemplate.units[1].x --x --AirbaseCoord.x + SpawnPoint.y = SpawnTemplate.units[1].y --y --AirbaseCoord.z + SpawnPoint.alt = SpawnTemplate.units[1].alt --AirbaseCoord:GetLandHeight() + + SpawnTemplate.x = SpawnTemplate.units[1].x --x --AirbaseCoord.x + SpawnTemplate.y = SpawnTemplate.units[1].y --y --AirbaseCoord.z -- Set uncontrolled state. SpawnTemplate.uncontrolled=Uncontrolled - -- Destroy and respawn. + -- Destroy old group. self:Destroy() + + + --SCHEDULER:New(nil, DATABASE.Spawn, {_DATABASE, SpawnTemplate}, 0.00001) _DATABASE:Spawn( SpawnTemplate ) -- Reset events. From f9b4eeef67025c8a009508e171f76b5db260f87b Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 20 Aug 2018 16:29:04 +0200 Subject: [PATCH 269/420] Warehouse v0.2.2w --- .../Moose/Functional/Warehouse.lua | 384 ++++++++++++------ 1 file changed, 263 insertions(+), 121 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index a602549a3..e1fb8d8b2 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -39,7 +39,7 @@ -- @field #number markerid ID of the warehouse marker at the airbase. -- @field #number dTstatus Time interval in seconds of updating the warehouse status and processing new events. Default 30 seconds. -- @field #number assetid Unique id of asset items in stock. Essentially a running number starting at one and incremented when a new asset is added. --- @field #table stock Table holding all assets in stock. Table entries are of type @{#WAREHOUSE.Stockitem}. +-- @field #table stock Table holding all assets in stock. Table entries are of type @{#WAREHOUSE.Assetitem}. -- @field #table queue Table holding all queued requests. Table entries are of type @{#WAREHOUSE.Queueitem}. -- @field #table pending Table holding all pending requests, i.e. those that are currently in progress. Table entries are of type @{#WAREHOUSE.Pendingitem}. -- @extends Core.Fsm#FSM @@ -113,7 +113,7 @@ WAREHOUSE = { } --- Item of the warehouse stock table. --- @type WAREHOUSE.Stockitem +-- @type WAREHOUSE.Assetitem -- @field #number uid Unique id of the asset. -- @field #string templatename Name of the template group. -- @field #table template The spawn template of the group. @@ -121,7 +121,9 @@ WAREHOUSE = { -- @field #string unittype Type of the first unit of the group as obtained by the Object.getTypeName() DCS API function. -- @field #number nunits Number of units in the group. -- @field #number range Range of the unit in meters. +-- @field #number size Maximum size in length and with of the asset in meters. -- @field #number speedmax Maximum speed in km/h the unit can do. +-- @field DCS#Object.Desc DCSdesc All DCS descriptors. -- @field #WAREHOUSE.Attribute attribute Generalized attribute of the group. --- Item of the warehouse queue table. @@ -140,7 +142,7 @@ WAREHOUSE = { --- Item of the warehouse pending queue table. -- @type WAREHOUSE.Pendingitem -- @extends #WAREHOUSE.Queueitem --- @field #table assets Table of assets - cargo or transport. Each element of the table is a @{#WAREHOUSE.Stockitem}. +-- @field #table assets Table of assets - cargo or transport. Each element of the table is a @{#WAREHOUSE.Assetitem}. -- @field Core.Set#SET_GROUP cargogroupset Set of cargo groups do be delivered. -- @field #number ndelivered Number of groups delivered to destination. -- @field #number cargoattribute Attribute of cargo assets of type @{#WAREHOUSE.Attribute}. @@ -150,19 +152,23 @@ WAREHOUSE = { -- @field #number transportcategory Category of transport assets of type @{#WAREHOUSE.Category}. -- @field #number ntransporthome Number of transports back home. transportattribute ---- Descriptors enumerator describing the type of the asset in stock. +--- Descriptors enumerator describing the type of the asset. -- @type WAREHOUSE.Descriptor +-- @field #string TEMPLATENAME Name of the asset template. +-- @field #string UNITTYPE Typename of the DCS unit. +-- @field #string ATTRIBUTE Generalized attribute @{#WAREHOUSE.Attribute}. +-- @field #string CATEGORY Asset category of type DCS#Group.Category, i.e. GROUND, AIRPLANE, HELICOPTER, SHIP, TRAIN. WAREHOUSE.Descriptor = { - ID="id", + --ID="id", TEMPLATENAME="templatename", - CATEGORY="category", UNITTYPE="unittype", ATTRIBUTE="attribute", + CATEGORY="category", } --- Warehouse generalited categories. -- @type WAREHOUSE.Attribute --- @field #string TRANSPORT_PLANE Airplane with transport capability. Usually bigger. +-- @field #string TRANSPORT_PLANE Airplane with transport capability. Usually bigger, i.e. needs larger airbases and parking spots. -- @field #string TRANSPORT_HELO Helicopter with transport capability. WAREHOUSE.Attribute = { TRANSPORT_PLANE="Transport_Plane", @@ -193,9 +199,18 @@ WAREHOUSE.TransportType = { SELFPROPELLED = "Selfpropelled", } +--- Warehouse database. Note that this is a global array to have easier exchange between warehouses. +-- @type WAREHOUSE.db +-- @field #number AssetID Unique ID of each asset. This is a running number, which is increased each time a new asset is added. +-- @field #table Assets Table holding registered assets, which are of type @{Functional.Warehouse#WAREHOUSE.Assetitem}. +WAREHOUSE.db = { + AssetID = 0, + Assets = {}, +} + --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.2.2" +WAREHOUSE.version="0.2.2w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -220,6 +235,7 @@ WAREHOUSE.version="0.2.2" -- TODO: Add general message function for sending to coaliton or debug. -- TODO: Use RAT for routing air units. Should be possible but might need some modifications of RAT, e.g. explit spawn place. But flight plan should be better. -- TODO: Can I make a request with specific assets? E.g., once delivered, make a request for exactly those assests that were in the original request. +-- TODO: Handle the case when units of a group die during the transfer. Adjust template?! See Grouping in SPAWN. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor(s) @@ -515,7 +531,7 @@ function WAREHOUSE:IsAttacked() return self:is("Attacked") end ---- Check if the warehouse has a road connection to another warehouse. Both warehouses need to be running! +--- Check if the warehouse has a road connection to another warehouse. Both warehouses need to be started! -- @param #WAREHOUSE self -- @param #WAREHOUSE warehouse The remote warehose to where the connection is checked. -- @param #boolean markpath If true, place markers of path segments on the F10 map. @@ -535,6 +551,26 @@ function WAREHOUSE:HasConnectionRoad(warehouse, markpath, smokepath) return nil, -1 end +--- Check if the warehouse has a railroad connection to another warehouse. Both warehouses need to be started! +-- @param #WAREHOUSE self +-- @param #WAREHOUSE warehouse The remote warehose to where the connection is checked. +-- @param #boolean markpath If true, place markers of path segments on the F10 map. +-- @param #boolean smokepath If true, put green smoke on path segments. +-- @return #boolean If true, the two warehouses are connected by road. +-- @return #number Path length in meters. Negative distance -1 meter indicates no connection. +function WAREHOUSE:HasConnectionRail(warehouse, markpath, smokepath) + if warehouse then + if self.rail and warehouse.rail then + local _,length,gotpath=self.road:GetPathOnRoad(warehouse.road, false, true, markpath, smokepath) + return gotpath, length or -1 + else + -- At least one of the warehouses has no rail connection. + return false, -1 + end + end + return nil, -1 +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- FSM states ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -568,8 +604,7 @@ function WAREHOUSE:onafterStart(From, Event, To) -- Debug mark warehouse & spawn zone. self.zone:BoundZone(30, self.country) self.spawnzone:BoundZone(30, self.country) - - --self.spawnzone:GetCoordinate():MarkToAll("Spawnzone of warehouse "..self.alias) + -- Get the closest point on road wrt spawnzone of ground assets. local _road=self.spawnzone:GetCoordinate():GetClosestPointToRoad() @@ -625,7 +660,7 @@ end -- @param #string Event Event. -- @param #string To To state. function WAREHOUSE:onafterStop(From, Event, To) - self:E(self.wid..string.format("Warehouse stopped")) + self:E(self.wid..string.format("Warehouse stopped!")) -- Unhandle event. self:UnHandleEvent(EVENTS.Birth) @@ -637,6 +672,9 @@ function WAREHOUSE:onafterStop(From, Event, To) self:UnHandleEvent(EVENTS.Dead) self:UnHandleEvent(EVENTS.BaseCaptured) + -- Clear all pending schedules. + self.CallScheduler:Clear() + end --- On after "Pause" event. Pauses the warehouse, i.e. no requests are processed. However, new requests and new assets can be added in this state. @@ -645,7 +683,7 @@ end -- @param #string Event Event. -- @param #string To To state. function WAREHOUSE:onafterPause(From, Event, To) - self:E(self.wid..string.format("Warehouse paused! Queued requests are not processed in this state.")) + self:E(self.wid..string.format("Warehouse %s paused! Queued requests are not processed in this state.", self.alias)) end --- On after "Unpause" event. Unpauses the warehouse, i.e. requests in queue are processed again. @@ -665,7 +703,7 @@ end -- @param #string Event Event. -- @param #string To To state. function WAREHOUSE:onafterStatus(From, Event, To) - self:E(self.wid..string.format("Checking warehouse status of %s", self.alias)) + self:E(self.wid..string.format("Checking status of warehouse %s.", self.alias)) -- Print status. self:_DisplayStatus() @@ -717,61 +755,90 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups) -- Set default. local n=ngroups or 1 - local asset --Wrapper.Group#GROUP - if type(group)=="string" then - env.info("New asset passed as string") - asset=GROUP:FindByName(group) - elseif type(group)=="table" and group.aid~=nil then - env.info("New asset passed as asset") - self:_AddExistingAsset(group) - return - elseif group:IsInstanceOf("GROUP") then - env.info("New asset passed as string") - asset=group - else - self:E("ERROR: Invalid asset added!") - return nil - end - - - -- Check if group exists and has a DCS object. - -- TODO: Need to check this carefully if this words with CARGO etc. - if asset and asset:IsAlive()~=nil and asset:GetDCSObject() then - - -- Add new asset. - self:_AddNewAsset(asset, ngroups) + if group then + + -- Get unique ids from group name. + local wid,aid,rid=self:_GetIDsFromGroup(group) + + -- Check if this is an known or a new asset group. + + if aid~=nil and wid~=nil then + -- We got a warehouse and asset id ==> this is an "old" group. + local asset=self:_FindAssetInDB(group) + + -- Note the group is only added once, i.e. the ngroups parameter is ignored here. + -- This is because usually these request comes from an asset that has been transfered from another warehouse and hence should only be added once. + if asset~=nil then + table.insert(self.stock, asset) + end + + else + + -- This is a group that is not in the db yet. Add it n times. + local assets=self:_RegisterAsset(group, n) + + -- Add created assets to stock of this warehouse. + for _,asset in pairs(assets) do + table.insert(self.stock, asset) + end + end -- Destroy group if it is alive. + -- TODO: This causes a problem, when a completely new asset is added, i.e. not from a template group. + -- Need to create a "zombie" template group maybe? if group:IsAlive()==true then group:Destroy() end - - else - -- Group name does not exist! - self:E(string.format("ERROR: Template group name not defined in the mission editor. Check the spelling! templategroupname=%s",tostring(templategroupname))) + end - + end ---- Add an existing asset to the warehouse stock. +--- Find an asset in the the global warehouse db. -- @param #WAREHOUSE self --- @param #WAREHOUSE.Stockitem asset The asset to be added. -function WAREHOUSE:_AddExistingAsset(asset) - table.insert(self.stock, asset) +-- @param Wrapper.Group#GROUP group The group from which it is assumed that it has a registered asset. +-- @return #WAREHOUSE.Assetitem The asset from the data base or nil if it could not be found. +function WAREHOUSE:_FindAssetInDB(group) + -- Get unique ids from group name. + local wid,aid,rid=self:_GetIDsFromGroup(group) + + if aid~=nil then + local asset=WAREHOUSE.db.Assets[aid] + if asset==nil then + self:E(string.format("ERROR: Asset for group %s not found in the data base!", group:GetName())) + end + return asset + end + + self:E(string.format("ERROR: Group %s does not contain an asset ID in its name!", group:GetName())) + return nil end ---- Add a completely new asset to the warehouse. +--- Register new asset in globase warehouse data base. -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group The group that will be added to the warehouse stock. -- @param #number ngroups Number of groups to be added. -function WAREHOUSE:_AddNewAsset(group, ngroups) +-- @return #table A table containing all registered assets. +function WAREHOUSE:_RegisterAsset(group, ngroups) -- Set default. local n=ngroups or 1 + -- Get the size of an object. + local function _GetObjectSize(DCSdesc) + if DCSdesc.box then + local x=DCSdesc.box.max.x+math.abs(DCSdesc.box.min.x) + local y=DCSdesc.box.max.y+math.abs(DCSdesc.box.min.y) --height + local z=DCSdesc.box.max.z+math.abs(DCSdesc.box.min.z) + return math.max(x,z), x , y, z + end + return 0,0,0,0 + end + local templategroupname=group:GetName() local DCSgroup=group:GetDCSObject() + local DCSunit=DCSgroup:getUnit(1) local DCSdesc=DCSunit:getDesc() local DCSdisplay=DCSdesc.displayName @@ -779,45 +846,61 @@ function WAREHOUSE:_AddNewAsset(group, ngroups) local DCStype=DCSunit:getTypeName() local SpeedMax=group:GetSpeedMax() local RangeMin=group:GetRange() - group:GetCategory() - - env.info(string.format("New asset for warehouse %s:", self.alias)) - env.info(string.format("Group name = %s", group:GetName())) - env.info(string.format("Display name = %s", DCSdisplay)) - env.info(string.format("Category = %s", DCScategory)) - env.info(string.format("Type = %s", DCStype)) - env.info(string.format("Speed max = %s km/h", tostring(SpeedMax))) - env.info(string.format("Range min = %s m", tostring(RangeMin))) - self:E({fullassetdesc=DCSdesc}) + local smax,sx,sy,sz=_GetObjectSize(DCSdesc) -- Get the generalized attribute. local attribute=self:_GetAttribute(templategroupname) + -- Table for returned assets. + local assets={} + -- Add this n times to the table. for i=1,n do - local stockitem={} --#WAREHOUSE.Stockitem + local asset={} --#WAREHOUSE.Assetitem -- Increase asset unique id counter. self.assetid=self.assetid+1 + WAREHOUSE.db.AssetID=WAREHOUSE.db.AssetID+1 -- Set parameters. - stockitem.uid=self.assetid - stockitem.templatename=templategroupname - stockitem.template=UTILS.DeepCopy(_DATABASE.Templates.Groups[templategroupname].Template) - stockitem.category=DCScategory - stockitem.unittype=DCStype - stockitem.attribute=attribute - stockitem.range=RangeMin - stockitem.speedmax=SpeedMax + asset.uid=WAREHOUSE.db.AssetID + asset.templatename=templategroupname + asset.template=UTILS.DeepCopy(_DATABASE.Templates.Groups[templategroupname].Template) + asset.category=DCScategory + asset.unittype=DCStype + asset.nunits=#asset.template.units + asset.attribute=attribute + asset.range=RangeMin + asset.speedmax=SpeedMax + asset.size=smax + asset.DCSdesc=DCSdesc - -- Modify the template so that the group is spawned with the right coalition. - -- TODO: somehow this is now acknoleged properly. Found a workaround however with SPAWN AIP functions. - stockitem.template.CoalitionID=self.coalition - stockitem.template.CountryID=self.country + if i==1 then + env.info(string.format("New asset for warehouse %s:", self.alias)) + env.info(string.format("Group name = %s", group:GetName())) + env.info(string.format("Display name = %s", DCSdisplay)) + env.info(string.format("Category = %s", DCScategory)) + env.info(string.format("Type = %s", DCStype)) + env.info(string.format("Size max = %s", asset.size)) + env.info(string.format("Speed max = %s km/h", SpeedMax)) + env.info(string.format("Range min = %s m", RangeMin)) + self:E({fullassetdesc=DCSdesc}) + end - table.insert(self.stock, stockitem) + -- Add asset to global db. + table.insert(WAREHOUSE.db.Assets, {uid=asset.uid, asset=asset}) + + table.insert(assets,asset) end + return assets +end + +--- Asset item characteristics. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE.Assetitem asset +function WAREHOUSE:_AssetItemInfo(asset) + end --- On after "AddAsset" event. Add a group to the warehouse stock. If the group is alive, it is destroyed. @@ -831,7 +914,7 @@ end --- Spawn a ground asset in the spawnzone of the warehouse. -- @param #WAREHOUSE self --- @param #WAREHOUSE.Stockitem asset Ground asset that will be spawned. +-- @param #WAREHOUSE.Assetitem asset Ground asset that will be spawned. -- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. -- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned. function WAREHOUSE:_SpawnAssetGround(asset, request) @@ -887,7 +970,7 @@ end --- Spawn an aircraft asset (plane or helo) at the airbase associated with the warehouse. -- @param #WAREHOUSE self --- @param #WAREHOUSE.Stockitem asset Ground asset that will be spawned. +-- @param #WAREHOUSE.Assetitem asset Ground asset that will be spawned. -- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. -- @param #table parking Parking data for this asset. -- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned. @@ -900,8 +983,9 @@ function WAREHOUSE:_SpawnAssetAircraft(asset, request, parking) -- Set and empty route. if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then - --template.route.points=self:_GetFlightplan(group,_departure,_destination) - template.route.points[1] = {} + -- Get flight path. + template.route.points=self:_GetFlightplan(asset, self.airbase, request.warehouse.airbase) + --template.route.points[1] = {} else template.route.points[1] = {} end @@ -1013,9 +1097,9 @@ end --- Prepare a spawn template for the asset. Deep copy of asset template, adjusting template and unit names, nillifying group and unit ids. -- @param #WAREHOUSE self --- @param #WAREHOUSE.Stockitem asset Ground asset that will be spawned. +-- @param #WAREHOUSE.Assetitem asset Ground asset that will be spawned. -- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. --- @return #template Prepared new spawn template. +-- @return #table Prepared new spawn template. function WAREHOUSE:_SpawnAssetPrepareTemplate(asset, request) -- Create an own copy of the template! @@ -1127,7 +1211,7 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request) -- Check if destination is in range for all requested assets. for _,_asset in pairs(_assets) do - local asset=_asset --#WAREHOUSE.Stockitem + local asset=_asset --#WAREHOUSE.Assetitem -- Check if destination is in range. if asset.range Date: Mon, 20 Aug 2018 23:05:35 +0200 Subject: [PATCH 270/420] Warehouse v0.2.3 --- Moose Development/Moose/Core/Settings.lua | 2 +- .../Moose/Functional/Warehouse.lua | 154 ++++++++++++------ 2 files changed, 107 insertions(+), 49 deletions(-) diff --git a/Moose Development/Moose/Core/Settings.lua b/Moose Development/Moose/Core/Settings.lua index 819690607..d5909452d 100644 --- a/Moose Development/Moose/Core/Settings.lua +++ b/Moose Development/Moose/Core/Settings.lua @@ -364,7 +364,7 @@ do -- SETTINGS -- @param #SETTINGS self -- @return #boolean true if BRA function SETTINGS:IsA2A_BRAA() - self:E( { BRA = ( self.A2ASystem and self.A2ASystem == "BRAA" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BRAA() ) } ) + self:T( { BRA = ( self.A2ASystem and self.A2ASystem == "BRAA" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BRAA() ) } ) return ( self.A2ASystem and self.A2ASystem == "BRAA" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BRAA() ) end diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index e1fb8d8b2..cf04abcca 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -210,7 +210,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.2.2w" +WAREHOUSE.version="0.2.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -233,7 +233,7 @@ WAREHOUSE.version="0.2.2w" -- TODO: Handle cases for aircraft carriers and other ships. Place warehouse on carrier possible? On others probably not - exclude them? -- TODO: Handle cargo crates. -- TODO: Add general message function for sending to coaliton or debug. --- TODO: Use RAT for routing air units. Should be possible but might need some modifications of RAT, e.g. explit spawn place. But flight plan should be better. +-- NOGO: Use RAT for routing air units. Should be possible but might need some modifications of RAT, e.g. explit spawn place. But flight plan should be better. -- TODO: Can I make a request with specific assets? E.g., once delivered, make a request for exactly those assests that were in the original request. -- TODO: Handle the case when units of a group die during the transfer. Adjust template?! See Grouping in SPAWN. @@ -365,14 +365,14 @@ function WAREHOUSE:New(warehouse, alias) --- Trigger the FSM event "AddAsset". Add an airplane group to the warehouse stock. -- @function [parent=#WAREHOUSE] AddAsset -- @param #WAREHOUSE self - -- @param #string templategroupname Name of the late activated template group as defined in the mission editor. + -- @param Wrapper.Group#GROUP group Group to be added as new asset. -- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1. --- Trigger the FSM event "AddAsset" with a delay. Add an airplane group to the warehouse stock. -- @function [parent=#WAREHOUSE] __AddAsset -- @param #WAREHOUSE self -- @param #number delay Delay in seconds. - -- @param #string templategroupname Name of the late activated template group as defined in the mission editor. + -- @param Wrapper.Group#GROUP group Group to be added as new asset. -- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1. @@ -704,6 +704,7 @@ end -- @param #string To To state. function WAREHOUSE:onafterStatus(From, Event, To) self:E(self.wid..string.format("Checking status of warehouse %s.", self.alias)) + env.info(string.format("FF number of global assets = %d, current asset id = %d", #WAREHOUSE.db.Assets, WAREHOUSE.db.AssetID)) -- Print status. self:_DisplayStatus() @@ -712,8 +713,8 @@ function WAREHOUSE:onafterStatus(From, Event, To) self:_CheckConquered() -- Print queue. - self:_PrintQueue(self.queue, "Queue:") - self:_PrintQueue(self.pending, "Pending:") + self:_PrintQueue(self.queue, "Queue waiting") + self:_PrintQueue(self.pending, "Queue pending") -- Check if requests are valid and remove invalid one. self:_CheckRequestConsistancy(self.queue) @@ -755,24 +756,38 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups) -- Set default. local n=ngroups or 1 + -- Handle case where just a string is passed. + if type(group)=="string" then + group=GROUP:FindByName(group) + end + + env.info(string.format("Adding asset group %s", group:GetName())) + if group then -- Get unique ids from group name. local wid,aid,rid=self:_GetIDsFromGroup(group) -- Check if this is an known or a new asset group. - if aid~=nil and wid~=nil then + env.info(string.format("Adding known! asset group %s with id %d", group:GetName(), aid)) + -- We got a warehouse and asset id ==> this is an "old" group. local asset=self:_FindAssetInDB(group) + env.info(string.format("Adding known! asset group %s with id %d", group:GetName(), aid)) + -- Note the group is only added once, i.e. the ngroups parameter is ignored here. -- This is because usually these request comes from an asset that has been transfered from another warehouse and hence should only be added once. if asset~=nil then + env.info(string.format("Adding new asset to stock. asset id = %d, attribute = %s", asset.uid, asset.attribute)) table.insert(self.stock, asset) + else + env.error("ERROR known asset could not be found in global warehouse db!") end else + env.info("Asset unkonwn ==> registering in DB!") -- This is a group that is not in the db yet. Add it n times. local assets=self:_RegisterAsset(group, n) @@ -787,6 +802,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups) -- TODO: This causes a problem, when a completely new asset is added, i.e. not from a template group. -- Need to create a "zombie" template group maybe? if group:IsAlive()==true then + env.info("FF destorying group "..group:GetName()) group:Destroy() end @@ -799,11 +815,14 @@ end -- @param Wrapper.Group#GROUP group The group from which it is assumed that it has a registered asset. -- @return #WAREHOUSE.Assetitem The asset from the data base or nil if it could not be found. function WAREHOUSE:_FindAssetInDB(group) + -- Get unique ids from group name. local wid,aid,rid=self:_GetIDsFromGroup(group) if aid~=nil then + local asset=WAREHOUSE.db.Assets[aid] + self:E({asset=asset}) if asset==nil then self:E(string.format("ERROR: Asset for group %s not found in the data base!", group:GetName())) end @@ -827,9 +846,9 @@ function WAREHOUSE:_RegisterAsset(group, ngroups) -- Get the size of an object. local function _GetObjectSize(DCSdesc) if DCSdesc.box then - local x=DCSdesc.box.max.x+math.abs(DCSdesc.box.min.x) + local x=DCSdesc.box.max.x+math.abs(DCSdesc.box.min.x) --length local y=DCSdesc.box.max.y+math.abs(DCSdesc.box.min.y) --height - local z=DCSdesc.box.max.z+math.abs(DCSdesc.box.min.z) + local z=DCSdesc.box.max.z+math.abs(DCSdesc.box.min.z) --width return math.max(x,z), x , y, z end return 0,0,0,0 @@ -876,20 +895,13 @@ function WAREHOUSE:_RegisterAsset(group, ngroups) asset.DCSdesc=DCSdesc if i==1 then - env.info(string.format("New asset for warehouse %s:", self.alias)) - env.info(string.format("Group name = %s", group:GetName())) - env.info(string.format("Display name = %s", DCSdisplay)) - env.info(string.format("Category = %s", DCScategory)) - env.info(string.format("Type = %s", DCStype)) - env.info(string.format("Size max = %s", asset.size)) - env.info(string.format("Speed max = %s km/h", SpeedMax)) - env.info(string.format("Range min = %s m", RangeMin)) - self:E({fullassetdesc=DCSdesc}) + self:_AssetItemInfo(asset) end -- Add asset to global db. - table.insert(WAREHOUSE.db.Assets, {uid=asset.uid, asset=asset}) + WAREHOUSE.db.Assets[asset.uid]=asset + -- Add asset to the table that is retured. table.insert(assets,asset) end @@ -900,7 +912,19 @@ end -- @param #WAREHOUSE self -- @param #WAREHOUSE.Assetitem asset function WAREHOUSE:_AssetItemInfo(asset) - + -- Info about asset: + local text=string.format("\nNew asset with id=%d for warehouse %s:\n", asset.uid, self.alias) + text=text..string.format("Template name = %s\n", asset.templatename) + text=text..string.format("Unit type = %s\n", asset.unittype) + text=text..string.format("Attribute = %s\n", asset.attribute) + text=text..string.format("Category = %d\n", asset.category) + text=text..string.format("Units # = %d\n", asset.nunits) + text=text..string.format("Size max = %5.2f m\n", asset.size) + text=text..string.format("Speed max = %5.2f km/h\n", asset.speedmax) + text=text..string.format("Range max = %5.2f km\n", asset.range/1000) + self:E(self.wid..text) + self:E({DCSdesc=asset.DCSdesc}) + self:E({Template=asset.template}) end --- On after "AddAsset" event. Add a group to the warehouse stock. If the group is alive, it is destroyed. @@ -1871,12 +1895,13 @@ function WAREHOUSE:onafterSelfRequest(From, Event, To, groupset, request) --group:SmokeGreen() end - -- Remove pending request unless warehouse is under attack in which case we assume + -- Add a "defender request" to be able to despawn all assets once defeated. if self:IsAttacked() then - self.defenderrequest=request - self:_DeleteQueueItem(request, self.pending) + self.defenderrequest=request end + -- Remove pending request. + self:_DeleteQueueItem(request, self.pending) end --- On after "Attacked" event. Warehouse is under attack by an another coalition. @@ -1899,13 +1924,30 @@ end -- @param #string To To state. function WAREHOUSE:onafterDefeated(From, Event, To) self:E(self.wid..string.format("Attack was defeated!")) - --TODO Put all ground assets back in stock? How to remember which? Request id. Don't delete from pending? - local request=self.defenderrequest --#WAREHOUSE.Pendingitem - for _,group in pairs(request.cargogroupset:GetSetObjects()) do - -- Add assets back to stock. - self:__AddAsset(1,group) + + if self.defenderrequest then + + -- Get all assets that were deployed for defending the warehouse. + local request=self.defenderrequest --#WAREHOUSE.Pendingitem + + -- Route defenders back to warehoue (for visual reasons only) and put them back into stock. + for _,_group in pairs(request.cargogroupset:GetSetObjects()) do + local group=_group --Wrapper.Group#GROUP + + -- Get max speed of group and route it back slowly to the warehouse. + local speed=group:GetSpeedMax() + if group:IsGround() and speed>1 then + group:RouteGroundTo(self.coordinate, speed*0.3) + end + + -- Add asset group back to stock after 60 seconds. + self:__AddAsset(60, group) + end + + -- Set defender request back to nil. + self.defenderrequest=nil + end - self.defenderrequest=nil end --- On after "Captured" event. Warehouse has been captured by another coalition. @@ -2139,13 +2181,13 @@ end -- @param #WAREHOUSE self -- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventBirth(EventData) - self:T3(self.wid..string.format("Warehouse %s captured event birth!",self.alias)) + self:T3(self.wid..string.format("Warehouse %s captured event birth!", self.alias)) - if EventData and EventData.id==world.event.S_EVENT_BIRTH then - if EventData.IniGroup then - local group=EventData.IniGroup - --local asset=self:_FindAssetInDB(group) - --if asset. + if EventData and EventData.IniGroup then + local group=EventData.IniGroup + local wid,aid,rid=self:_GetIDsFromGroup(group) + if wid==self.uid then + self:E(self.wid..string.format("Warehouse %s captured event birth of its asset unit %s", self.alias, EventData.IniUnitName)) end end end @@ -2255,6 +2297,7 @@ function WAREHOUSE:_CheckConquered() -- Scan units in zone. --TODO: need to check if scan radius does what it should! + -- It seems to return units that are further away than the radius. local gotunits,_,_,units,_,_=coord:ScanObjects(radius, true, false, false) local Nblue=0 @@ -2270,13 +2313,19 @@ function WAREHOUSE:_CheckConquered() for _,_unit in pairs(units) do local unit=_unit --Wrapper.Unit#UNIT - -- Filter only groud units. - if unit:IsGround() then + local distance=coord:Get2DDistance(unit:GetCoordinate()) + + -- Filter only alive groud units. Also check distance again, because the scan routine might give some larger distances. + if unit:IsGround() and unit:IsAlive() and distance<= radius then -- Get coalition and country. local _coalition=unit:GetCoalition() local _country=unit:GetCountry() + -- Debug info. + self:E(self.wid..string.format("Unit %s in warehouse zone of radius=%d m. Coalition=%d, country=%d. Distance = %d m.",unit:GetName(), radius,_coalition,_country, distance)) + + -- Add up units for each side. if _coalition==coalition.side.BLUE then Nblue=Nblue+1 CountryBlue=_country @@ -2809,12 +2858,13 @@ function WAREHOUSE:_GetParkingForAssets(assetlist, parkingdata) local nunits=#group:GetUnits() local terminal=self:_GetTerminal(asset.attribute) - - env.info("asset name = "..tostring(asset.templatename)) - env.info("asset attribute = "..tostring(asset.attribute)) - env.info("terminal type = "..tostring(terminal)) - env.info("parking spots = "..tostring(nunits)) - env.info("parking spots = "..tostring(#parkingdata)) + -- Debug info + env.info(string.format("Parking spot search:")) + env.info(string.format("Asset name = %s", asset.templatename)) + env.info(string.format("Asset attribute = %s", asset.attribute)) + env.info(string.format("Terminal type = %d", terminal)) + env.info(string.format("Unit number = %d", nunits)) + env.info(string.format("Parking spots = %d", #parkingdata)) -- Find appropiate parking spots for this group. local spots=self.airbase:FindFreeParkingSpotForAircraft(group, terminal, nil, nil, nil, nil, nil, nil, parkingdata) @@ -3173,13 +3223,21 @@ end -- @param #table queue Queue to print. -- @param #string name Name of the queue for info reasons. function WAREHOUSE:_PrintQueue(queue, name) - self:E(self.wid..name) + local text=string.format("%s at %s: ",name, self.alias) for _,_qitem in ipairs(queue) do local qitem=_qitem --#WAREHOUSE.Queueitem - local text=self.wid..string.format("UID=%d, Prio=%d, Requestor=%s, Airbase=%s (category=%d), Descriptor: %s=%s, Nasssets=%s, Transport=%s, Ntransport=%d", - qitem.uid, qitem.prio, qitem.warehouse.alias, qitem.airbase:GetName(),qitem.category, qitem.assetdesc,tostring(qitem.assetdescval), tostring(qitem.nasset),qitem.transporttype,qitem.ntransport) - self:E(text) + -- Set airbase: + local airbasename="none" + if qitem.airbase then + airbasename=qitem.airbase:GetName() + end + local text=text..string.format("\nUID=%d, Prio=%d, Requestor=%s, Airbase=%s (category=%d), Descriptor: %s=%s, Nasssets=%s, Transport=%s, Ntransport=%d.", + qitem.uid, qitem.prio, qitem.warehouse.alias, airbasename, qitem.category, qitem.assetdesc,tostring(qitem.assetdescval), tostring(qitem.nasset), qitem.transporttype, qitem.ntransport) end + if #queue==0 then + text=text.."Empty." + end + self:E(self.wid..text) end --- Display status of warehouse. @@ -3282,7 +3340,7 @@ function WAREHOUSE:_GetFlightplan(asset, departure, destination) local Range=asset.range local _category=asset.category local ceiling=asset.DCSdesc.Hmax - local Vymax=asset.DCSDesc.VyMax + local Vymax=asset.DCSdesc.VyMax -- Max cruise speed 90% of max speed. local VxCruiseMax=0.90*Vmax From 31a5bfee9e35dcbda6480c5b49440ece214cc254 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Tue, 21 Aug 2018 16:38:55 +0200 Subject: [PATCH 271/420] Warehouse 0.2.3w --- .../Moose/Functional/Warehouse.lua | 350 +++++++++++++----- 1 file changed, 257 insertions(+), 93 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index cf04abcca..32fdb9cc0 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -38,7 +38,7 @@ -- @field #number uid Unit identifier of the warehouse. Derived from the associated airbase. -- @field #number markerid ID of the warehouse marker at the airbase. -- @field #number dTstatus Time interval in seconds of updating the warehouse status and processing new events. Default 30 seconds. --- @field #number assetid Unique id of asset items in stock. Essentially a running number starting at one and incremented when a new asset is added. +-- @field #number queueid Unit id of each request in the queue. Essentially a running number starting at one and incremented when a new request is added. -- @field #table stock Table holding all assets in stock. Table entries are of type @{#WAREHOUSE.Assetitem}. -- @field #table queue Table holding all queued requests. Table entries are of type @{#WAREHOUSE.Queueitem}. -- @field #table pending Table holding all pending requests, i.e. those that are currently in progress. Table entries are of type @{#WAREHOUSE.Pendingitem}. @@ -105,7 +105,6 @@ WAREHOUSE = { uid = nil, markerid = nil, dTstatus = 30, - assetid = 0, queueid = 0, stock = {}, queue = {}, @@ -210,7 +209,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.2.3" +WAREHOUSE.version="0.2.3w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -673,8 +672,7 @@ function WAREHOUSE:onafterStop(From, Event, To) self:UnHandleEvent(EVENTS.BaseCaptured) -- Clear all pending schedules. - self.CallScheduler:Clear() - + self.CallScheduler:Clear() end --- On after "Pause" event. Pauses the warehouse, i.e. no requests are processed. However, new requests and new assets can be added in this state. @@ -703,8 +701,8 @@ end -- @param #string Event Event. -- @param #string To To state. function WAREHOUSE:onafterStatus(From, Event, To) - self:E(self.wid..string.format("Checking status of warehouse %s.", self.alias)) - env.info(string.format("FF number of global assets = %d, current asset id = %d", #WAREHOUSE.db.Assets, WAREHOUSE.db.AssetID)) + self:E(self.wid..string.format("Checking status of warehouse %s. Current FSM state %s. Global warehouse asssets = %d.", self.alias, self:GetState(), #WAREHOUSE.db.Assets)) + --env.info(string.format("FF number of global assets = %d, current asset id = %d", #WAREHOUSE.db.Assets, WAREHOUSE.db.AssetID)) -- Print status. self:_DisplayStatus() @@ -713,14 +711,15 @@ function WAREHOUSE:onafterStatus(From, Event, To) self:_CheckConquered() -- Print queue. - self:_PrintQueue(self.queue, "Queue waiting") - self:_PrintQueue(self.pending, "Queue pending") + self:_PrintQueue(self.queue, "Queue waiting - before request") + self:_PrintQueue(self.pending, "Queue pending - before request") -- Check if requests are valid and remove invalid one. self:_CheckRequestConsistancy(self.queue) -- If warehouse is running than requests can be processed. if self:IsRunning() or self:IsAttacked() then + -- Check queue and handle requests if possible. local request=self:_CheckQueue() @@ -728,6 +727,11 @@ function WAREHOUSE:onafterStatus(From, Event, To) if request then self:Request(request) end + + -- Print queue after processing requests. + self:_PrintQueue(self.queue, "Queue waiting - after request") + self:_PrintQueue(self.pending, "Queue pending - after request") + end -- Update warhouse marker on F10 map. @@ -761,7 +765,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups) group=GROUP:FindByName(group) end - env.info(string.format("Adding asset group %s", group:GetName())) + self:E(string.format("Adding %d assets of group %s.", n, group:GetName())) if group then @@ -770,24 +774,20 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups) -- Check if this is an known or a new asset group. if aid~=nil and wid~=nil then - env.info(string.format("Adding known! asset group %s with id %d", group:GetName(), aid)) -- We got a warehouse and asset id ==> this is an "old" group. local asset=self:_FindAssetInDB(group) - env.info(string.format("Adding known! asset group %s with id %d", group:GetName(), aid)) - -- Note the group is only added once, i.e. the ngroups parameter is ignored here. -- This is because usually these request comes from an asset that has been transfered from another warehouse and hence should only be added once. if asset~=nil then - env.info(string.format("Adding new asset to stock. asset id = %d, attribute = %s", asset.uid, asset.attribute)) + self:E(string.format("Adding new asset with id = %d, attribute = %s to warehouse stock.", asset.uid, asset.attribute)) table.insert(self.stock, asset) else env.error("ERROR known asset could not be found in global warehouse db!") end else - env.info("Asset unkonwn ==> registering in DB!") -- This is a group that is not in the db yet. Add it n times. local assets=self:_RegisterAsset(group, n) @@ -878,7 +878,6 @@ function WAREHOUSE:_RegisterAsset(group, ngroups) local asset={} --#WAREHOUSE.Assetitem -- Increase asset unique id counter. - self.assetid=self.assetid+1 WAREHOUSE.db.AssetID=WAREHOUSE.db.AssetID+1 -- Set parameters. @@ -1036,12 +1035,7 @@ function WAREHOUSE:_SpawnAssetAircraft(asset, request, parking) spawnpoint.helipadId = nil end - -- Get the right terminal type for this kind of aircraft. - --local terminaltype=self:_GetTerminal(asset.attribute) - - -- Get parking data. - --local parking=self.airbase:GetFreeParkingSpotsTable(terminaltype) - + -- Check enough parking spots. if AirbaseCategory == Airbase.Category.HELIPAD or AirbaseCategory == Airbase.Category.SHIP then --TODO Figure out what's necessary in this case. @@ -1421,7 +1415,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Now we try to find all parking spots for all cargo groups in advance. Due to the for loop, the parking spots do not get updated while spawning. local Parking={} if _transportcategory==Group.Category.AIRPLANE or _transportcategory==Group.Category.HELICOPTER then - Parking=self:_GetParkingForAssets(_assetstock) + Parking=self:_FindParkingForAssets(self.airbase,_assetstock) end -- Transport assets table. @@ -1440,12 +1434,9 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Spawn with ALIAS here or DCS crashes! --local _alias=string.format("%s_%d", _assetitem.templatename,_assetitem.id) local _alias=self:_Alias(_assetitem, Request) - - -- Spawn plane at airport in uncontrolled state. - local _takeoff=SPAWN.Takeoff.Cold - --local spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias) - local spawn=SPAWN:NewFromTemplate(_assetitem.template,_assetitem.templatename,_alias):InitCoalition(self.coalition):InitCountry(self.country) - local spawngroup=spawn:InitUnControlled(true):SpawnAtAirbase(self.airbase,_takeoff, nil, nil, false, _parking) + + -- Spawn plane at airport in uncontrolled state. + local spawngroup=self:_SpawnAssetAircraft(_assetitem, Pending, Parking[_assetitem.uid]) if spawngroup then -- Set state of warehouse so we can retrieve it later. @@ -1474,17 +1465,12 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Get stock item. local _assetitem=_assetstock[i] --#WAREHOUSE.Assetitem - local _parking=Parking[i] -- Spawn with ALIAS here or DCS crashes! - --local _alias=string.format("%s_%d", _assetitem.templatename,_assetitem.id) local _alias=self:_Alias(_assetitem, Request) - -- Spawn helo at airport. - local _takeoff=SPAWN.Takeoff.Hot - --local spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias) - local spawn=SPAWN:NewFromTemplate(_assetitem.template,_assetitem.templatename,_alias):InitCoalition(self.coalition):InitCountry(self.country) - local spawngroup=spawn:InitUnControlled(false):SpawnAtAirbase(self.airbase,_takeoff, nil, nil, false, _parking) + -- Spawn plane at airport in uncontrolled state. + local spawngroup=self:_SpawnAssetAircraft(_assetitem, Pending, Parking[_assetitem.uid]) if spawngroup then -- Set state of warehouse so we can retrieve it later. @@ -1523,10 +1509,8 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Spawn with ALIAS here or DCS crashes! local _alias=self:_Alias(_assetitem, Request) - -- Spawn plane at airport in uncontrolled state. - --local spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias) - local spawn=SPAWN:NewFromTemplate(_assetitem.template,_assetitem.templatename,_alias):InitCoalition(self.coalition):InitCountry(self.country) - local spawngroup=spawn:SpawnFromCoordinate(self.spawnzone:GetRandomCoordinate()) + -- Spawn ground asset. + local spawngroup=self:_SpawnAssetGround(_assetitem, Request) if spawngroup then -- Set state of warehouse so we can retrieve it later. @@ -1600,7 +1584,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) Carrier:SmokeRed() -- Add carrier back to warehouse stock. Actual unit is destroyed. - warehouse:AddAsset(Carrier, 1) + warehouse:AddAsset(Carrier) end @@ -1643,7 +1627,8 @@ function WAREHOUSE:_SpawnAssetRequest(Request) -- Now we try to find all parking spots for all cargo groups in advance. Due to the for loop, the parking spots do not get updated while spawning. local Parking={} if _cargocategory==Group.Category.AIRPLANE or _cargocategory==Group.Category.HELICOPTER then - Parking=self:_GetParkingForAssets(_assetstock) + Parking=self:_FindParkingForAssets(self.airbase,_assetstock) + --Parking=self:_GetParkingForAssets(_assetstock) end -- Spawn aircraft in uncontrolled state if request comes from the same warehouse. @@ -1665,24 +1650,15 @@ function WAREHOUSE:_SpawnAssetRequest(Request) -- Get stock item. local _assetitem=_assetstock[i] --#WAREHOUSE.Assetitem - - -- Find a random point within the spawn zone. - local spawncoord=self.spawnzone:GetRandomCoordinate() -- Alias of the group. local _alias=self:_Alias(_assetitem, Request) - - -- Spawn object. Spawn with ALIAS here or DCS crashes! - --local _spawn=SPAWN:NewFromTemplate(_assetitem.template,_assetitem.templatename,_alias):InitCoalition(self.coalition):InitCountry(self.country) - --local _spawn=SPAWN:NewWithAlias(_assetitem.templatename,_alias):InitCoalition(self.coalition):InitCountry(self.country):InitUnControlled(UnControlled):InitAIOnOff(AIOnOff) - local _group=nil --Wrapper.Group#GROUP - local _attribute=_assetitem.attribute - + -- Spawn an asset group. + local _group=nil --Wrapper.Group#GROUP if _assetitem.category==Group.Category.GROUND then -- Spawn ground troops. - --_group=_spawn:SpawnFromCoordinate(spawncoord) _group=self:_SpawnAssetGround(_assetitem, Request) elseif _assetitem.category==Group.Category.AIRPLANE or _assetitem.category==Group.Category.HELICOPTER then @@ -1690,7 +1666,6 @@ function WAREHOUSE:_SpawnAssetRequest(Request) --TODO: spawn only so many groups as there are parking spots. Adjust request and create a new one with the reduced number! -- Spawn air units. - --_group=_spawn:SpawnAtAirbase(self.airbase, SPAWN.Takeoff.Cold, nil, nil, true, Parking[i]) _group=self:_SpawnAssetAircraft(_assetitem, Request, Parking[i]) elseif _assetitem.category==Group.Category.TRAIN then @@ -1708,7 +1683,7 @@ function WAREHOUSE:_SpawnAssetRequest(Request) _groupset:AddGroup(_group) table.insert(_assets, _assetitem) else - self:E(self.wid.."ERROR: cargo asset could not be spawned!") + self:E(self.wid.."ERROR: Cargo asset could not be spawned!") end end @@ -1792,7 +1767,7 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) end -- Move asset from pending queue into new warehouse. - request.warehouse:__AddAsset(60, group, 1) + request.warehouse:__AddAsset(60, group) -- All cargo delivered. if request and ncargo==0 then @@ -1913,7 +1888,8 @@ end -- @param DCS#country.id Country which is attacking the warehouse. function WAREHOUSE:onafterAttacked(From, Event, To, Coalition, Country) self:E(self.wid..string.format("Our warehouse is under attack!")) - --TODO: Spawn all ground units in the spawnzone? + + -- Spawn all ground units in the spawnzone? self:AddRequest(self, WAREHOUSE.Descriptor.CATEGORY, Group.Category.GROUND, "all", nil, nil , 0) end @@ -1969,6 +1945,17 @@ function WAREHOUSE:onafterCaptured(From, Event, To, Coalition, Country) end +--- On after "Destroyed" event. Warehouse was destroyed. Service is stopped. +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function WAREHOUSE:onafterDestroyed(From, Event, To) + self:E(self.wid..string.format("Our warehouse was destroyed!")) + -- Stop warehouse FSM. + self:Stop() +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Routing functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -2187,7 +2174,7 @@ function WAREHOUSE:_OnEventBirth(EventData) local group=EventData.IniGroup local wid,aid,rid=self:_GetIDsFromGroup(group) if wid==self.uid then - self:E(self.wid..string.format("Warehouse %s captured event birth of its asset unit %s", self.alias, EventData.IniUnitName)) + self:E(self.wid..string.format("Warehouse %s captured event birth of its asset unit %s.", self.alias, EventData.IniUnitName)) end end end @@ -2196,21 +2183,45 @@ end -- @param #WAREHOUSE self -- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventEngineStartup(EventData) - self:E(self.wid..string.format("Warehouse %s captured event engine startup!",self.alias)) + self:T3(self.wid..string.format("Warehouse %s captured event engine startup!",self.alias)) + + if EventData and EventData.IniGroup then + local group=EventData.IniGroup + local wid,aid,rid=self:_GetIDsFromGroup(group) + if wid==self.uid then + self:E(self.wid..string.format("Warehouse %s captured event engine startup of its asset unit %s.", self.alias, EventData.IniUnitName)) + end + end end --- Warehouse event handling function. -- @param #WAREHOUSE self -- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventTakeOff(EventData) - self:E(self.wid..string.format("Warehouse %s captured event takeoff!",self.alias)) + self:T3(self.wid..string.format("Warehouse %s captured event takeoff!",self.alias)) + + if EventData and EventData.IniGroup then + local group=EventData.IniGroup + local wid,aid,rid=self:_GetIDsFromGroup(group) + if wid==self.uid then + self:E(self.wid..string.format("Warehouse %s captured event takeoff of its asset unit %s.", self.alias, EventData.IniUnitName)) + end + end end --- Warehouse event handling function. -- @param #WAREHOUSE self -- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventLanding(EventData) - self:E(self.wid..string.format("Warehouse %s captured event landing!",self.alias)) + self:T3(self.wid..string.format("Warehouse %s captured event landing!",self.alias)) + + if EventData and EventData.IniGroup then + local group=EventData.IniGroup + local wid,aid,rid=self:_GetIDsFromGroup(group) + if wid==self.uid then + self:E(self.wid..string.format("Warehouse %s captured event landing of its asset unit %s.", self.alias, EventData.IniUnitName)) + end + end end --- Warehouse event handling function. @@ -2232,8 +2243,7 @@ function WAREHOUSE:_OnEventCrashOrDead(EventData) local warehousename=self.warehouse:GetName() if EventData.IniUnitName==warehousename then env.info(self.wid..string.format("Warehouse %s alias %s was destroyed!", warehousename, self.alias)) - --TODO: Add destroy event. - self:__Stop(1) + self:Destroyed() end end @@ -2251,34 +2261,34 @@ function WAREHOUSE:_OnEventBaseCaptured(EventData) return end - if EventData and EventData.id==world.event.S_EVENT_BASE_CAPTURED then - if EventData.Place then + if EventData and EventData.Place then - -- Place is the airbase that was captured. - local airbase=EventData.Place --Wrapper.Airbase#AIRBASE + -- Place is the airbase that was captured. + local airbase=EventData.Place --Wrapper.Airbase#AIRBASE + + if EventData.PlaceName==self.airbasename then + -- Okay, this airbase belongs or did belong to this warehouse. - if EventData.PlaceName==self.airbasename then - -- Okay, this airbase belongs or did belong to this warehouse. - - -- New coalition of airbase after it was captured. - local coalitionAirbase=airbase:GetCoalition() - - -- So what can happen? - -- Warehouse is blue, airbase is blue and belongs to warehouse and red captures it ==> self.airbase=nil - -- Warehouse is blue, airbase is blue self.airbase is nil and blue (re-)captures it ==> self.airbase=Event.Place - if self.airbase==nil then - -- Warehouse lost this airbase previously and not it was re-captured. - if coalitionAirbase == self.coalition then - self.airbase=airbase - end - else - -- Captured airbase belongs to this warehouse but was captured by other coaltion. - if coalitionAirbase ~= self.coalition then - self.airbase=nil - end + -- New coalition of airbase after it was captured. + local coalitionAirbase=airbase:GetCoalition() + + -- So what can happen? + -- Warehouse is blue, airbase is blue and belongs to warehouse and red captures it ==> self.airbase=nil + -- Warehouse is blue, airbase is blue self.airbase is nil and blue (re-)captures it ==> self.airbase=Event.Place + if self.airbase==nil then + -- Warehouse lost this airbase previously and not it was re-captured. + env.info("FF airbase of warehouse is nil") + if coalitionAirbase == self.coalition then + self.airbase=airbase + env.info("FF air") + end + else + -- Captured airbase belongs to this warehouse but was captured by other coaltion. + if coalitionAirbase ~= self.coalition then + self.airbase=nil end - end + end end end @@ -2702,12 +2712,9 @@ function WAREHOUSE:_CheckRequestNow(request) local _assetattribute=_assets[1].attribute local _assetcategory=_assets[1].category - -- Check available parking for asset units. - local Parkingdata - local Parking + -- Check available parking for air asset units. if self.airbase and (_assetcategory==Group.Category.AIRPLANE or _assetcategory==Group.Category.HELICOPTER) then - Parkingdata=self.airbase:GetParkingSpotsTable() - Parking, Parkingdata=self:_GetParkingForAssets(_assets, Parkingdata) + local Parking=self:_FindParkingForAssets(self.airbase,_assets) if Parking==nil then local text=string.format("Request denied! Not enough free parking spots for all assets at the moment.") MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) @@ -2741,7 +2748,7 @@ function WAREHOUSE:_CheckRequestNow(request) -- Check available parking for transport units. if self.airbase and (_transportcategory==Group.Category.AIRPLANE or _transportcategory==Group.Category.HELICOPTER) then - Parking, Parkingdata=self:_GetParkingForAssets(_transports, Parkingdata) + local Parking=self:_FindParkingForAssets(self.airbase,_transports) if Parking==nil then local text=string.format("Request denied! Not enough free parking spots for all transports at the moment.") MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) @@ -2893,6 +2900,148 @@ function WAREHOUSE:_GetParkingForAssets(assetlist, parkingdata) return assetparking, parkingdata end +---------------- + +--- Seach unoccupied parking spots at the airbase for a list of assets. For each asset group a list of parking spots is returned. +-- During the search also the not yet spawned asset aircraft are considered. +-- If not enough spots for all asset units could be found, the routine returns nil! +-- @param #WAREHOUSE self +-- @param Wrapper.Airbase#AIRBASE airbase The airbase where we search for parking spots. +-- @param #table assets A table of assets for which the parking spots are needed. +-- @return #table Table of coordinates and terminal IDs of free parking spots. Each table entry has the elements .Coordinate and .TerminalID. +function WAREHOUSE:_FindParkingForAssets(airbase, assets) + + -- Init default + local scanradius=50 + local scanunits=true + local scanstatics=true + local scanscenery=false + local verysafe=false + + -- Function calculating the overlap of two (square) objects. + local function _overlap(l1,l2,dist) + local safedist=(l1/2+l2/2)*1.1 + local safe = (dist > safedist) + self:T3(string.format("l1=%.1f l2=%.1f s=%.1f d=%.1f ==> safe=%s", l1,l2,safedist,dist,tostring(safe))) + return safe + end + + -- Get parking spot data table. This contains all free and "non-free" spots. + local parkingdata=airbase:GetParkingSpotsTable() + + -- List of obstacles. + local obstacles={} + + -- Loop over all parking spots and get the obstacles. + -- TODO: How long does this take on very large airbases, i.e. those with hundereds of parking spots? + for _,parkingspot in pairs(parkingdata) do + + -- Coordinate of the parking spot. + local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE + local _termid=parkingspot.TerminalID + + -- Obstacles at or around this parking spot. + obstacles[_termid]={} + + -- Scan a radius of 50 meters around the spot. + local _,_,_,_units,_statics,_sceneries=_spot:ScanObjects(scanradius, scanunits, scanstatics, scanscenery) + + -- Check all units. + for _,_unit in pairs(_units) do + local unit=_unit --Wrapper.Unit#UNIT + local _coord=unit:GetCoordinate() + local _size=self:_GetObjectSize(unit:GetDCSObject()) + local _name=unit:GetName() + table.insert(obstacles[_termid],{coord=_coord, size=_size, name=_name, type="unit"}) + end + + -- Check all statics. + for _,static in pairs(_statics) do + local _vec3=static:getPoint() + local _coord=COORDINATE:NewFromVec3(_vec3) + local _name=static:getName() + local _size=self:_GetObjectSize(static:GetDCSObject()) + table.insert(obstacles[_termid],{coord=_coord, size=_size, name=_name, type="static"}) + end + + -- Check all scenery. + for _,scenery in pairs(_sceneries) do + local _vec3=scenery:getPoint() + local _coord=COORDINATE:NewFromVec3(_vec3) + local _name=scenery:getTypeName() + local _size=self:_GetObjectSize(scenery:GetDCSObject()) + table.insert(obstacles[_termid],{coord=_coord, size=_size, name=_name, type="scenery"}) + end + + -- TODO Clients? Unoccupied client aircraft are also important! Are they already included in scanned units maybe? + + end + + -- Parking data for all assets. + local parking={} + + -- Loop over all assets that need a parking psot. + for _,asset in pairs(assets) do + + local _asset=asset --#WAREHOUSE.Assetitem + + local terminaltype=self:_GetTerminal(asset.attribute) + + -- Asset specific parking. + parking[_asset.uid]={} + + -- Loop over all units - each one needs a spot. + for i=1,_asset.nunits do + + -- Loop over all parking spots. + for _,parkingspot in pairs(parkingdata) do + + -- Check correct terminal type for asset. We don't want helos in shelters etc. + if AIRBASE._CheckTerminalType(parkingspot.TerminalType,terminaltype) then + + -- Coordinate of the parking spot. + local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE + local _termid=parkingspot.TerminalID + + -- Loop over all obstacles. + local free=true + for _,obstacle in pairs(obstacles[_termid]) do + + -- Check if aircraft overlaps with any obstacle. + local safe=_overlap(_asset.size, obstacle.size, _spot:Get2DDistance(obstacle.coord)) + + -- Spot is blocked. + if not safe then + free=false + break + end + + end + + if free then + + -- Add parkingspot for this asset unit. + table.insert(parking[_asset.uid], parkingspot) + + -- Add the unit as obstacle so that this spot will not be available for the next unit. + -- TODO Alternatively, I could remove this parking spot from the table, right? + obstacles[_termid]={coord=_spot, size=_asset.size, name=_asset.templatename, type="asset"} + + else + -- Not enough parking available! + return nil + end + + end -- check terminal type + end -- loop over parking spots + end -- loop over asset units + end -- loop over asset groups + + return parking +end + +----------------- + --- Get the request belonging to a group. -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group The group from which the info is gathered. @@ -3154,6 +3303,21 @@ function WAREHOUSE:_GetAttribute(groupname) return attribute end +--- Size of the bounding box of a DCS object derived from the DCS descriptor table. If boundinb box is nil, a size of zero is returned. +-- @param #WAREHOUSE self +-- @param DCS#Object DCSobject The DCS object for which the size is needed. +-- @return #number Max size of object in meters. +function WAREHOUSE:_GetObjectSize(DCSobject) + local DCSdesc=DCSobject:getDesc() + if DCSdesc.box then + local x=DCSdesc.box.max.x+math.abs(DCSdesc.box.min.x) --length + local y=DCSdesc.box.max.y+math.abs(DCSdesc.box.min.y) --height + local z=DCSdesc.box.max.z+math.abs(DCSdesc.box.min.z) --width + return math.max(x,z), x , y, z + end + return 0,0,0,0 +end + --- Returns the number of assets for each generalized attribute. -- @param #WAREHOUSE self -- @param #table stock The stock of the warehouse. From 64e67494b6851b021837c6c37f885990fc8bb7ee Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Tue, 21 Aug 2018 23:58:22 +0200 Subject: [PATCH 272/420] Warehouse v0.2.4 --- .../Moose/Functional/Warehouse.lua | 83 +++++++++++++------ Moose Development/Moose/Wrapper/Airbase.lua | 23 ++++- 2 files changed, 79 insertions(+), 27 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 32fdb9cc0..8b1fd74c7 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -41,7 +41,8 @@ -- @field #number queueid Unit id of each request in the queue. Essentially a running number starting at one and incremented when a new request is added. -- @field #table stock Table holding all assets in stock. Table entries are of type @{#WAREHOUSE.Assetitem}. -- @field #table queue Table holding all queued requests. Table entries are of type @{#WAREHOUSE.Queueitem}. --- @field #table pending Table holding all pending requests, i.e. those that are currently in progress. Table entries are of type @{#WAREHOUSE.Pendingitem}. +-- @field #table pending Table holding all pending requests, i.e. those that are currently in progress. Table elements are of type @{#WAREHOUSE.Pendingitem}. +-- @field #table defending Table holding all defending requests, i.e. self requests that were if the warehouse is under attack. Table elements are of type @{#WAREHOUSE.Pendingitem}. -- @extends Core.Fsm#FSM --- Manages ground assets of an airbase and offers the possibility to transport them to another airbase or warehouse. @@ -109,6 +110,7 @@ WAREHOUSE = { stock = {}, queue = {}, pending = {}, + defending = {}, } --- Item of the warehouse stock table. @@ -209,7 +211,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.2.3w" +WAREHOUSE.version="0.2.4" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -729,8 +731,8 @@ function WAREHOUSE:onafterStatus(From, Event, To) end -- Print queue after processing requests. - self:_PrintQueue(self.queue, "Queue waiting - after request") - self:_PrintQueue(self.pending, "Queue pending - after request") + self:_PrintQueue(self.queue, "Queue waiting - after request") + self:_PrintQueue(self.pending, "Queue pending - after request") end @@ -1429,10 +1431,8 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Get stock item. local _assetitem=_assetstock[i] --#WAREHOUSE.Assetitem - local _parking=Parking[i] -- Spawn with ALIAS here or DCS crashes! - --local _alias=string.format("%s_%d", _assetitem.templatename,_assetitem.id) local _alias=self:_Alias(_assetitem, Request) -- Spawn plane at airport in uncontrolled state. @@ -1666,7 +1666,7 @@ function WAREHOUSE:_SpawnAssetRequest(Request) --TODO: spawn only so many groups as there are parking spots. Adjust request and create a new one with the reduced number! -- Spawn air units. - _group=self:_SpawnAssetAircraft(_assetitem, Request, Parking[i]) + _group=self:_SpawnAssetAircraft(_assetitem, Request, Parking[_assetitem.uid]) elseif _assetitem.category==Group.Category.TRAIN then @@ -1872,7 +1872,8 @@ function WAREHOUSE:onafterSelfRequest(From, Event, To, groupset, request) -- Add a "defender request" to be able to despawn all assets once defeated. if self:IsAttacked() then - self.defenderrequest=request + --self.defenderrequest=request + table.insert(self.defending, request) end -- Remove pending request. @@ -1901,10 +1902,11 @@ end function WAREHOUSE:onafterDefeated(From, Event, To) self:E(self.wid..string.format("Attack was defeated!")) - if self.defenderrequest then + --if self.defenderrequest then + for _,request in pairs(self.defending) do -- Get all assets that were deployed for defending the warehouse. - local request=self.defenderrequest --#WAREHOUSE.Pendingitem + --local request=self.defenderrequest --#WAREHOUSE.Pendingitem -- Route defenders back to warehoue (for visual reasons only) and put them back into stock. for _,_group in pairs(request.cargogroupset:GetSetObjects()) do @@ -1921,9 +1923,13 @@ function WAREHOUSE:onafterDefeated(From, Event, To) end -- Set defender request back to nil. - self.defenderrequest=nil - + --self.defenderrequest=nil + + --self:_DeleteQueueItem(request, self.defending) end + + self.defending=nil + self.defending={} end --- On after "Captured" event. Warehouse has been captured by another coalition. @@ -1942,7 +1948,7 @@ function WAREHOUSE:onafterCaptured(From, Event, To, Coalition, Country) self.country=Country self.airbase=nil self.category=-1 - + end --- On after "Destroyed" event. Warehouse was destroyed. Service is stopped. @@ -2974,7 +2980,17 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) end -- TODO Clients? Unoccupied client aircraft are also important! Are they already included in scanned units maybe? - + --[[ + local clients=_DATABASE.CLIENTS + for _,_client in pairs(clients) do + local client=_client --Wrapper.Client#CLIENT + local unit=client:GetClientGroupUnit() + local _coord=unit:GetCoordinate() + local _name=unit:GetName() + local _size=self:_GetObjectSize(client:GetClientGroupDCSUnit()) + table.insert(obstacles[_termid],{coord=_coord, size=_size, name=_name, type="client"}) + end + ]] end -- Parking data for all assets. @@ -2994,25 +3010,31 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) for i=1,_asset.nunits do -- Loop over all parking spots. + local gotit=false for _,parkingspot in pairs(parkingdata) do -- Check correct terminal type for asset. We don't want helos in shelters etc. - if AIRBASE._CheckTerminalType(parkingspot.TerminalType,terminaltype) then + if AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) then -- Coordinate of the parking spot. local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE local _termid=parkingspot.TerminalID + local _toac=parkingspot.TOAC -- Loop over all obstacles. local free=true + local problem=nil for _,obstacle in pairs(obstacles[_termid]) do -- Check if aircraft overlaps with any obstacle. - local safe=_overlap(_asset.size, obstacle.size, _spot:Get2DDistance(obstacle.coord)) + local dist=_spot:Get2DDistance(obstacle.coord) + local safe=_overlap(_asset.size, obstacle.size, dist) -- Spot is blocked. if not safe then free=false + problem=obstacle + problem.dist=dist break end @@ -3023,17 +3045,29 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) -- Add parkingspot for this asset unit. table.insert(parking[_asset.uid], parkingspot) + self:E(self.wid..string.format("Parking spot #%d is free for asset id=%d!", _termid, _asset.uid)) + -- Add the unit as obstacle so that this spot will not be available for the next unit. -- TODO Alternatively, I could remove this parking spot from the table, right? - obstacles[_termid]={coord=_spot, size=_asset.size, name=_asset.templatename, type="asset"} + table.insert(obstacles[_termid], {coord=_spot, size=_asset.size, name=_asset.templatename, type="asset"}) + gotit=true + break else - -- Not enough parking available! - return nil + self:E(self.wid..string.format("Parking spot #%d is occupied or not big enough!", _termid)) + local coord=problem.coord --Core.Point#COORDINATE + local text=string.format("Obstacle blocking spot #%d is %s type %s with size=%.1f m and distance=%.1f m.", _termid, problem.name, problem.type, problem.size, problem.dist) + coord:MarkToAll(string.format(text)) end end -- check terminal type end -- loop over parking spots + + + if not gotit then + self:E(self.wid..string.format("WARNING: No free parking spot for asset id=%d",_asset.uid)) + return nil + end end -- loop over asset units end -- loop over asset groups @@ -3127,7 +3161,6 @@ function WAREHOUSE:_GetIDsFromGroup(group) return _wid,_aid,_rid end - self:E({_function="getids", group=group}) if group then -- Group name @@ -3137,10 +3170,10 @@ function WAREHOUSE:_GetIDsFromGroup(group) local wid,aid,rid=analyse(name) -- Debug info - self:E(self.wid..string.format("Group Name = %s", tostring(name))) - self:E(self.wid..string.format("Warehouse ID = %s", tostring(wid))) - self:E(self.wid..string.format("Asset ID = %s", tostring(aid))) - self:E(self.wid..string.format("Request ID = %s", tostring(rid))) + self:T3(self.wid..string.format("Group Name = %s", tostring(name))) + self:T3(self.wid..string.format("Warehouse ID = %s", tostring(wid))) + self:T3(self.wid..string.format("Asset ID = %s", tostring(aid))) + self:T3(self.wid..string.format("Request ID = %s", tostring(rid))) return wid,aid,rid else @@ -3395,7 +3428,7 @@ function WAREHOUSE:_PrintQueue(queue, name) if qitem.airbase then airbasename=qitem.airbase:GetName() end - local text=text..string.format("\nUID=%d, Prio=%d, Requestor=%s, Airbase=%s (category=%d), Descriptor: %s=%s, Nasssets=%s, Transport=%s, Ntransport=%d.", + text=text..string.format("\nUID=%d, Prio=%d, Requestor=%s, Airbase=%s (category=%d), Descriptor: %s=%s, Nasssets=%s, Transport=%s, Ntransport=%d.", qitem.uid, qitem.prio, qitem.warehouse.alias, airbasename, qitem.category, qitem.assetdesc,tostring(qitem.assetdescval), tostring(qitem.nasset), qitem.transporttype, qitem.ntransport) end if #queue==0 then diff --git a/Moose Development/Moose/Wrapper/Airbase.lua b/Moose Development/Moose/Wrapper/Airbase.lua index 7c9da217c..40282285a 100644 --- a/Moose Development/Moose/Wrapper/Airbase.lua +++ b/Moose Development/Moose/Wrapper/Airbase.lua @@ -4,7 +4,7 @@ -- -- ### Author: **FlightControl** -- --- ### Contributions: +-- ### Contributions: **funkyfranky** -- -- === -- @@ -260,6 +260,16 @@ AIRBASE.PersianGulf = { ["Shiraz_International_Airport"] = "Shiraz International Airport", ["Kerman_Airport"] = "Kerman Airport", } + +--- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy". +-- @type AIRBASE.ParkingSpot +-- @field Core.Point#COORDINATE Coordinate Coordinate of the parking spot. +-- @field #number TerminalID Terminal ID of the spot. Generally, this is not the same number as displayed in the mission editor. +-- @field #AIRBASE.TerminalType TerminalType Type of the spot, i.e. for which type of aircraft it can be used. +-- @field #boolean TOAC Takeoff or landing aircarft. I.e. this stop is occupied currently by an aircraft until it took of or until it landed. +-- @field #boolean Free This spot is currently free, i.e. there is no alive aircraft on it at the present moment. +-- @field #number TerminalID0 Unknown what this means. If you know, please tell us! +-- @field #number DistToRwy Distance to runway in meters. Currently bugged and giving the same number as the TerminalID. --- Terminal Types of parking spots. See also https://wiki.hoggitworld.com/view/DCS_func_getParking -- @@ -273,7 +283,16 @@ AIRBASE.PersianGulf = { -- * AIRBASE.TerminalType.OpenMedOrBig = 176: Combines OpenMed and OpenBig spots. -- * AIRBASE.TerminalType.HelicopterUnsable = 216: Combines HelicopterOnly, OpenMed and OpenBig. -- * AIRBASE.TerminalType.FighterAircraft = 244: Combines Shelter. OpenMed and OpenBig spots. So effectively all spots usable by fixed wing aircraft. --- @field TerminalType +-- +-- @type AIRBASE.TerminalType +-- @field #number Runway 16: Valid spawn points on runway. +-- @field #number HelicopterOnly 40: Special spots for Helicopers. +-- @field #number Shelter 68: Hardened Air Shelter. Currently only on Caucaus map. +-- @field #number OpenMed 72: Open/Shelter air airplane only. +-- @field #number OpenBig 104: Open air spawn points. Generally larger but does not guarantee large aircraft are capable of spawning there. +-- @field #number OpenMedOrBig 176: Combines OpenMed and OpenBig spots. +-- @field #number HelicopterUnsable 216: Combines HelicopterOnly, OpenMed and OpenBig. +-- @field #number FighterAircraft 244: Combines Shelter. OpenMed and OpenBig spots. So effectively all spots usable by fixed wing aircraft. AIRBASE.TerminalType = { Runway=16, HelicopterOnly=40, From c46d6879904ab6eea7e04622f531eb8d54f2f314 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 22 Aug 2018 16:44:58 +0200 Subject: [PATCH 273/420] Warehouse v0.2.4w --- .../Moose/Functional/Warehouse.lua | 350 ++++++++++++------ .../Moose/Wrapper/Controllable.lua | 7 +- 2 files changed, 233 insertions(+), 124 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 8b1fd74c7..e1880c243 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -74,14 +74,86 @@ -- Any kind of ground or airborn asset can be stored. Ships not supported at the moment due to the fact that airbases are bound to airbases which are -- normally not located near the sea. -- +-- # Creating a new warehouse +-- +-- A MOOSE warehouse must be represented in game by a phyical static object. For example, the mission editor already has warehouse as static object available. +-- This would be a good first choice but any static object will do. +-- +-- The positioning of the warehouse static object is very important for a couple of reasons. Firtly, a warehouse needs a good infrastructure so that spawned assets +-- have a proper road connection or can reach the associated airbase easily. +-- +-- Once the static warehouse object is placed in the mission editor it can be used as a MOOSE warehouse by the @{#WAREHOUSE.New}(*warehousestatic*, *alias*) constructor, +-- like for example: +-- +-- warehouse=WAREHOUSE:New(STATIC:FindByName("Warehouse Static Batumi"), "My Warehouse Alias") +-- warehouse:Start() +-- +-- So the first parameter *warehousestatic* is the static MOOSE object. By default, the name of the warehouse will be the same as the name given to the static object. +-- The second parameter *alias* can be used to choose a more convenient name if desired. This will be the name the warehouse calls itself when reporting messages. +-- -- # Adding Assets +-- +-- Assets can be added to the warehouse stock by using the @{#WAREHOUSE.AddAsset}(*group*, *ngroups*) function. The parameter *group* has to be a MOOSE @{Wrapper.Group#GROUP}. +-- The parameter *ngroups* specifies how many clones of this group are added to the stock. +-- +-- Note that the group should be a late activated template group, which was defined in the mission editor. +-- +-- infrantry=GROUP:FindByName("Some Infantry Group") +-- warehouse:AddAsset(infantry, 5) +-- +-- This will add five infantry groups to the warehouse stock. +-- +-- Note that you can also add assets with a delay by using the @{#WAREHOUSE.__AddAsset}(*delay*, *group*, *ngroups*), where *delay* is the delay in seconds before the asset is added. -- --- # Requests +-- # Requesting Assets +-- +-- Assets of the warehouse can be requested by other MOOSE warehouses. A request will first be scrutinize to check if can be fullfilled at all. If the request is valid, it is +-- put into the warehouse queue and processed as soon as possible. +-- +-- A request can be assed by the @{#WAREHOUSE.AddRequest}(*warehouse*, *AssetDescriptor*, *AssetDescriptorValue*, *nAsset*, *TransportType*, *nTransport*, *Prio*) function. +-- The parameters are +-- +-- * *warehouse*: The requesting MOOSE @{#WAREHOUSE}. Assets will be delivered there. +-- * *AssetDescriptor*: The descriptor to describe the asset "type". See the @{#WAREHOUSE.Descriptor} enumerator. For example, assets requested by their generalized attibute. +-- * *AssetDescriptorValue*: The value of the asset descriptor. +-- * *nAsset*: (Optional) Number of asset group requested. Default is one group. +-- * *TransportType*: (Optional) The transport method used to deliver the assets to the requestor. Default is that assets go to the requesting warehouse on their own. +-- * *nTransport*: (Optional) Number of asset groups used to transport the cargo assets from A to B. Default is one group. +-- * *Prio*: A number between 1 (high) and 100 (low) describing the priority of the request. Request with high priority are processed first. Default is 50, i.e. medium priority. +-- +-- So for example: +-- +-- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.INFANTRY, 5, WAREHOUSE.TransportType.APC, 2, 20) -- +-- Here, warehouse Kobuleti requests 5 infantry groups from warehouse Batumi. These "cargo" assets should be transported from Batumi to Kobuleti by 2 APCS. +-- Note that the warehouse at Batumi needs to have at least five infantry groups and two APC groups in their stock if the request can be processed. +-- If either to few infantry or APC groups are available when the request is made, the request is held in the warehouse queue until enough cargo and +-- transport assets are available. +-- +-- Also not that the above request is for five infantry units. So any group in stock that has the generalized attribute "INFANTRY" can be selected. +-- +-- A more specific request could look like: +-- +-- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.UNITTYPE, "A-10C", 2) +-- +-- Here, Kobuleti requests a specific unit type, in particular two groups of A-10Cs. Note that the spelling is important as it must exacly be the same as +-- what one get's when using the DCS unit type. +-- +-- An even more specific request would be: +-- +-- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.TEMPLATENAME, "Group Name as in ME", 3) +-- +-- In this case three groups named "Group Name as in ME" are requested. So this explicitly request the groups named like that in the Mission Editor. +-- +-- On the other hand, very general unspecifc requests can be made as +-- +-- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.CATEGORY, Group.Category.Ground, 10) +-- +-- Here, Kubuleti requests 10 ground groups and does not care which ones. This could be a mix of infantry, APCs, trucks etc. -- -- === -- --- # USAGE GUIDE +-- # Examples -- -- -- @@ -156,7 +228,7 @@ WAREHOUSE = { --- Descriptors enumerator describing the type of the asset. -- @type WAREHOUSE.Descriptor -- @field #string TEMPLATENAME Name of the asset template. --- @field #string UNITTYPE Typename of the DCS unit. +-- @field #string UNITTYPE Typename of the DCS unit, e.g. "A-10C". -- @field #string ATTRIBUTE Generalized attribute @{#WAREHOUSE.Attribute}. -- @field #string CATEGORY Asset category of type DCS#Group.Category, i.e. GROUND, AIRPLANE, HELICOPTER, SHIP, TRAIN. WAREHOUSE.Descriptor = { @@ -167,10 +239,22 @@ WAREHOUSE.Descriptor = { CATEGORY="category", } ---- Warehouse generalited categories. +--- Generalized asset attributes. Can be used to request assets with certain general characteristics. -- @type WAREHOUSE.Attribute -- @field #string TRANSPORT_PLANE Airplane with transport capability. Usually bigger, i.e. needs larger airbases and parking spots. -- @field #string TRANSPORT_HELO Helicopter with transport capability. +-- @field #string TRANSPORT_APC Amoured Personell Carrier. +-- @field #string FIGHER Fighter, interceptor, ... airplane. +-- @field #string TANKER Airplane which can refuel other aircraft. +-- @field #string AWACS Airborne Early Warning and Control System. +-- @field #string ARTILLERY Artillery assets. +-- @field #string INFANTRY Ground infantry assets. +-- @field #string BOMBER Aircraft which can be used for bombing. +-- @field #string TANK Tanks. +-- @field #string TRUCK Unarmed ground vehicles. +-- @field #string TRAIN Trains. +-- @field #string SHIP Naval assets. +-- @field #string OTHER Anything that does not fall into any other category. WAREHOUSE.Attribute = { TRANSPORT_PLANE="Transport_Plane", TRANSPORT_HELO="Transport_Helo", @@ -189,8 +273,14 @@ WAREHOUSE.Attribute = { OTHER="Other", } ---- Cargo transport type. +--- Cargo transport type. Defines how assets are transported to their destination. -- @type WAREHOUSE.TransportType +-- @field #string AIRPLANE Transports are conducted by airplanes. +-- @field #string HELICOPTER Transports are conducted by helicopters. +-- @field #string APC Transports are conducted by APCs. +-- @field #string SHIP Transports are conducted by ships. +-- @field #string TRAIN Transports are conducted by trains. +-- @field #string SELFPROPELLED Assets go to their destination by themselves. No transport carrier needed. WAREHOUSE.TransportType = { AIRPLANE = "Transport_Plane", HELICOPTER = "Transport_Helo", @@ -211,7 +301,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.2.4" +WAREHOUSE.version="0.2.4w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -237,6 +327,7 @@ WAREHOUSE.version="0.2.4" -- NOGO: Use RAT for routing air units. Should be possible but might need some modifications of RAT, e.g. explit spawn place. But flight plan should be better. -- TODO: Can I make a request with specific assets? E.g., once delivered, make a request for exactly those assests that were in the original request. -- TODO: Handle the case when units of a group die during the transfer. Adjust template?! See Grouping in SPAWN. +-- TODO: Set ROE for spawned groups. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor(s) @@ -315,38 +406,38 @@ function WAREHOUSE:New(warehouse, alias) -- Pseudo Functions - --- Triggers the FSM event "Start". Starts the warehouse. + --- Triggers the FSM event "Start". Starts the warehouse. Initializes parameters and starts event handlers. -- @function [parent=#WAREHOUSE] Start -- @param #WAREHOUSE self - --- Triggers the FSM event "Start" after a delay. Starts the warehouse. + --- Triggers the FSM event "Start" after a delay. Starts the warehouse. Initializes parameters and starts event handlers. -- @function [parent=#WAREHOUSE] __Start -- @param #WAREHOUSE self -- @param #number delay Delay in seconds. - --- Triggers the FSM event "Stop". Stops the warehouse. + --- Triggers the FSM event "Stop". Stops the warehouse and all its event handlers. -- @function [parent=#WAREHOUSE] Stop -- @param #WAREHOUSE self - --- Triggers the FSM event "Stop" after a delay. Stops the warehouse. + --- Triggers the FSM event "Stop" after a delay. Stops the warehouse and all its event handlers. -- @function [parent=#WAREHOUSE] __Stop -- @param #WAREHOUSE self -- @param #number delay Delay in seconds. - --- Triggers the FSM event "Pause". Pauses the warehouse. + --- Triggers the FSM event "Pause". Pauses the warehouse. Assets can still be added and requests be made. However, requests are not processed. -- @function [parent=#WAREHOUSE] Pauses -- @param #WAREHOUSE self - --- Triggers the FSM event "Pause" after a delay. Pause the warehouse. + --- Triggers the FSM event "Pause" after a delay. Pauses the warehouse. Assets can still be added and requests be made. However, requests are not processed. -- @function [parent=#WAREHOUSE] __Pause -- @param #WAREHOUSE self -- @param #number delay Delay in seconds. - --- Triggers the FSM event "Unpause". Pauses the warehouse. + --- Triggers the FSM event "Unpause". Unpauses the warehouse. Processing of queued requests is resumed. -- @function [parent=#WAREHOUSE] UnPause -- @param #WAREHOUSE self - --- Triggers the FSM event "Unpause" after a delay. Pause the warehouse. + --- Triggers the FSM event "Unpause" after a delay. Unpauses the warehouse. Processing of queued requests is resumed. -- @function [parent=#WAREHOUSE] __Unpause -- @param #WAREHOUSE self -- @param #number delay Delay in seconds. @@ -428,13 +519,83 @@ function WAREHOUSE:New(warehouse, alias) --- Triggers the FSM event "Delivered". A group has been delivered from the warehouse to another airbase or warehouse. -- @function [parent=#WAREHOUSE] Delivered -- @param #WAREHOUSE self - -- @param Core.Set#SET_GROUP groupset Set of groups that were delivered. + -- @param #WAREHOUSE.Pendingitem request Pending request that was now delivered. --- Triggers the FSM event "Delivered" after a delay. A group has been delivered from the warehouse to another airbase or warehouse. -- @function [parent=#WAREHOUSE] __Delivered -- @param #WAREHOUSE self -- @param #number delay Delay in seconds. - -- @param Core.Set#SET_GROUP groupset Set of groups that were delivered. + -- @param #WAREHOUSE.Pendingitem request Pending request that was now delivered. + + + --- Triggers the FSM event "SelfRequest". Request was initiated to the warehouse itself. Groups are just spawned at the warehouse or the associated airbase. + -- If the warehouse is currently under attack when the self request is made, the self request is added to the defending table. One the attack is defeated, + -- this request is used to put the groups back into the warehouse stock. + -- @function [parent=#WAREHOUSE] SelfRequest + -- @param #WAREHOUSE self + -- @param Core.Set#SET_GROUP groupset The set of cargo groups that was delivered to the warehouse itself. + -- @param #WAREHOUSE.Pendingitem request Pending self request. + + --- Triggers the FSM event "SelfRequest" with a delay. Request was initiated to the warehouse itself. Groups are just spawned at the warehouse or the associated airbase. + -- If the warehouse is currently under attack when the self request is made, the self request is added to the defending table. One the attack is defeated, + -- this request is used to put the groups back into the warehouse stock. + -- @function [parent=#WAREHOUSE] __SelfRequest + -- @param #WAREHOUSE self + -- @param #number delay Delay in seconds. + -- @param Core.Set#SET_GROUP groupset The set of cargo groups that was delivered to the warehouse itself. + -- @param #WAREHOUSE.Pendingitem request Pending self request. + + + --- Triggers the FSM event "Attacked" when a warehouse is under attack by an another coalition. + -- @param #WAREHOUSE self + -- @function [parent=#WAREHOUSE] Attacked + -- @param DCS#coalition.side Coalition which is attacking the warehouse. + -- @param DCS#country.id Country which is attacking the warehouse. + + --- Triggers the FSM event "Attacked" with a delay when a warehouse is under attack by an another coalition. + -- @param #WAREHOUSE self + -- @function [parent=#WAREHOUSE] __Attacked + -- @param #number delay Delay in seconds. + -- @param DCS#coalition.side Coalition which is attacking the warehouse. + -- @param DCS#country.id Country which is attacking the warehouse. + + + --- Triggers the FSM event "Defeated" when an attack from an enemy was defeated. + -- @param #WAREHOUSE self + -- @function [parent=#WAREHOUSE] Defeated + -- @param DCS#coalition.side Coalition which is attacking the warehouse. + -- @param DCS#country.id Country which is attacking the warehouse. + + --- Triggers the FSM event "Defeated" with a delay when an attack from an enemy was defeated. + -- @param #WAREHOUSE self + -- @function [parent=#WAREHOUSE] __Defeated + -- @param #number delay Delay in seconds. + -- @param DCS#coalition.side Coalition which is attacking the warehouse. + -- @param DCS#country.id Country which is attacking the warehouse. + + + --- Triggers the FSM event "Captured" when a warehouse has been captured by another coalition. + -- @param #WAREHOUSE self + -- @function [parent=#WAREHOUSE] Captured + -- @param DCS#coalition.side Coalition which captured the warehouse. + -- @param DCS#country.id Country which has captured the warehouse. + + --- Triggers the FSM event "Captured" with a delay when a warehouse has been captured by another coalition. + -- @param #WAREHOUSE self + -- @function [parent=#WAREHOUSE] __Captured + -- @param #number delay Delay in seconds. + -- @param DCS#coalition.side Coalition which captured the warehouse. + -- @param DCS#country.id Country which has captured the warehouse. + + + --- Triggers the FSM event "Destroyed" when the warehouse was destroyed. All services are stopped. + -- @param #WAREHOUSE self + -- @function [parent=#WAREHOUSE] Destroyed + + --- Triggers the FSM event "Destroyed" with a delay when the warehouse was destroyed. All services are stopped. + -- @param #WAREHOUSE self + -- @function [parent=#WAREHOUSE] Destroyed + -- @param #number delay Delay in seconds. return self end @@ -1327,24 +1488,32 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) local group=_spawngroup --Wrapper.Group#GROUP - local ToCoordinate=Request.warehouse.spawnzone:GetRandomCoordinate(50) - ToCoordinate:MarkToAll("Destination") -- Route cargo to their destination. - if _cargocategory==Group.Category.GROUND then + if _cargocategory==Group.Category.GROUND then env.info("FF route ground "..group:GetName()) - self:_RouteGround(group, ToCoordinate) - elseif _cargocategory==Group.Category.AIRPLANE then - env.info("FF route plane "..group:GetName()) - self:_RouteAir(group, Request.airbase) - elseif _cargocategory==Group.Category.HELICOPTER then - env.info("FF route helo "..group:GetName()) + + -- Random place in the spawn zone of the requesting warehouse. + local ToCoordinate=Request.warehouse.spawnzone:GetRandomCoordinate() + ToCoordinate:MarkToAll("Destination") + + -- Route ground. + self:_RouteGround(group, Request) + + elseif _cargocategory==Group.Category.AIRPLANE or _cargocategory==Group.Category.HELICOPTER then + env.info("FF route aircraft "..group:GetName()) + + -- Route plane the the requesting warehouses airbase. + -- Actually, the route is already set. We only need to activate the uncontrolled group. self:_RouteAir(group, Request.airbase) + elseif _cargocategory==Group.Category.SHIP then self:E("ERROR: self propelled ship not implemented yet!") elseif _cargocategory==Group.Category.TRAIN then env.info("FF route train "..group:GetName()) - self:_RouteTrain(group, ToCoordinate) + + -- Route train to the rail connection of the requesting warehouse. + self:_RouteTrain(group, Request.warehouse.rail) else self:E(self.wid..string.format("ERROR: unknown category %s for self propelled cargo %s!",tostring(_cargocategory), tostring(group:GetName()))) end @@ -1627,8 +1796,7 @@ function WAREHOUSE:_SpawnAssetRequest(Request) -- Now we try to find all parking spots for all cargo groups in advance. Due to the for loop, the parking spots do not get updated while spawning. local Parking={} if _cargocategory==Group.Category.AIRPLANE or _cargocategory==Group.Category.HELICOPTER then - Parking=self:_FindParkingForAssets(self.airbase,_assetstock) - --Parking=self:_GetParkingForAssets(_assetstock) + Parking=self:_FindParkingForAssets(self.airbase,_assetstock) end -- Spawn aircraft in uncontrolled state if request comes from the same warehouse. @@ -1828,12 +1996,12 @@ function WAREHOUSE:_UpdatePending(group) end ---- On after "Delivered" event. +--- On after "Delivered" event. Triggered when all asset groups have reached their destination. Corresponding request is deleted from the pending queue. -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param #WAREHOUSE.Pendingitem request +-- @param #WAREHOUSE.Pendingitem request The pending request that is finished and deleted from the pending queue. function WAREHOUSE:onafterDelivered(From, Event, To, request) -- Debug info @@ -1851,11 +2019,13 @@ function WAREHOUSE:onafterDelivered(From, Event, To, request) end --- On after "SelfRequest" event. Request was initiated to the warehouse itself. Groups are just spawned at the warehouse or the associated airbase. +-- If the warehouse is currently under attack when the self request is made, the self request is added to the defending table. One the attack is defeated, +-- this request is used to put the groups back into the warehouse stock. -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param Core.Set#SET_GROUP groupset The set of cargo groups that was delivered. +-- @param Core.Set#SET_GROUP groupset The set of cargo groups that was delivered to the warehouse itself. -- @param #WAREHOUSE.Pendingitem request Pending self request. function WAREHOUSE:onafterSelfRequest(From, Event, To, groupset, request) @@ -1894,7 +2064,7 @@ function WAREHOUSE:onafterAttacked(From, Event, To, Coalition, Country) self:AddRequest(self, WAREHOUSE.Descriptor.CATEGORY, Group.Category.GROUND, "all", nil, nil , 0) end ---- On after "Defeated" event. Warehouse defeated an attack by another coalition. +--- On after "Defeated" event. Warehouse defeated an attack by another coalition. Defender assets are added back to warehouse stock. -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. @@ -1951,7 +2121,7 @@ function WAREHOUSE:onafterCaptured(From, Event, To, Coalition, Country) end ---- On after "Destroyed" event. Warehouse was destroyed. Service is stopped. +--- On after "Destroyed" event. Warehouse was destroyed. All services are stopped. -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. @@ -1968,30 +2138,41 @@ end --- Route ground units to destination. -- @param #WAREHOUSE self --- @param Wrapper.Group#GROUP Group The ground group. --- @param Core.Point#COORDINATE Coordinate of the destination. +-- @param Wrapper.Group#GROUP group The ground group to be routed +-- @param #WAREHOUSE.Queueitem request The request for this group. -- @param #number Speed Speed in km/h to drive to the destination coordinate. Default is 60% of max possible speed the unit can go. -function WAREHOUSE:_RouteGround(Group, Coordinate, Speed) +function WAREHOUSE:_RouteGround(group, request) - if Group and Group:IsAlive() then + if group and group:IsAlive() then - -- Set speed. - local _speed=Speed or Group:GetSpeedMax()*0.6 + -- Set speed to 70% of max possible. + local _speed=group:GetSpeedMax()*0.7 -- Create task. - -- TODO: It might be necessary to ALWAYS route the group to the road connection first. - -- At the moment, the random spawn point might give another first road point which could also be a dead end like in Kobuliti(?). - local Waypoints, canroad = Group:TaskGroundOnRoad(Coordinate, _speed, "Off Road", true) + -- DONE: It might be necessary to ALWAYS route the group to the road connection first. + -- At the moment, the random spawn point might give another first road point which could also be a dead end like in Kobuliti(?). + -- TODO: Might be necessary to include the current coordinate as first waypoint?! + + -- Waypoints for road-to-road connection. + local Waypoints, canroad = group:TaskGroundOnRoad(request.warehouse.road, _speed, "Off Road", false, self.road) + + -- First waypoint = current position of the group. + local FromWP=group:GetCoordinate():WaypointGround(_speed, "Off Road") + table.insert(Waypoints, FromWP, 1) + + -- Final coordinate. + local ToWP=request.warehouse.spawnzone:GetRandomCoordinate():WaypointGround(_speed, "Off Road") + table.insert(Waypoints, ToWP, #Waypoints) -- Task function triggering the arrived event. - local TaskFunction = Group:TaskFunction("WAREHOUSE._Arrived", self) + local TaskFunction = group:TaskFunction("WAREHOUSE._Arrived", self) -- Put task function on last waypoint. local Waypoint = Waypoints[#Waypoints] - Group:SetTaskWaypoint(Waypoint, TaskFunction) + group:SetTaskWaypoint(Waypoint, TaskFunction) -- Route group to destination. - Group:Route(Waypoints, 1) + group:Route(Waypoints, 1) end end @@ -2839,74 +3020,6 @@ function WAREHOUSE:_GetTerminal(_attribute) return _terminal end ---- Get parking data for all air assets that need to be spawned at an airbase. ---@param #WAREHOUSE self ---@param #table assetlist A list of assets for which parking spots are required. ---@param #table parkingdata Table of the complete parking data to check. Default is to take it from the @{Wrapper.Airbase#AIRBASE.GetParkingSpotsTable}() function. ---@return #table A table with parking spots for each asset group. ---@return #table The reduced parking data table of the spots that have not been assigned. -function WAREHOUSE:_GetParkingForAssets(assetlist, parkingdata) - - --- Remove selected spots from parking data table. - local function removeparking(parkingdata,spots) - for j=1,#spots do - for i=1,#parkingdata do - if parkingdata[i].TerminalID==spots[j].TerminalID then - table.remove(parkingdata, i) - break - end - end - end - end - - -- Get complete parking data of the airbase. - parkingdata=parkingdata or self.airbase:GetParkingSpotsTable() - - local assetparking={} - for i=1,#assetlist do - - -- Asset specifics. - local asset=assetlist[i] --#WAREHOUSE.Assetitem - local group=GROUP:FindByName(asset.templatename) - local nunits=#group:GetUnits() - local terminal=self:_GetTerminal(asset.attribute) - - -- Debug info - env.info(string.format("Parking spot search:")) - env.info(string.format("Asset name = %s", asset.templatename)) - env.info(string.format("Asset attribute = %s", asset.attribute)) - env.info(string.format("Terminal type = %d", terminal)) - env.info(string.format("Unit number = %d", nunits)) - env.info(string.format("Parking spots = %d", #parkingdata)) - - -- Find appropiate parking spots for this group. - local spots=self.airbase:FindFreeParkingSpotForAircraft(group, terminal, nil, nil, nil, nil, nil, nil, parkingdata) - - for _,spot in pairs(spots) do - if spot then - local coord=spot.Coordinate --Core.Point#COORDINATE - local text=string.format("Parking spot for %s:\nAsset id=%d, Terminal id=%d", asset.templatename, asset.uid, spot.TerminalID) - --coord:MarkToAll(text) - self:E(self.wid..text) - end - end - - -- Not enough parking spots for this group. - if #spots Date: Wed, 22 Aug 2018 20:41:37 +0200 Subject: [PATCH 274/420] New weight driven limits logic for cargo to load multiple cargo. Fixed near range issue for carriers. now the infantry disappearance range for boarding is calculated by the carrier bounding range, which is derived from the bounding rectangle on the Y-axis. The near range parameter can still be provided and will be interpreted as the loading range for static cargo objects! --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 37 +----- .../Moose/AI/AI_Cargo_Airplane.lua | 5 +- .../Moose/AI/AI_Cargo_Helicopter.lua | 4 +- Moose Development/Moose/Cargo/Cargo.lua | 2 +- Moose Development/Moose/Cargo/CargoUnit.lua | 11 +- .../Moose/Tasking/Task_CARGO.lua | 15 ++- .../Moose/Wrapper/Positionable.lua | 121 +++++++++++++++++- Moose Development/Moose/Wrapper/Unit.lua | 44 ------- 8 files changed, 134 insertions(+), 105 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index acf565f81..c1c871cdd 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -197,42 +197,7 @@ function AI_CARGO_APC:New( APC, CargoSet, CombatRadius ) self:SetCarrier( APC ) for _, APCUnit in pairs( APC:GetUnits() ) do - local Desc = APCUnit:GetDesc() - - local VolumeUnit = ( Desc.box.max.x - Desc.box.min.x ) * ( Desc.box.max.y - Desc.box.min.y ) * ( Desc.box.max.z - Desc.box.min.z ) - - local Weights = { - ["M1126 Stryker ICV"] = 9, - ["M-113"] = 9, - ["AAV7"] = 25, - ["M2A1_halftrack"] = 9, - ["BMD-1"] = 9, - ["BMP-1"] = 8, - ["BMP-2"] = 7, - ["BMP-3"] = 8, - ["Boman"] = 25, - ["BTR-80"] = 9, - ["BTR_D"] = 12, - ["Cobra"] = 8, - ["LAV-25"] = 6, - ["M-2 Bradley"] = 6, - ["M1043 HMMWV Armament"] = 4, - ["M1045 HMMWV TOW"] = 4, - ["M1126 Stryker ICV"] = 9, - ["M1134 Stryker ATGM"] = 9, - ["Marder"] = 6, - ["MCV-80"] = 9, - ["MLRS FDDM"] = 4, - ["MTLB"] = 25, - ["TPZ"] = 10, - } - - local CargoBayWeightLimit = ( Weights[Desc.typeName] or 0 ) * 70 - - APCUnit:SetCargoBayWeightLimit( CargoBayWeightLimit ) - --Airplane:SetCargoBayVolumeLimit( 15 ) - - self:F( {TypeName = Desc.typeName, Desc = Desc, WeightLimit = CargoBayWeightLimit } ) + APCUnit:SetCargoBayWeightLimit() end self.Transporting = false diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 638211643..1d5d1e398 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -128,10 +128,7 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) -- Set carrier. self:SetCarrier( Airplane ) - local Desc = Airplane:GetUnit(1):GetDesc() - self:F({Desc=Desc}) - Airplane:SetCargoBayWeightLimit( Desc.massMax - ( Desc.massEmpty + Desc.fuelMassMax ) ) - --Airplane:SetCargoBayVolumeLimit( 15 ) + Airplane:SetCargoBayWeightLimit() self.Relocating = true diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 1341129f6..0148e9c79 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -146,9 +146,7 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) ) for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do - local Desc = HelicopterUnit:GetDesc() - self:F({Desc=Desc}) - HelicopterUnit:SetCargoBayWeightLimit( Desc.massMax - ( Desc.massEmpty + Desc.fuelMassMax ) ) + HelicopterUnit:SetCargoBayWeightLimit() end self.Relocating = false diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 3311a1bff..82015511e 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -811,7 +811,7 @@ do -- CARGO end - --- Check if CargoCarrier is near the Cargo to be Loaded. + --- Check if CargoCarrier is near the coordinate within NearRadius. -- @param #CARGO self -- @param Core.Point#COORDINATE Coordinate -- @param #number NearRadius The radius when the cargo will board the Carrier (to avoid collision). diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index 90eceeda9..da9c73f2b 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -226,8 +226,6 @@ do -- CARGO_UNIT function CARGO_UNIT:onafterBoard( From, Event, To, CargoCarrier, NearRadius, ... ) self:F( { From, Event, To, CargoCarrier, NearRadius } ) - local NearRadius = NearRadius or 25 - self.CargoInAir = self.CargoObject:InAir() local Desc = self.CargoObject:GetDesc() @@ -239,6 +237,9 @@ do -- CARGO_UNIT -- Only move the group to the carrier when the cargo is not in the air -- (eg. cargo can be on a oil derrick, moving the cargo on the oil derrick will drop the cargo on the sea). if not self.CargoInAir then + -- If NearRadius is given, then use the given NearRadius, otherwise calculate the NearRadius + -- based upon the Carrier bounding radius, which is calculated from the bounding rectangle on the Y axis. + local NearRadius = CargoCarrier:GetBoundingRadius( NearRadius ) if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then self:Load( CargoCarrier, NearRadius, ... ) else @@ -250,8 +251,6 @@ do -- CARGO_UNIT local Angle = 180 local Distance = 5 - NearRadius = NearRadius or 25 - local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) @@ -302,8 +301,6 @@ do -- CARGO_UNIT local Angle = 180 local Distance = 5 - NearRadius = NearRadius or 25 - local CargoCarrierPointVec2 = CargoCarrier:GetPointVec2() local CargoCarrierHeading = CargoCarrier:GetHeading() -- Get Heading of object in degrees. local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) @@ -349,8 +346,6 @@ do -- CARGO_UNIT local Angle = 180 local Distance = 5 - local NearRadius = NearRadius or 25 - if From == "UnLoaded" or From == "Boarding" then end diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index fd1d60ef7..ae4ebc7b8 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -589,6 +589,12 @@ do -- TASK_CARGO Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) Fsm:AddTransition( "Failed", "Fail", "Failed" ) + for _, Group in pairs( SetGroup:GetSet() ) do + for __, Unit in pairs( Group:GetUnits() ) do + local Unit = Unit -- Wrapper.Unit#UNIT + Unit:SetCargoBayWeightLimit() + end + end ---- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit @@ -610,7 +616,6 @@ do -- TASK_CARGO local TaskUnitName = TaskUnit:GetName() local MenuTime = Task:InitTaskControlMenu( TaskUnit ) local MenuControl = Task:GetTaskControlMenu( TaskUnit ) - local CargoItemCount = TaskUnit:CargoItemCount() Task.SetCargo:ForEachCargo( @@ -635,7 +640,13 @@ do -- TASK_CARGO local TaskGroup = TaskUnit:GetGroup() if Cargo:IsUnLoaded() then - if CargoItemCount < 1 then + local CargoBayFreeWeight = TaskUnit:GetCargoBayFreeWeight() + local CargoWeight = Cargo:GetWeight() + + self:F({CargoBayFreeWeight=CargoBayFreeWeight}) + + -- Only when there is space within the bay to load the next cargo item! + if CargoBayFreeWeight > CargoWeight then if Cargo:IsInReportRadius( TaskUnit:GetPointVec2() ) then local NotInDeployZones = true for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 287338c2b..b0200fd24 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -243,7 +243,7 @@ end --- Get the bounding box of the underlying POSITIONABLE DCS Object. -- @param #POSITIONABLE self --- @return DCS#Distance The bounding box of the POSITIONABLE. +-- @return DCS#Box3 The bounding box of the POSITIONABLE. -- @return #nil The POSITIONABLE is not existing or alive. function POSITIONABLE:GetBoundingBox() --R2.1 self:F2() @@ -264,6 +264,29 @@ function POSITIONABLE:GetBoundingBox() --R2.1 end +--- Get the bounding radius of the underlying POSITIONABLE DCS Object. +-- @param #POSITIONABLE self +-- @return DCS#Distance The bounding radius of the POSITIONABLE. +-- @return #nil The POSITIONABLE is not existing or alive. +function POSITIONABLE:GetBoundingRadius() + self:F2() + + local Box = self:GetBoundingBox() + + + if Box then + local X = Box.max.x - Box.min.x + local Z = Box.max.z - Box.min.z + local CX = X / 2 + local CZ = Z / 2 + return math.max( CX, CZ ) + end + + BASE:E( { "Cannot GetBoundingRadius", Positionable = self, Alive = self:IsAlive() } ) + + return nil +end + --- Returns the altitude of the POSITIONABLE. -- @param Wrapper.Positionable#POSITIONABLE self -- @return DCS#Distance The altitude of the POSITIONABLE. @@ -323,7 +346,7 @@ end --- Returns the POSITIONABLE heading in degrees. -- @param Wrapper.Positionable#POSITIONABLE self --- @return #number The POSTIONABLE heading +-- @return #number The POSITIONABLE heading -- @return #nil The POSITIONABLE is not existing or alive. function POSITIONABLE:GetHeading() local DCSPositionable = self:GetDCSObject() @@ -347,6 +370,52 @@ function POSITIONABLE:GetHeading() return nil end +-- Is Methods + +--- Returns if the unit is of an air category. +-- If the unit is a helicopter or a plane, then this method will return true, otherwise false. +-- @param #POSITIONABLE self +-- @return #boolean Air category evaluation result. +function POSITIONABLE:IsAir() + self:F2() + + local DCSUnit = self:GetDCSObject() + + if DCSUnit then + local UnitDescriptor = DCSUnit:getDesc() + self:T3( { UnitDescriptor.category, Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } ) + + local IsAirResult = ( UnitDescriptor.category == Unit.Category.AIRPLANE ) or ( UnitDescriptor.category == Unit.Category.HELICOPTER ) + + self:T3( IsAirResult ) + return IsAirResult + end + + return nil +end + +--- Returns if the unit is of an ground category. +-- If the unit is a ground vehicle or infantry, this method will return true, otherwise false. +-- @param #POSITIONABLE self +-- @return #boolean Ground category evaluation result. +function POSITIONABLE:IsGround() + self:F2() + + local DCSUnit = self:GetDCSObject() + + if DCSUnit then + local UnitDescriptor = DCSUnit:getDesc() + self:T3( { UnitDescriptor.category, Unit.Category.GROUND_UNIT } ) + + local IsGroundResult = ( UnitDescriptor.category == Unit.Category.GROUND_UNIT ) + + self:T3( IsGroundResult ) + return IsGroundResult + end + + return nil +end + --- Returns true if the POSITIONABLE is in the air. -- Polymorphic, is overridden in GROUP and UNIT. @@ -895,12 +964,50 @@ do -- Cargo -- @param #POSITIONABLE self -- @param #number WeightLimit function POSITIONABLE:SetCargoBayWeightLimit( WeightLimit ) - self.__.CargoBayWeightLimit = WeightLimit - end - - - + if WeightLimit then + self.__.CargoBayWeightLimit = WeightLimit + else + -- If weightlimit is not provided, we will calculate it depending on the type of unit. + + -- When an airplane or helicopter, we calculate the weightlimit based on the descriptor. + if self:IsAir() then + local Desc = self:GetDesc() + self:F({Desc=Desc}) + self.__.CargoBayWeightLimit = Desc.massMax - ( Desc.massEmpty + Desc.fuelMassMax ) + else + local Desc = self:GetDesc() + local Weights = { + ["M1126 Stryker ICV"] = 9, + ["M-113"] = 9, + ["AAV7"] = 25, + ["M2A1_halftrack"] = 9, + ["BMD-1"] = 9, + ["BMP-1"] = 8, + ["BMP-2"] = 7, + ["BMP-3"] = 8, + ["Boman"] = 25, + ["BTR-80"] = 9, + ["BTR_D"] = 12, + ["Cobra"] = 8, + ["LAV-25"] = 6, + ["M-2 Bradley"] = 6, + ["M1043 HMMWV Armament"] = 4, + ["M1045 HMMWV TOW"] = 4, + ["M1126 Stryker ICV"] = 9, + ["M1134 Stryker ATGM"] = 9, + ["Marder"] = 6, + ["MCV-80"] = 9, + ["MLRS FDDM"] = 4, + ["MTLB"] = 25, + ["TPZ"] = 10, + } + + local CargoBayWeightLimit = ( Weights[Desc.typeName] or 0 ) * 70 + self.__.CargoBayWeightLimit = CargoBayWeightLimit + end + end + end end --- Cargo --- Signal a flare at the position of the POSITIONABLE. diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 753b842f4..059a0266c 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -854,51 +854,7 @@ end --- Is methods ---- Returns if the unit is of an air category. --- If the unit is a helicopter or a plane, then this method will return true, otherwise false. --- @param #UNIT self --- @return #boolean Air category evaluation result. -function UNIT:IsAir() - self:F2() - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitDescriptor = DCSUnit:getDesc() - self:T3( { UnitDescriptor.category, Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } ) - - local IsAirResult = ( UnitDescriptor.category == Unit.Category.AIRPLANE ) or ( UnitDescriptor.category == Unit.Category.HELICOPTER ) - - self:T3( IsAirResult ) - return IsAirResult - end - - return nil -end - ---- Returns if the unit is of an ground category. --- If the unit is a ground vehicle or infantry, this method will return true, otherwise false. --- @param #UNIT self --- @return #boolean Ground category evaluation result. -function UNIT:IsGround() - self:F2() - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - local UnitDescriptor = DCSUnit:getDesc() - self:T3( { UnitDescriptor.category, Unit.Category.GROUND_UNIT } ) - - local IsGroundResult = ( UnitDescriptor.category == Unit.Category.GROUND_UNIT ) - - self:T3( IsGroundResult ) - return IsGroundResult - end - - return nil -end --- Returns if the unit is a friendly unit. -- @param #UNIT self From b0ac01f25a079d2e1f17f240b5e5f7611f73377f Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 23 Aug 2018 00:31:40 +0200 Subject: [PATCH 275/420] Warehouse v0.2.5 --- .../Moose/Functional/Warehouse.lua | 188 +++++++++++++++--- 1 file changed, 155 insertions(+), 33 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index e1880c243..0c6e24136 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -301,7 +301,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.2.4w" +WAREHOUSE.version="0.2.5" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -382,26 +382,28 @@ function WAREHOUSE:New(warehouse, alias) self:SetStartState("Stopped") -- Add FSM transitions. - self:AddTransition("Stopped", "Load", "Stopped") -- TODO Load the warehouse state. No sure if it should be in stopped state. - self:AddTransition("Stopped", "Start", "Running") -- Start the warehouse. - self:AddTransition("*", "Status", "*") -- Status update. - self:AddTransition("*", "AddAsset", "*") -- Add asset to warehouse stock. - self:AddTransition("*", "AddRequest", "*") -- New request from other warehouse. - self:AddTransition("Running", "Request", "*") -- Process a request. Only in running mode. - self:AddTransition("Attacked", "Request", "*") -- Process a request. Only in running mode. - self:AddTransition("*", "Unloaded", "*") -- Cargo has been unloaded from the carrier. - self:AddTransition("*", "Arrived", "*") -- Cargo group has arrived at destination. - self:AddTransition("*", "Delivered", "*") -- All cargo groups of a request have been delivered to the requesting warehouse. - self:AddTransition("Running", "SelfRequest", "*") -- Request to warehouse itself. Requested assets are only spawned but not delivered anywhere. - self:AddTransition("Attacked", "SelfRequest", "*") -- Request to warehouse itself. Also possible when warehouse is under attack! - self:AddTransition("Running", "Pause", "Paused") -- TODO Pause the processing of new requests. Still possible to add assets and requests. - self:AddTransition("Paused", "Unpause", "Running") -- TODO Unpause the warehouse. Queued requests are processed again. - self:AddTransition("*", "Stop", "Stopped") -- TODO Stop the warehouse. - self:AddTransition("*", "Save", "*") -- TODO Save the warehouse state to disk. - self:AddTransition("*", "Attacked", "Attacked") -- TODO Warehouse is under attack by enemy coalition. - self:AddTransition("Attacked", "Defeated", "Running") -- TODO Attack by other coalition was defeated! - self:AddTransition("Attacked", "Captured", "Running") -- TODO Warehouse was captured by another coalition. It must have been attacked first. - self:AddTransition("*", "Destroyed", "*") -- TODO Warehouse was destoryed. All assets in stock are gone and warehouse is stopped. + self:AddTransition("Stopped", "Load", "Stopped") -- TODO Load the warehouse state. No sure if it should be in stopped state. + self:AddTransition("Stopped", "Start", "Running") -- Start the warehouse. + self:AddTransition("*", "Status", "*") -- Status update. + self:AddTransition("*", "AddAsset", "*") -- Add asset to warehouse stock. + self:AddTransition("*", "AddRequest", "*") -- New request from other warehouse. + self:AddTransition("Running", "Request", "*") -- Process a request. Only in running mode. + self:AddTransition("Attacked", "Request", "*") -- Process a request. Only in running mode. + self:AddTransition("*", "Unloaded", "*") -- Cargo has been unloaded from the carrier. + self:AddTransition("*", "Arrived", "*") -- Cargo group has arrived at destination. + self:AddTransition("*", "Delivered", "*") -- All cargo groups of a request have been delivered to the requesting warehouse. + self:AddTransition("Running", "SelfRequest", "*") -- Request to warehouse itself. Requested assets are only spawned but not delivered anywhere. + self:AddTransition("Attacked", "SelfRequest", "*") -- Request to warehouse itself. Also possible when warehouse is under attack! + self:AddTransition("Running", "Pause", "Paused") -- TODO Pause the processing of new requests. Still possible to add assets and requests. + self:AddTransition("Paused", "Unpause", "Running") -- TODO Unpause the warehouse. Queued requests are processed again. + self:AddTransition("*", "Stop", "Stopped") -- TODO Stop the warehouse. + self:AddTransition("*", "Save", "*") -- TODO Save the warehouse state to disk. + self:AddTransition("*", "Attacked", "Attacked") -- TODO Warehouse is under attack by enemy coalition. + self:AddTransition("Attacked", "Defeated", "Running") -- TODO Attack by other coalition was defeated! + self:AddTransition("Attacked", "Captured", "Running") -- TODO Warehouse was captured by another coalition. It must have been attacked first. + self:AddTransition("*", "AirbaseCaptured", "*") -- TODO Airbase was captured by other coalition. + self:AddTransition("*", "AirbaseRecaptured", "*") -- TODO Airbase was re-captured from other coalition. + self:AddTransition("*", "Destroyed", "*") -- TODO Warehouse was destoryed. All assets in stock are gone and warehouse is stopped. -- Pseudo Functions @@ -588,6 +590,30 @@ function WAREHOUSE:New(warehouse, alias) -- @param DCS#country.id Country which has captured the warehouse. + --- Triggers the FSM event "AirbaseCaptured" when the airbase of the warehouse has been captured by another coalition. + -- @param #WAREHOUSE self + -- @function [parent=#WAREHOUSE] AirbaseCaptured + -- @param DCS#coalition.side Coalition which captured the airbase. + + --- Triggers the FSM event "AirbaseCaptured" with a delay when the airbase of the warehouse has been captured by another coalition. + -- @param #WAREHOUSE self + -- @function [parent=#WAREHOUSE] __AirbaseCaptured + -- @param #number delay Delay in seconds. + -- @param DCS#coalition.side Coalition which captured the airbase. + + + --- Triggers the FSM event "AirbaseRecaptured" when the airbase of the warehouse has been re-captured from the other coalition. + -- @param #WAREHOUSE self + -- @function [parent=#WAREHOUSE] AirbaseRecaptured + -- @param DCS#coalition.side Coalition which re-captured the airbase. + + --- Triggers the FSM event "AirbaseRecaptured" with a delay when the airbase of the warehouse has been re-captured from the other coalition. + -- @param #WAREHOUSE self + -- @function [parent=#WAREHOUSE] __AirbaseRecaptured + -- @param #number delay Delay in seconds. + -- @param DCS#coalition.side Coalition which re-captured the airbase. + + --- Triggers the FSM event "Destroyed" when the warehouse was destroyed. All services are stopped. -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] Destroyed @@ -864,8 +890,7 @@ end -- @param #string Event Event. -- @param #string To To state. function WAREHOUSE:onafterStatus(From, Event, To) - self:E(self.wid..string.format("Checking status of warehouse %s. Current FSM state %s. Global warehouse asssets = %d.", self.alias, self:GetState(), #WAREHOUSE.db.Assets)) - --env.info(string.format("FF number of global assets = %d, current asset id = %d", #WAREHOUSE.db.Assets, WAREHOUSE.db.AssetID)) + self:E(self.wid..string.format("Checking status of warehouse %s. Current FSM state %s. Global warehouse assets = %d.", self.alias, self:GetState(), #WAREHOUSE.db.Assets)) -- Print status. self:_DisplayStatus() @@ -1390,6 +1415,13 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request) -- Filter the requested assets. local _assets,_nasset,_enough=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval, Request.nasset) + if Request.nasset==0 then + local text=string.format("Request denied! Zero assets were requested.") + MESSAGE:New(text, 10):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:E(self.wid..text) + return false + end + -- Check if destination is in range for all requested assets. for _,_asset in pairs(_assets) do local asset=_asset --#WAREHOUSE.Assetitem @@ -1780,10 +1812,13 @@ end -- @return Core.Set#SET_GROUP Set of groups that were spawned. -- @return #table List of spawned assets. function WAREHOUSE:_SpawnAssetRequest(Request) + self:E({requestUID=Request.uid}) -- Filter the requested cargo assets. local _assetstock,_nasset,_enough=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval, Request.nasset) + self:E({num_assetstoc=#_assetstock, nasset=_nasset, enough=_enough}) + -- No assets in stock :( if not _enough then return nil,nil,nil @@ -2007,17 +2042,22 @@ function WAREHOUSE:onafterDelivered(From, Event, To, request) -- Debug info self:E(self.wid..string.format("All assets from warehouse %s delivered to warehouse %s!", self.alias, request.warehouse.alias)) + self:_Fireworks(request.warehouse.coordinate) + + --[[ -- Fireworks! for i=1,91 do local color=math.random(0,3) request.warehouse.coordinate:Flare(color, i-1) end + ]] -- Remove pending request: self:_DeleteQueueItem(request, self.pending) end + --- On after "SelfRequest" event. Request was initiated to the warehouse itself. Groups are just spawned at the warehouse or the associated airbase. -- If the warehouse is currently under attack when the self request is made, the self request is added to the defending table. One the attack is defeated, -- this request is used to put the groups back into the warehouse stock. @@ -2114,13 +2154,62 @@ function WAREHOUSE:onafterCaptured(From, Event, To, Coalition, Country) -- Respawn warehouse with new coalition/country. self.warehouse:ReSpawn(Country) + + -- Set new country and coalition self.coalition=Coalition self.country=Country - self.airbase=nil - self.category=-1 + + -- Delete all waiting requests because they are not valid any more + self.queue=nil + self.queue={} + + --TODO: What about pending items? Is there any problem due to the coalition change? + --TODO: Maybe if the receiving warehouse gets captured! Oh, oh :( + -- What to do? send the items back? Impossible. + + -- Airbase could have been captured before and already belongs to the new coalition. + local airbase=AIRBASE:FindByName(self.airbasename) + local airbasecoaltion=airbase:GetCoalition() + + if self.coalition==airbasecoaltion then + -- Airbase already owned by the coalition that captured the warehouse. Airbase can be used by this warehouse. + self.airbase=airbase + self.category=airbase:GetDesc().category + else + -- Airbase is owned by other coalition. So this warehouse does not have an airbase unil it is captured. + self.airbase=nil + self.category=-1 + end end +--- On after "AirbaseCaptured" event. Airbase of warehouse has been captured by another coalition. +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param DCS#coalition.side Coalition which captured the warehouse. +function WAREHOUSE:onafterAirbaseCaptured(From, Event, To, Coalition) + self:E(self.wid..string.format("Our airbase %s was captured by coalition %d!", self.airbasename, Coalition)) + self.airbase:GetCoordinate():SmokeRed() + self.airbase=nil + self.category=-1 -- -1 indicates no airbase. +end + +--- On after "AirbaseRecaptured" event. Airbase of warehouse has been re-captured from other coalition. +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param DCS#coalition.side Coalition which captured the warehouse. +function WAREHOUSE:onafterAirbaseRecaptured(From, Event, To, Coalition) + self:E(self.wid..string.format("We re-capturd our airbase %s from coalition %d!", self.airbasename, Coalition)) + self.airbase=AIRBASE:FindByName(self.airbasename) + self.category=self.airbase:GetDesc().category + self.airbase:GetCoordinate():SmokeGreen() +end + + --- On after "Destroyed" event. Warehouse was destroyed. All services are stopped. -- @param #WAREHOUSE self -- @param #string From From state. @@ -2158,11 +2247,11 @@ function WAREHOUSE:_RouteGround(group, request) -- First waypoint = current position of the group. local FromWP=group:GetCoordinate():WaypointGround(_speed, "Off Road") - table.insert(Waypoints, FromWP, 1) + table.insert(Waypoints, 1, FromWP) -- Final coordinate. local ToWP=request.warehouse.spawnzone:GetRandomCoordinate():WaypointGround(_speed, "Off Road") - table.insert(Waypoints, ToWP, #Waypoints) + table.insert(Waypoints, #Waypoints+1, ToWP) -- Task function triggering the arrived event. local TaskFunction = group:TaskFunction("WAREHOUSE._Arrived", self) @@ -2441,13 +2530,15 @@ end -- @param #WAREHOUSE self -- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventBaseCaptured(EventData) - self:E(self.wid..string.format("Warehouse %s captured event base captured!",self.alias)) -- This warehouse does not have an airbase and never had one. So i could not be captured. if self.airbasename==nil then + -- This warehouse never had an airbase so I cannot have been captured. return end + self:E(self.wid..string.format("Warehouse %s captured event base captured!",self.alias)) + if EventData and EventData.Place then -- Place is the airbase that was captured. @@ -2456,6 +2547,8 @@ function WAREHOUSE:_OnEventBaseCaptured(EventData) if EventData.PlaceName==self.airbasename then -- Okay, this airbase belongs or did belong to this warehouse. + self:E(self.wid..string.format("Airbase of warehouse %s was captured! ",self.alias)) + -- New coalition of airbase after it was captured. local coalitionAirbase=airbase:GetCoalition() @@ -2466,13 +2559,14 @@ function WAREHOUSE:_OnEventBaseCaptured(EventData) -- Warehouse lost this airbase previously and not it was re-captured. env.info("FF airbase of warehouse is nil") if coalitionAirbase == self.coalition then - self.airbase=airbase - env.info("FF air") + self:AirbaseRecaptured(coalitionAirbase) + --self.airbase=airbase end else -- Captured airbase belongs to this warehouse but was captured by other coaltion. if coalitionAirbase ~= self.coalition then - self.airbase=nil + self:AirbaseCaptured(coalitionAirbase) + --self.airbase=nil end end @@ -2628,6 +2722,7 @@ end -- @param #table queue The queue which is holding the requests to check. -- @return #boolean If true, request can be executed. If false, something is not right. function WAREHOUSE:_CheckRequestConsistancy(queue) + env.info("FF checking request consistancy!") -- Requests to delete. local invalid={} @@ -2642,6 +2737,12 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) -- if warehouse or requestor is a FARP, plane asset and transport not possible. -- if requestor or warehouse is a SHIP, APC transport not possible, SELFPROPELLED only for AIR/SHIP -- etc. etc... + + -- Check if at least one asset was requested. + if request.nasset==0 then + self:E(self.wid..string.format("ERROR: Incorrect request. Request for zero assets not possible. Can happen when, e.g. \"all\" ground assets are requests but none in stock.")) + valid=false + end -- Request from enemy coalition? if self.coalition~=request.warehouse.coalition then @@ -2858,7 +2959,7 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) end -- Add request as unvalid and delete it later. - if not valid then + if not valid then table.insert(invalid, request) end @@ -2867,8 +2968,9 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) -- Delete invalid requests. for _,_request in pairs(invalid) do + self:E(self.wid..string.format("Deleting invalid request %d",_request.uid)) self:_DeleteQueueItem(_request, self.queue) - end + end end @@ -3316,6 +3418,11 @@ function WAREHOUSE:_FilterStock(stock, item, value, nmax) end end + -- Treat case where ntot=0, i.e. no assets at all. + if ntot==0 then + return filtered, ntot, false + end + -- Handle string input for nmax. if type(nmax)=="string" then if nmax:lower()=="all" then @@ -3631,6 +3738,21 @@ function WAREHOUSE:_DisplayStockItems(stock) MESSAGE:New(text, 10):ToAll() end +--- Fireworks! +-- @param #WAREHOUSE self +-- @param Core.Point#COORDINATE coord +function WAREHOUSE:_Fireworks(coord) + + -- Place. + coord=coord or self.coordinate + + -- Fireworks! + for i=1,91 do + local color=math.random(0,3) + coord:Flare(color, i-1) + end +end + --- Make a flight plan from a departure to a destination airport. -- @param #WAREHOUSE self -- @param #WAREHOUSE.Assetitem asset From 7148fe0c128bc9c49ff075a423de390c6149a0de Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 23 Aug 2018 16:30:38 +0200 Subject: [PATCH 276/420] Warehouse v0.25w --- .../Moose/Functional/Warehouse.lua | 117 ++++++------------ 1 file changed, 37 insertions(+), 80 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 0c6e24136..d13aaf6e5 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -301,33 +301,35 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.2.5" +WAREHOUSE.version="0.2.5w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Add event handlers. +-- TODO: Set ROE for spawned groups. +-- TODO: Add possibility to add active groups. Need to create a pseudo template before destroy. +-- DONE: If warehouse is destoyed, all asssets are gone. +-- TODO: Write documentation. +-- TODO: Handle the case when units of a group die during the transfer. Adjust template?! See Grouping in SPAWN. +-- TODO: Handle cases with immobile units. +-- TODO: Handle cargo crates. +-- TODO: Handle cases for aircraft carriers and other ships. Place warehouse on carrier possible? On others probably not - exclude them? +-- TODO: Add general message function for sending to coaliton or debug. +-- TODO: Fine tune event handlers. +-- DONE: Add event handlers. -- DONE: Add AI_CARGO_AIRPLANE -- DONE: Add AI_CARGO_APC -- DONE: Add AI_CARGO_HELICOPTER -- DONE: Switch to AI_CARGO_XXX_DISPATCHER -- DONE: Add queue. --- TODO: Write documentation. -- DONE: Put active groups into the warehouse, e.g. when they were transported to this warehouse. -- NOGO: Spawn warehouse assets as uncontrolled or AI off and activate them when requested. --- TODO: Handle cases with immobile units. -- DONE: How to handle multiple units in a transport group? <== Cargo dispatchers. -- DONE: Add phyical object. --- TODO: If warehouse is destoyed, all asssets are gone. --- TODO: If warehosue is captured, change warehouse and assets to other coalition. --- TODO: Handle cases for aircraft carriers and other ships. Place warehouse on carrier possible? On others probably not - exclude them? --- TODO: Handle cargo crates. --- TODO: Add general message function for sending to coaliton or debug. +-- DONE: If warehosue is captured, change warehouse and assets to other coalition. -- NOGO: Use RAT for routing air units. Should be possible but might need some modifications of RAT, e.g. explit spawn place. But flight plan should be better. --- TODO: Can I make a request with specific assets? E.g., once delivered, make a request for exactly those assests that were in the original request. --- TODO: Handle the case when units of a group die during the transfer. Adjust template?! See Grouping in SPAWN. --- TODO: Set ROE for spawned groups. +-- DONE: Can I make a request with specific assets? E.g., once delivered, make a request for exactly those assests that were in the original request. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Constructor(s) @@ -2262,6 +2264,10 @@ function WAREHOUSE:_RouteGround(group, request) -- Route group to destination. group:Route(Waypoints, 1) + + -- Set ROE and alaram state. + group:OptionROEReturnFire() + group:OptionAlarmStateGreen() end end @@ -2269,73 +2275,18 @@ end --- Route the airplane from one airbase another. -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP Aircraft Airplane group to be routed. --- @param Wrapper.Airbase#AIRBASE ToAirbase Destination airbase. --- @param #number Speed Speed in km/h. Default is 80% of max possible speed the group can do. -function WAREHOUSE:_RouteAir(Aircraft, ToAirbase, Speed) +function WAREHOUSE:_RouteAir(aircraft) - if Aircraft and Aircraft:IsAlive()~=nil then + if aircraft and aircraft:IsAlive()~=nil then -- Start command. - if true then - local StartCommand = {id = 'Start', params = {}} - Aircraft:SetCommand(StartCommand) - return - end + local StartCommand = {id = 'Start', params = {}} + aircraft:SetCommand(StartCommand) - -- Set takeoff type. - local Takeoff = SPAWN.Takeoff.Cold - - -- Get template of group. - local Template = Aircraft:GetTemplate() - - -- Nil check - if Template==nil then - self:E(self.wid.."ERROR: Template nil in RouteAir!") - return - end - - local Waypoints,Coordinates=self:_GetFlightplan(Aircraft,self.airbase,ToAirbase) - - --[[ - -- Waypoints of the route. - local Points={} - - -- To point. - local AirbasePointVec2 = ToAirbase:GetPointVec2() - local ToWaypoint = AirbasePointVec2:WaypointAir( - POINT_VEC3.RoutePointAltType.BARO, - "Land", - "Landing", - Speed or Aircraft:GetSpeedMax()*0.8 - ) - ToWaypoint["airdromeId"] = ToAirbase:GetID() - ToWaypoint["speed_locked"] = true - - -- Aibase id and category. - local AirbaseID = ToAirbase:GetID() - local AirbaseCategory = ToAirbase:GetDesc().category - - if AirbaseCategory == Airbase.Category.SHIP or AirbaseCategory == Airbase.Category.HELIPAD then - ToWaypoint.linkUnit = AirbaseID - ToWaypoint.helipadId = AirbaseID - ToWaypoint.airdromeId = nil - elseif AirbaseCategory == Airbase.Category.AIRDROME then - ToWaypoint.airdromeId = AirbaseID - ToWaypoint.helipadId = nil - ToWaypoint.linkUnit = nil - end + -- Set ROE and alaram state. + aircraft:OptionROEReturnFire() + aircraft:OptionROTPassiveDefense() - -- Second point of the route. First point is done in RespawnAtCurrentAirbase() routine. - Template.route.points[2] = ToWaypoint - ]] - - -- Set waypoints. - Template.route.points=Waypoints - - -- Respawn group at the current airbase. - env.info("FF Respawn at current airbase group = "..Aircraft:GetName().." name before") - local newAC=Aircraft:RespawnAtCurrentAirbase(Template, Takeoff, false) - env.info("FF Respawn at current airbase group = "..newAC:GetName().." name after") else self:E(string.format("ERROR: aircraft %s cannot be routed since it does not exist or is not alive %s!", tostring(Aircraft:GetName()), tostring(Aircraft:IsAlive()))) @@ -2432,6 +2383,7 @@ function WAREHOUSE:_OnEventArrived(EventData) local nunits=#group:GetUnits() local dt=10*(nunits-1)+1 -- one unit = 1 sec, two units = 11 sec, three units = 21 sec before we call the group arrived. self:__Arrived(dt, group) + else self:E(string.format("Group that arrived did not belong to a warehouse. Warehouse ID=%s, Asset ID=%s, Request ID=%s.", tostring(wid), tostring(aid), tostring(rid))) end @@ -2504,7 +2456,15 @@ end -- @param #WAREHOUSE self -- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventEngineShutdown(EventData) - self:E(self.wid..string.format("Warehouse %s captured event engine shutdown!",self.alias)) + self:T3(self.wid..string.format("Warehouse %s captured event engine shutdown!", self.alias)) + + if EventData and EventData.IniGroup then + local group=EventData.IniGroup + local wid,aid,rid=self:_GetIDsFromGroup(group) + if wid==self.uid then + self:E(self.wid..string.format("Warehouse %s captured event engine shutdown of its asset unit %s.", self.alias, EventData.IniUnitName)) + end + end end --- Warehouse event handling function. @@ -2547,7 +2507,7 @@ function WAREHOUSE:_OnEventBaseCaptured(EventData) if EventData.PlaceName==self.airbasename then -- Okay, this airbase belongs or did belong to this warehouse. - self:E(self.wid..string.format("Airbase of warehouse %s was captured! ",self.alias)) + self:I(self.wid..string.format("Airbase of warehouse %s was captured! ",self.alias)) -- New coalition of airbase after it was captured. local coalitionAirbase=airbase:GetCoalition() @@ -2557,16 +2517,13 @@ function WAREHOUSE:_OnEventBaseCaptured(EventData) -- Warehouse is blue, airbase is blue self.airbase is nil and blue (re-)captures it ==> self.airbase=Event.Place if self.airbase==nil then -- Warehouse lost this airbase previously and not it was re-captured. - env.info("FF airbase of warehouse is nil") if coalitionAirbase == self.coalition then self:AirbaseRecaptured(coalitionAirbase) - --self.airbase=airbase end else -- Captured airbase belongs to this warehouse but was captured by other coaltion. if coalitionAirbase ~= self.coalition then self:AirbaseCaptured(coalitionAirbase) - --self.airbase=nil end end @@ -2607,7 +2564,7 @@ function WAREHOUSE:_CheckConquered() local distance=coord:Get2DDistance(unit:GetCoordinate()) -- Filter only alive groud units. Also check distance again, because the scan routine might give some larger distances. - if unit:IsGround() and unit:IsAlive() and distance<= radius then + if unit:IsGround() and unit:IsAlive() and distance <= radius then -- Get coalition and country. local _coalition=unit:GetCoalition() From 743b59546582c87fc9218fcac24cd806f2bb7972 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Fri, 24 Aug 2018 11:09:51 +0200 Subject: [PATCH 277/420] Warehouse v0.2.6w --- .../Moose/Functional/Warehouse.lua | 81 ++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index d13aaf6e5..0db133ebd 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -132,6 +132,8 @@ -- -- Also not that the above request is for five infantry units. So any group in stock that has the generalized attribute "INFANTRY" can be selected. -- +-- ### Requesting a Specific Unit Type +-- -- A more specific request could look like: -- -- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.UNITTYPE, "A-10C", 2) @@ -139,17 +141,85 @@ -- Here, Kobuleti requests a specific unit type, in particular two groups of A-10Cs. Note that the spelling is important as it must exacly be the same as -- what one get's when using the DCS unit type. -- +-- ### Requesting a Specifc Group +-- -- An even more specific request would be: -- -- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.TEMPLATENAME, "Group Name as in ME", 3) -- -- In this case three groups named "Group Name as in ME" are requested. So this explicitly request the groups named like that in the Mission Editor. -- +-- ### Requesting a general category +-- -- On the other hand, very general unspecifc requests can be made as -- -- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.CATEGORY, Group.Category.Ground, 10) -- -- Here, Kubuleti requests 10 ground groups and does not care which ones. This could be a mix of infantry, APCs, trucks etc. +-- +-- # Employing Assets +-- +-- Assets in the warehouse' stock can used for user defined tasks realtively easily. They can be spawned into the game by a "self request", i.e. the warehouse +-- requests the assets from itself: +-- +-- warehouseBatumi:AddRequest(warehouseBatumi, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.INFANTRY, 5) +-- +-- This would simply spawn five infantry groups in the spawn zone of the Batumi warehouse if/when they are available. +-- +-- ## Accessing the Assets +-- +-- If a warehouse requests assets from itself, it triggers the event **SelfReqeuest**. The mission designer can capture this event with the associated +-- @{#WAREHOUSE.OnAfterSelfRequest}(*From*, *Event*, *To*, *groupset*, *request*) function. +-- +-- --- OnAfterSelfRequest user function. Access groups spawned from the warehouse for further tasking. +-- -- @param #WAREHOUSE self +-- -- @param #string From From state. +-- -- @param #string Event Event. +-- -- @param #string To To state. +-- -- @param Core.Set#SET_GROUP groupset The set of cargo groups that was delivered to the warehouse itself. +-- -- @param #WAREHOUSE.Pendingitem request Pending self request. +-- function WAREHOUSE:onafterSelfRequest(From, Event, To, groupset, request) +-- +-- for _,_group in pairs(groupset:GetSetObjects()) do +-- local group=_group --Wrapper.Group#GROUP +-- group:SmokeGreen() +-- end +-- +-- end +-- +-- The variable *groupset* is a @{Core.Set#SET_GOUP} object and holds all asset groups from the request. The code above shows, how the mission designer can access the groups +-- for further tasking. Here, the groups are only smoked but, of course, you can use them for whatever task you imagine. +-- +-- Note that airborn groups are spawned in uncontrolled state and need to be activated first before they can start their assigned mission. +-- +-- # Strategic Tasks +-- +-- Due to the fact that a warehouse holds (or can hold) a lot of valuable assets, it makes a jucy task for enemy attacks. +-- There are several interesting situations, which can occurr. +-- +-- ## Capturing a Warehouse' Airbase +-- +-- If a warehouse has an associated airbase, it can be captured by the enemy. In this case, the warehouse looses it ability so employ all airborn assets and is also cut-off +-- from supply by airborn units. +-- +-- Technically, the capturing of the airbase is triggered by the DCS S_EVENT_CAPTURE_BASE event. So the capturing takes place when only enemy ground units are in the +-- airbase zone whilst no ground units of the present airbase owner are in that zone. +-- +-- The warehouse will also create an event named "AirbaseCaptured", which can be captured by the @{#WAREHOUSE.OnAfterAirbaseCaptured} function. So the warehouse can react on +-- this attack and for example spawn ground groups to re-capture its airbase. +-- +-- When an airbase is re-captured the event "AirbaseRecaptured" is triggered and can be captured by the @{#WAREHOUSE.OnAfterAirbaseRecaptured} function. +-- This can be used to put the defending assets back into the warehouse stock. +-- +-- ## Capturing the Warehouse +-- +-- A warehouse can also be captured by the enemy coaltion. If enemy groups enter the warehouse zone the event "Attacked" is triggered which can be captured by the +-- @{#WAREHOUSE.OnAfterAttacked} event. +-- +-- If a warehouse is attacked it will spawn all its ground assets in the spawn zone which can than be used to defend the warehouse zone. +-- +-- If only ground troops of the enemy coalition are present in the warehouse zone, the warehouse and all its assets falls into the hands of the enemy. +-- This event triggered in this case is -- -- === -- @@ -301,7 +371,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.2.5w" +WAREHOUSE.version="0.2.6w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -549,6 +619,15 @@ function WAREHOUSE:New(warehouse, alias) -- @param Core.Set#SET_GROUP groupset The set of cargo groups that was delivered to the warehouse itself. -- @param #WAREHOUSE.Pendingitem request Pending self request. + --- On after "SelfRequest" event. Request was initiated to the warehouse itself. Groups are just spawned at the warehouse or the associated airbase. + -- @function [parent=#WAREHOUSE] OnAfterSelfRequest + -- @param #WAREHOUSE self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Core.Set#SET_GROUP groupset The set of (cargo) groups that was delivered to the warehouse itself. + -- @param #WAREHOUSE.Pendingitem request Pending self request. + --- Triggers the FSM event "Attacked" when a warehouse is under attack by an another coalition. -- @param #WAREHOUSE self From 76f34e448c820a816382a33ff83033b7ebff8ac7 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Fri, 24 Aug 2018 11:59:59 +0200 Subject: [PATCH 278/420] Warehouse v0.2.6w --- .../Moose/Functional/Warehouse.lua | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 0db133ebd..2bcf70e56 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -194,7 +194,7 @@ -- -- # Strategic Tasks -- --- Due to the fact that a warehouse holds (or can hold) a lot of valuable assets, it makes a jucy task for enemy attacks. +-- Due to the fact that a warehouse holds (or can hold) a lot of valuable assets, it makes a juicy task for enemy attacks. -- There are several interesting situations, which can occurr. -- -- ## Capturing a Warehouse' Airbase @@ -213,13 +213,24 @@ -- -- ## Capturing the Warehouse -- --- A warehouse can also be captured by the enemy coaltion. If enemy groups enter the warehouse zone the event "Attacked" is triggered which can be captured by the +-- A warehouse can also be captured by the enemy coaltion. If enemy groups enter the warehouse zone the event **Attacked** is triggered which can be captured by the -- @{#WAREHOUSE.OnAfterAttacked} event. -- -- If a warehouse is attacked it will spawn all its ground assets in the spawn zone which can than be used to defend the warehouse zone. -- -- If only ground troops of the enemy coalition are present in the warehouse zone, the warehouse and all its assets falls into the hands of the enemy. --- This event triggered in this case is +-- In this case the event **Captured** is triggered which can be captured by the @{#WAREHOUSE.OnAfterCaptured} function. +-- +-- The warehouse turn to the capturing coalition, i.e. its physical representation, and all assets as well. In paticular, all requests to the warehouse will +-- spawn assets beloning to the new owner. +-- +-- ## Destroying a Warehouse +-- +-- If an enemy destroy the physical warehouse structure, the warehouse will of course stop all its services. In priciple, all assets contained in the warehouse are +-- gone as well. So a warehouse should be properly defended. +-- +-- Upon destruction of the warehouse, the event **Destroyed** is triggered, which can be captured by the @{#WAREHOUSE.OnAfterDestroyed} function. +-- So the mission designer can invene at this point and for example choose to spawn all or paricular types of assets before the warehouse is gone for good. -- -- === -- From 5b7852ef6cfbc48b69018bed2bac601c31859310 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Fri, 24 Aug 2018 21:19:53 +0200 Subject: [PATCH 279/420] Warehouse v0.2.5 --- Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua | 2 ++ Moose Development/Moose/AI/AI_Cargo_Helicopter.lua | 6 +++++- Moose Development/Moose/Functional/Warehouse.lua | 11 ++++++++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 75ae757bb..5bda17ffe 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -368,6 +368,8 @@ end -- @param #AI_CARGO_DISPATCHER self function AI_CARGO_DISPATCHER:onafterMonitor() + env.info("FF number of cargo set = "..self.SetCargo:Count()) + for CarrierGroupName, Carrier in pairs( self.SetCarrier:GetSet() ) do local Carrier = Carrier -- Wrapper.Group#GROUP local AI_Cargo = self.AI_Cargo[Carrier] diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index dec71fd3e..540d4b064 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -642,7 +642,11 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin if Helicopter and Helicopter:IsAlive() ~= nil then - Helicopter:Activate() + --Helicopter:Activate() + + env.info("FF route pickup") + + Coordinate:MarkToAll("helo pickupcoord") self.RoutePickup = true Coordinate.y = math.random( 50, 500 ) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index d13aaf6e5..9dd2a6be9 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -1628,6 +1628,9 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Dependent on transport type, spawn the transports and set up the dispatchers. if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then + ---------------- + --- AIRPLANE --- + ---------------- -- Spawn the transport groups. for i=1,Request.ntransport do @@ -1662,6 +1665,9 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) CargoTransport = AI_CARGO_DISPATCHER_AIRPLANE:New(TransportSet, CargoGroups, PickupAirbaseSet, DeployAirbaseSet) elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then + ------------------ + --- HELICOPTER --- + ------------------ -- Spawn the transport groups. for i=1,Request.ntransport do @@ -1702,6 +1708,9 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) --CargoTransport:SetHomeZone(self.spawnzone) elseif Request.transporttype==WAREHOUSE.TransportType.APC then + ----------- + --- APC --- + ----------- -- Spawn the transport groups. for i=1,Request.ntransport do @@ -1733,7 +1742,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) end -- Define dispatcher for this task. - CargoTransport = AI_CARGO_DISPATCHER_APC:NewWithZones(TransportSet, CargoGroups, DeployZoneSet, 0) + CargoTransport = AI_CARGO_DISPATCHER_APC:New(TransportSet, CargoGroups, DeployZoneSet, 0) -- Set home zone. CargoTransport:SetHomeZone(self.spawnzone) From d9a5618773f97ecf052421f732f8c54ec1a13b69 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sat, 25 Aug 2018 09:20:50 +0200 Subject: [PATCH 280/420] Fix crash in AI_CARGO_AIRPLANE --- Moose Development/Moose/AI/AI_Cargo_Airplane.lua | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 1d5d1e398..219cdd8bf 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -128,8 +128,10 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) -- Set carrier. self:SetCarrier( Airplane ) - Airplane:SetCargoBayWeightLimit() - + for _, AirplaneUnit in pairs( Airplane:GetUnits() ) do + AirplaneUnit:SetCargoBayWeightLimit() + end + self.Relocating = true return self From 185c27013d4946ece0d9bc722d9ee7fe4a33bc42 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sun, 26 Aug 2018 08:45:36 +0200 Subject: [PATCH 281/420] Fix for clients which weren't initialized with a cargo bay weight limit value. Each time a cargo bay weight limit is enquired through the method GetCargoBayFeeWeight(), if there isn't a cargo bay weight limit set, the default cargo bay weight limit will be calculated, so that the logic will always be executed. --- Moose Development/Moose/Core/Set.lua | 51 +++++++++++++++++-- .../Moose/Tasking/Task_CARGO.lua | 7 --- .../Moose/Wrapper/Positionable.lua | 7 +++ 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 12a611250..2b9c1c8b5 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -810,12 +810,18 @@ function SET_GROUP:GetAliveSet() end --- Add a GROUP to SET_GROUP. +-- Note that for each unit in the group that is set, a default cargo bay limit is initialized. -- @param Core.Set#SET_GROUP self -- @param Wrapper.Group#GROUP group The group which should be added to the set. -- @return self function SET_GROUP:AddGroup( group ) self:Add( group:GetName(), group ) + + -- I set the default cargo bay weight limit each time a new group is added to the set. + for UnitID, UnitData in pairs( group:GetUnits() ) do + UnitData:SetCargoBayWeightLimit() + end return self end @@ -1425,6 +1431,24 @@ function SET_GROUP:IsIncludeObject( MooseGroup ) end +--- Iterate the SET_GROUP and set for each unit the default cargo bay weight limit. +-- Because within a group, the type of carriers can differ, each cargo bay weight limit is set on @{Wrapper.Unit} level. +-- @param #SET_GROUP self +-- @usage +-- -- Set the default cargo bay weight limits of the carrier units. +-- local MySetGroup = SET_GROUP:New() +-- MySetGroup:SetCargoBayWeightLimit() +function SET_GROUP:SetCargoBayWeightLimit() + local Set = self:GetSet() + for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP + for UnitName, UnitData in pairs( GroupData:GetUnits() ) do + --local UnitData = UnitData -- Wrapper.Unit#UNIT + UnitData:SetCargoBayWeightLimit() + end + end +end + + do -- SET_UNIT --- @type SET_UNIT @@ -1593,12 +1617,15 @@ do -- SET_UNIT --- Add UNIT(s) to SET_UNIT. -- @param #SET_UNIT self - -- @param #string AddUnit A single UNIT. + -- @param Wrapper.Unit#UNIT Unit A single UNIT. -- @return #SET_UNIT self - function SET_UNIT:AddUnit( AddUnit ) - self:F2( AddUnit:GetName() ) + function SET_UNIT:AddUnit( Unit ) + self:F2( Unit:GetName() ) - self:Add( AddUnit:GetName(), AddUnit ) + self:Add( Unit:GetName(), Unit ) + + -- Set the default cargo bay limit each time a new unit is added to the set. + Unit:SetCargoBayWeightLimit() return self end @@ -2420,6 +2447,22 @@ do -- SET_UNIT return TypeReport:Text( Delimiter ) end + + --- Iterate the SET_UNIT and set for each unit the default cargo bay weight limit. + -- @param #SET_UNIT self + -- @usage + -- -- Set the default cargo bay weight limits of the carrier units. + -- local MySetUnit = SET_UNIT:New() + -- MySetUnit:SetCargoBayWeightLimit() + function SET_UNIT:SetCargoBayWeightLimit() + local Set = self:GetSet() + for UnitID, UnitData in pairs( Set ) do -- For each UNIT in SET_UNIT + --local UnitData = UnitData -- Wrapper.Unit#UNIT + UnitData:SetCargoBayWeightLimit() + end + end + + end diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index ae4ebc7b8..9225e5532 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -589,13 +589,6 @@ do -- TASK_CARGO Fsm:AddTransition( "Rejected", "Reject", "Aborted" ) Fsm:AddTransition( "Failed", "Fail", "Failed" ) - for _, Group in pairs( SetGroup:GetSet() ) do - for __, Unit in pairs( Group:GetUnits() ) do - local Unit = Unit -- Wrapper.Unit#UNIT - Unit:SetCargoBayWeightLimit() - end - end - ---- @param #FSM_PROCESS self -- @param Wrapper.Unit#UNIT TaskUnit -- @param #TASK_CARGO Task diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index b0200fd24..08889ea78 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -946,6 +946,12 @@ do -- Cargo -- @param #POSITIONABLE self -- @return #number CargoBayFreeWeight function POSITIONABLE:GetCargoBayFreeWeight() + + -- When there is no cargo bay weight limit set, then calculate this for this positionable! + if not self.__.CargoBayWeightLimit then + self:SetCargoBayWeightLimit() + end + local CargoWeight = 0 for CargoName, Cargo in pairs( self.__.Cargo ) do CargoWeight = CargoWeight + Cargo:GetWeight() @@ -1007,6 +1013,7 @@ do -- Cargo self.__.CargoBayWeightLimit = CargoBayWeightLimit end end + self:F({CargoBayWeightLimit = self.__.CargoBayWeightLimit}) end end --- Cargo From dffd66940d55fa55bbcdf71942eb604bf8398b78 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sun, 26 Aug 2018 09:05:48 +0200 Subject: [PATCH 282/420] Added 3 meters to the bounding radius to ensure infantry gets properly boarded around the carrier unit. --- Moose Development/Moose/Wrapper/Positionable.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 08889ea78..ac10a4f90 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -279,7 +279,7 @@ function POSITIONABLE:GetBoundingRadius() local Z = Box.max.z - Box.min.z local CX = X / 2 local CZ = Z / 2 - return math.max( CX, CZ ) + return math.max( CX, CZ ) + 3 end BASE:E( { "Cannot GetBoundingRadius", Positionable = self, Alive = self:IsAlive() } ) From 64355fb772de98cc2e36a97e8ed0360f834c37a8 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sun, 26 Aug 2018 17:29:31 +0200 Subject: [PATCH 283/420] Warehouse v0.2.6 Several fixes including BoundZone. Warehouse ID is not number not string. Updated Documentation. Fixed bug that invalid requests are not recognized. --- .../Moose/Functional/Warehouse.lua | 499 +++++++++++------- Moose Development/Moose/Wrapper/Group.lua | 3 +- Moose Development/Moose/Wrapper/Object.lua | 3 +- 3 files changed, 310 insertions(+), 195 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 4f985cf6f..0be2f17a5 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -1,4 +1,4 @@ ---- **Functional** - (R2.4) - Manages assets of an airbase and transportation to other airbases upon request. +--- **Functional** - (R2.5) - Simulation of logistics. -- -- -- Features: @@ -9,6 +9,8 @@ -- * Different means of automatic transportation (planes, helicopters, APCs, selfpropelled). -- -- # QUICK START GUIDE +-- +-- **WIP** -- -- === -- @@ -23,7 +25,7 @@ -- @field #boolean Debug If true, send debug messages to all. -- @field #boolean Report If true, send status messages to coalition. -- @field Wrapper.Static#STATIC warehouse The phyical warehouse structure. --- @field DCS#coalition.side coalition Coalition ID the warehouse belongs to. +-- @field DCS#coalition.side coalition Coalition side the warehouse belongs to. -- @field DCS#country.id country Country ID the warehouse belongs to. -- @field #string alias Alias of the warehouse. Name its called when sending messages. -- @field Core.Zone#ZONE zone Zone around the warehouse. If this zone is captured, the warehouse and all its assets goes to the capturing coaliton. @@ -63,20 +65,50 @@ -- -- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_Main.JPG) -- --- # What is a warehouse? +-- # The Warehouse Concept +-- +-- The MOOSE warehouse adds a new logistic component to the DCS World. *Assets*, i.e. ground, airborne and naval units, can be transferred from one place +-- to another in a realistic and highly automatic fashion. In contrast to a "DCS warehouse" these assets have a physical representation in game. In particular, +-- this means they can be destroyed during the transport, add more life to the DCS world etc. +-- +-- Or, in other words, on a scale between 1 and 10, how important do you reckon it is to have the right assets at the right place in a conflict? +-- +-- The warehouse adresses exactly this "problem" by simulating it in a realistic way while at the same time adding more life and dynamics to the DCS World. +-- +-- This comes along with some additional interesting stategic aspects since capturing/defending and destroying/protecting an enemy or your +-- own warehous becomes of critical importance for the development of a conflict. +-- +-- In essence, creating an efficient network of warehouses is vital for the success of a battle or even the whole war. Likewise, of course, cutting off the enemy +-- of important supply lines by capturing or destroying warehouses or their associated infrastructure is equally important. +-- +-- ## What is a warehouse? -- A warehouse is an abstract object represented by a physical (static) building that can hold virtual assets in stock. -- It can but it must not be associated with a particular airbase. The associated airbase can be an airdrome, a Helipad/FARP or a ship. -- --- If another another warehouse requests assets, the corresponding troops are spawned at the warehouse and being transported to the requestor or go their --- by themselfs. Once arrived at the requesting warehouse, the assets go into the stock of the requestor and can be reactivated when necessary. --- +-- If another warehouse requests assets, the corresponding troops are spawned at the warehouse and being transported to the requestor or go their +-- by themselfs. Once arrived at the requesting warehouse, the assets go into the stock of the requestor and can be activated/deployed when necessary. +-- -- ## What assets can be stored? --- Any kind of ground or airborn asset can be stored. Ships not supported at the moment due to the fact that airbases are bound to airbases which are +-- Any kind of ground or airborne asset can be stored. Ships not supported at the moment due to the fact that airbases are bound to airbases which are -- normally not located near the sea. -- --- # Creating a new warehouse +-- ## What means of transportation are available? +-- Firstly, all mobile assets can be send from warehouse to another on their own. -- --- A MOOSE warehouse must be represented in game by a phyical static object. For example, the mission editor already has warehouse as static object available. +-- * Ground vehicles will use the road infrastructure +-- * Airborne units are get a flightplan from the airbase the sending warehouse to the airbase of the receiving warehouse. This already implies that for airborne +-- assets both warehouses need an airbase. If either one of the warehouses does not have an associated airbase, transportation of airborne assest is not possible. +-- * Naval units can be exchanged between warehouses which posses a port/habour. Also shipping lanes must be specified manually but the user since DCS does not provide these. +-- * Trains use the available railroad infrastructure and both warehouses must have a connection to the railroad. Unfortunately, however, trains are not yet implemented to +-- a reasonable degree in DCS at the moment and hence cannot be used yet. +-- +-- Furthermore, ground assets can be transferred between warehouses by transport units. These are APCs, helicopters and airplanes. The transportation process is modelled +-- in a realistic way by using the corresponding cargo dispatcher classes, i.e. @{AI.AI_Cargo_Dispatcher_APC#AI_DISPATCHER_APC}, +-- @{AI.AI_Cargo_Dispatcher_Helicopter#AI_DISPATCHER_HELICOPTER} and @{AI.AI_Cargo_Dispatcher_Airplane#AI_DISPATCHER_AIRPLANE}. +-- +-- # Creating a Warehouse +-- +-- A MOOSE warehouse must be represented in game by a phyical *static* object. For example, the mission editor already has warehouse as static object available. -- This would be a good first choice but any static object will do. -- -- The positioning of the warehouse static object is very important for a couple of reasons. Firtly, a warehouse needs a good infrastructure so that spawned assets @@ -93,7 +125,7 @@ -- -- # Adding Assets -- --- Assets can be added to the warehouse stock by using the @{#WAREHOUSE.AddAsset}(*group*, *ngroups*) function. The parameter *group* has to be a MOOSE @{Wrapper.Group#GROUP}. +-- Assets can be added to the warehouse stock by using the @{#WAREHOUSE.AddAsset}(*group*, *ngroups*, *forceattribute*) function. The parameter *group* has to be a MOOSE @{Wrapper.Group#GROUP}. -- The parameter *ngroups* specifies how many clones of this group are added to the stock. -- -- Note that the group should be a late activated template group, which was defined in the mission editor. @@ -103,7 +135,10 @@ -- -- This will add five infantry groups to the warehouse stock. -- --- Note that you can also add assets with a delay by using the @{#WAREHOUSE.__AddAsset}(*delay*, *group*, *ngroups*), where *delay* is the delay in seconds before the asset is added. +-- Note that you can also add assets with a delay by using the @{#WAREHOUSE.__AddAsset}(*delay*, *group*, *ngroups*, *foceattribute*), where *delay* is the delay in seconds before the asset is added. +-- +-- By default, the generalized attribute of the asset is determined automatically from the DCS descriptor attributes. However, this might not always result in the desired outcome. +-- Therefore, it is possible, to force a generalized attribute for the asset with the third optional parameter *forceattribute*, which is of type @{#WAREHOUSE.Attribute}. -- -- # Requesting Assets -- @@ -190,17 +225,17 @@ -- The variable *groupset* is a @{Core.Set#SET_GOUP} object and holds all asset groups from the request. The code above shows, how the mission designer can access the groups -- for further tasking. Here, the groups are only smoked but, of course, you can use them for whatever task you imagine. -- --- Note that airborn groups are spawned in uncontrolled state and need to be activated first before they can start their assigned mission. +-- Note that airborne groups are spawned in uncontrolled state and need to be activated first before they can start their assigned mission. -- --- # Strategic Tasks +-- # Strategic Considerations -- --- Due to the fact that a warehouse holds (or can hold) a lot of valuable assets, it makes a juicy task for enemy attacks. +-- Due to the fact that a warehouse holds (or can hold) a lot of valuable assets, it makes a (potentially) juicy target for enemy attacks. -- There are several interesting situations, which can occurr. -- -- ## Capturing a Warehouse' Airbase -- --- If a warehouse has an associated airbase, it can be captured by the enemy. In this case, the warehouse looses it ability so employ all airborn assets and is also cut-off --- from supply by airborn units. +-- If a warehouse has an associated airbase, it can be captured by the enemy. In this case, the warehouse looses it ability so employ all airborne assets and is also cut-off +-- from supply by airborne units. -- -- Technically, the capturing of the airbase is triggered by the DCS S_EVENT_CAPTURE_BASE event. So the capturing takes place when only enemy ground units are in the -- airbase zone whilst no ground units of the present airbase owner are in that zone. @@ -213,7 +248,7 @@ -- -- ## Capturing the Warehouse -- --- A warehouse can also be captured by the enemy coaltion. If enemy groups enter the warehouse zone the event **Attacked** is triggered which can be captured by the +-- A warehouse can also be captured by the enemy coalition. If enemy ground troops enter the warehouse zone the event **Attacked** is triggered which can be captured by the -- @{#WAREHOUSE.OnAfterAttacked} event. -- -- If a warehouse is attacked it will spawn all its ground assets in the spawn zone which can than be used to defend the warehouse zone. @@ -230,12 +265,13 @@ -- gone as well. So a warehouse should be properly defended. -- -- Upon destruction of the warehouse, the event **Destroyed** is triggered, which can be captured by the @{#WAREHOUSE.OnAfterDestroyed} function. --- So the mission designer can invene at this point and for example choose to spawn all or paricular types of assets before the warehouse is gone for good. +-- So the mission designer can intervene at this point and for example choose to spawn all or paricular types of assets before the warehouse is gone for good. -- -- === -- -- # Examples -- +-- **WIP** -- -- -- @field #WAREHOUSE @@ -313,7 +349,6 @@ WAREHOUSE = { -- @field #string ATTRIBUTE Generalized attribute @{#WAREHOUSE.Attribute}. -- @field #string CATEGORY Asset category of type DCS#Group.Category, i.e. GROUND, AIRPLANE, HELICOPTER, SHIP, TRAIN. WAREHOUSE.Descriptor = { - --ID="id", TEMPLATENAME="templatename", UNITTYPE="unittype", ATTRIBUTE="attribute", @@ -382,15 +417,17 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.2.6w" +WAREHOUSE.version="0.2.6" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- TODO: Add transport units from dispatchers back to warehouse stock once they completed their mission. +-- TODO: Add harbours and ports. +-- TODO: Add shipping lanes between warehouses. -- TODO: Set ROE for spawned groups. -- TODO: Add possibility to add active groups. Need to create a pseudo template before destroy. --- DONE: If warehouse is destoyed, all asssets are gone. -- TODO: Write documentation. -- TODO: Handle the case when units of a group die during the transfer. Adjust template?! See Grouping in SPAWN. -- TODO: Handle cases with immobile units. @@ -398,6 +435,10 @@ WAREHOUSE.version="0.2.6w" -- TODO: Handle cases for aircraft carriers and other ships. Place warehouse on carrier possible? On others probably not - exclude them? -- TODO: Add general message function for sending to coaliton or debug. -- TODO: Fine tune event handlers. +-- TODO: Add save/load capability of warehouse <==> percistance after mission restart. +-- TODO: Improve generalized attributes. +-- TODO: Add a time stamp when an asset is added to the stock and for requests +-- DONE: If warehouse is destoyed, all asssets are gone. -- DONE: Add event handlers. -- DONE: Add AI_CARGO_AIRPLANE -- DONE: Add AI_CARGO_APC @@ -444,7 +485,7 @@ function WAREHOUSE:New(warehouse, alias) -- Set some variables. self.warehouse=warehouse - self.uid=warehouse:GetID() + self.uid=tonumber(warehouse:GetID()) self.coalition=warehouse:GetCoalition() self.country=warehouse:GetCountry() self.coordinate=warehouse:GetCoordinate() @@ -465,31 +506,33 @@ function WAREHOUSE:New(warehouse, alias) self:SetStartState("Stopped") -- Add FSM transitions. - self:AddTransition("Stopped", "Load", "Stopped") -- TODO Load the warehouse state. No sure if it should be in stopped state. - self:AddTransition("Stopped", "Start", "Running") -- Start the warehouse. - self:AddTransition("*", "Status", "*") -- Status update. - self:AddTransition("*", "AddAsset", "*") -- Add asset to warehouse stock. - self:AddTransition("*", "AddRequest", "*") -- New request from other warehouse. - self:AddTransition("Running", "Request", "*") -- Process a request. Only in running mode. - self:AddTransition("Attacked", "Request", "*") -- Process a request. Only in running mode. - self:AddTransition("*", "Unloaded", "*") -- Cargo has been unloaded from the carrier. - self:AddTransition("*", "Arrived", "*") -- Cargo group has arrived at destination. - self:AddTransition("*", "Delivered", "*") -- All cargo groups of a request have been delivered to the requesting warehouse. - self:AddTransition("Running", "SelfRequest", "*") -- Request to warehouse itself. Requested assets are only spawned but not delivered anywhere. - self:AddTransition("Attacked", "SelfRequest", "*") -- Request to warehouse itself. Also possible when warehouse is under attack! - self:AddTransition("Running", "Pause", "Paused") -- TODO Pause the processing of new requests. Still possible to add assets and requests. - self:AddTransition("Paused", "Unpause", "Running") -- TODO Unpause the warehouse. Queued requests are processed again. - self:AddTransition("*", "Stop", "Stopped") -- TODO Stop the warehouse. - self:AddTransition("*", "Save", "*") -- TODO Save the warehouse state to disk. - self:AddTransition("*", "Attacked", "Attacked") -- TODO Warehouse is under attack by enemy coalition. - self:AddTransition("Attacked", "Defeated", "Running") -- TODO Attack by other coalition was defeated! - self:AddTransition("Attacked", "Captured", "Running") -- TODO Warehouse was captured by another coalition. It must have been attacked first. - self:AddTransition("*", "AirbaseCaptured", "*") -- TODO Airbase was captured by other coalition. - self:AddTransition("*", "AirbaseRecaptured", "*") -- TODO Airbase was re-captured from other coalition. - self:AddTransition("*", "Destroyed", "*") -- TODO Warehouse was destoryed. All assets in stock are gone and warehouse is stopped. + -- From State --> Event --> To State + self:AddTransition("Stopped", "Load", "Stopped") -- TODO Load the warehouse state. No sure if it should be in stopped state. + self:AddTransition("Stopped", "Start", "Running") -- Start the warehouse. + self:AddTransition("*", "Status", "*") -- Status update. + self:AddTransition("*", "AddAsset", "*") -- Add asset to warehouse stock. + self:AddTransition("*", "AddRequest", "*") -- New request from other warehouse. + self:AddTransition("Running", "Request", "*") -- Process a request. Only in running mode. + self:AddTransition("Attacked", "Request", "*") -- Process a request. Only in running mode. + self:AddTransition("*", "Unloaded", "*") -- Cargo has been unloaded from the carrier. + self:AddTransition("*", "Arrived", "*") -- Cargo group has arrived at destination. + self:AddTransition("*", "Delivered", "*") -- All cargo groups of a request have been delivered to the requesting warehouse. + self:AddTransition("Running", "SelfRequest", "*") -- Request to warehouse itself. Requested assets are only spawned but not delivered anywhere. + self:AddTransition("Attacked", "SelfRequest", "*") -- Request to warehouse itself. Also possible when warehouse is under attack! + self:AddTransition("Running", "Pause", "Paused") -- TODO Pause the processing of new requests. Still possible to add assets and requests. + self:AddTransition("Paused", "Unpause", "Running") -- TODO Unpause the warehouse. Queued requests are processed again. + self:AddTransition("*", "Stop", "Stopped") -- TODO Stop the warehouse. + self:AddTransition("*", "Save", "*") -- TODO Save the warehouse state to disk. + self:AddTransition("*", "Attacked", "Attacked") -- TODO Warehouse is under attack by enemy coalition. + self:AddTransition("Attacked", "Defeated", "Running") -- TODO Attack by other coalition was defeated! + self:AddTransition("Attacked", "Captured", "Running") -- TODO Warehouse was captured by another coalition. It must have been attacked first. + self:AddTransition("*", "AirbaseCaptured", "*") -- TODO Airbase was captured by other coalition. + self:AddTransition("*", "AirbaseRecaptured", "*") -- TODO Airbase was re-captured from other coalition. + self:AddTransition("*", "Destroyed", "*") -- TODO Warehouse was destoryed. All assets in stock are gone and warehouse is stopped. - - -- Pseudo Functions + ------------------------ + --- Pseudo Functions --- + ------------------------ --- Triggers the FSM event "Start". Starts the warehouse. Initializes parameters and starts event handlers. -- @function [parent=#WAREHOUSE] Start @@ -528,7 +571,6 @@ function WAREHOUSE:New(warehouse, alias) -- @param #number delay Delay in seconds. - --- Triggers the FSM event "Status". Queue is updated and requests are executed. -- @function [parent=#WAREHOUSE] Status -- @param #WAREHOUSE self @@ -544,6 +586,7 @@ function WAREHOUSE:New(warehouse, alias) -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group Group to be added as new asset. -- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1. + -- @param #WAREHOUSE.Attribute forceattribute (Optional) Explicitly force a generalized attribute for the asset. This has to be an @{#WAREHOUSE.Attribute}. --- Trigger the FSM event "AddAsset" with a delay. Add an airplane group to the warehouse stock. -- @function [parent=#WAREHOUSE] __AddAsset @@ -551,6 +594,7 @@ function WAREHOUSE:New(warehouse, alias) -- @param #number delay Delay in seconds. -- @param Wrapper.Group#GROUP group Group to be added as new asset. -- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1. + -- @param #WAREHOUSE.Attribute forceattribute (Optional) Explicitly force a generalized attribute for the asset. This has to be an @{#WAREHOUSE.Attribute}. --- Triggers the FSM event "AddRequest". Add a request to the warehouse queue, which is processed when possible. @@ -811,6 +855,13 @@ function WAREHOUSE:IsAttacked() return self:is("Attacked") end +--- Check if the warehouse is stopped. +-- @param #WAREHOUSE self +-- @return #boolean If true, the warehouse is stopped. +function WAREHOUSE:IsStopped() + return self:is("Stopped") +end + --- Check if the warehouse has a road connection to another warehouse. Both warehouses need to be started! -- @param #WAREHOUSE self -- @param #WAREHOUSE warehouse The remote warehose to where the connection is checked. @@ -868,10 +919,10 @@ function WAREHOUSE:onafterStart(From, Event, To) text=text..string.format("Country = %d\n", self.country) text=text..string.format("Airbase = %s (%s)\n", tostring(self.airbase:GetName()), tostring(self.category)) env.info(text) - + -- Save self in static object. Easier to retrieve later. self.warehouse:SetState(self.warehouse, "WAREHOUSE", self) - + -- Set airbase name and category. if self.airbase and self.airbase:GetCoalition()==self.coalition then self.airbasename=self.airbase:GetName() @@ -880,11 +931,14 @@ function WAREHOUSE:onafterStart(From, Event, To) self.airbasename=nil self.category=-1 -- The -1 indicates that we dont have an airbase at this warehouse. end - + + -- THIS! caused aircraft to be spawned and started but they would never begin their route! + -- VERY strange. Need to test more. + --[[ -- Debug mark warehouse & spawn zone. self.zone:BoundZone(30, self.country) self.spawnzone:BoundZone(30, self.country) - + ]] -- Get the closest point on road wrt spawnzone of ground assets. local _road=self.spawnzone:GetCoordinate():GetClosestPointToRoad() @@ -932,6 +986,7 @@ function WAREHOUSE:onafterStart(From, Event, To) -- Start the status monitoring. self:__Status(1) + end --- On after "Stop" event. Stops the warehouse, unhandles all events. @@ -1035,7 +1090,8 @@ end -- @param #string To To state. -- @param Wrapper.Group#GROUP group Group or template group to be added to the warehouse stock. -- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1. -function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups) +-- @param #WAREHOUSE.Attribute forceattribute (Optional) Explicitly force a generalized attribute for the asset. This has to be an @{#WAREHOUSE.Attribute}. +function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribute) -- Set default. local n=ngroups or 1 @@ -1070,7 +1126,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups) else -- This is a group that is not in the db yet. Add it n times. - local assets=self:_RegisterAsset(group, n) + local assets=self:_RegisterAsset(group, n, forceattribute) -- Add created assets to stock of this warehouse. for _,asset in pairs(assets) do @@ -1082,7 +1138,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups) -- TODO: This causes a problem, when a completely new asset is added, i.e. not from a template group. -- Need to create a "zombie" template group maybe? if group:IsAlive()==true then - env.info("FF destorying group "..group:GetName()) + self:E(self.wid..string.format("Destroying group %s.", group:GetName())) group:Destroy() end @@ -1117,8 +1173,9 @@ end -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group The group that will be added to the warehouse stock. -- @param #number ngroups Number of groups to be added. +-- @param #string forceattribute Forced generalized attribute. -- @return #table A table containing all registered assets. -function WAREHOUSE:_RegisterAsset(group, ngroups) +function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute) -- Set default. local n=ngroups or 1 @@ -1147,8 +1204,8 @@ function WAREHOUSE:_RegisterAsset(group, ngroups) local RangeMin=group:GetRange() local smax,sx,sy,sz=_GetObjectSize(DCSdesc) - -- Get the generalized attribute. - local attribute=self:_GetAttribute(templategroupname) + -- Set/get the generalized attribute. + local attribute=forceattribute or self:_GetAttribute(templategroupname) -- Table for returned assets. local assets={} @@ -1219,8 +1276,9 @@ end -- @param #WAREHOUSE self -- @param #WAREHOUSE.Assetitem asset Ground asset that will be spawned. -- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. +-- @param boolean aioff If true, AI of ground units are set to off. -- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned. -function WAREHOUSE:_SpawnAssetGround(asset, request) +function WAREHOUSE:_SpawnAssetGround(asset, request, aioff) if asset and asset.category==Group.Category.GROUND then @@ -1228,7 +1286,7 @@ function WAREHOUSE:_SpawnAssetGround(asset, request) local template=self:_SpawnAssetPrepareTemplate(asset, request) -- Initial spawn point. - template.route.points[1] = {} + template.route.points[1]={} -- Get a random coordinate in the spawn zone. local coord=self.spawnzone:GetRandomCoordinate() @@ -1262,8 +1320,13 @@ function WAREHOUSE:_SpawnAssetGround(asset, request) -- Spawn group. local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP - -- Activate group. - group:Activate() + -- Activate group. Should only be necessary for late activated groups. + --group:Activate() + + -- Switch AI off if desired. This works only for ground and naval groups. + if aioff then + group:SetAIOff() + end return group end @@ -1276,45 +1339,55 @@ end -- @param #WAREHOUSE.Assetitem asset Ground asset that will be spawned. -- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. -- @param #table parking Parking data for this asset. +-- @param #boolean uncontrolled Spawn aircraft in uncontrolled state. -- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned. -function WAREHOUSE:_SpawnAssetAircraft(asset, request, parking) +function WAREHOUSE:_SpawnAssetAircraft(asset, request, parking, uncontrolled) if asset and asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then - -- Prepare spawn template. + -- Prepare the spawn template. local template=self:_SpawnAssetPrepareTemplate(asset, request) - -- Set and empty route. + -- Set route points. if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then - -- Get flight path. + + -- Get flight path if the group goes to another warehouse by itself. template.route.points=self:_GetFlightplan(asset, self.airbase, request.warehouse.airbase) - --template.route.points[1] = {} + else - template.route.points[1] = {} + + -- First route point is the warehouse airbase. + template.route.points[1]=self.airbase:GetCoordinate():WaypointAir("BARO", COORDINATE.WaypointType.TakeOffParking, COORDINATE.WaypointAction.FromParkingArea, 0, true, self.airbase, nil, "Spawnpoint") + + --[[ + -- Link. + local spawnpoint=template.route.points[1] + + -- Set initial waypoint type/action ==> cold start. + spawnpoint.type = COORDINATE.WaypointType.TakeOffParking + spawnpoint.action = COORDINATE.WaypointAction.FromParkingArea + + --spawnpoint.type = COORDINATE.WaypointType.TakeOffParkingHot + --spawnpoint.action = COORDINATE.WaypointAction.FromParkingAreaHot + + -- Set airbase ID. + if AirbaseCategory == Airbase.Category.HELIPAD or AirbaseCategory == Airbase.Category.SHIP then + spawnpoint.helipadId = AirbaseID + spawnpoint.linkUnit = AirbaseID + spawnpoint.airdromeId = nil + elseif AirbaseCategory == Airbase.Category.AIRDROME then + spawnpoint.airdromeId = AirbaseID + spawnpoint.linkUnit = nil + spawnpoint.helipadId = nil + end + ]] + end - -- Link. - local spawnpoint=template.route.points[1] - - -- Set initial waypoint type/action ==> cold start. - spawnpoint.type = COORDINATE.WaypointType.TakeOffParking - spawnpoint.action = COORDINATE.WaypointAction.FromParkingArea - -- Get airbase ID and category. local AirbaseID = self.airbase:GetID() local AirbaseCategory = self.category - -- Set airbase ID. - if AirbaseCategory == Airbase.Category.HELIPAD or AirbaseCategory == Airbase.Category.SHIP then - spawnpoint.helipadId = AirbaseID - spawnpoint.linkUnit = AirbaseID - spawnpoint.airdromeId = nil - elseif AirbaseCategory == Airbase.Category.AIRDROME then - spawnpoint.airdromeId = AirbaseID - spawnpoint.linkUnit = nil - spawnpoint.helipadId = nil - end - -- Check enough parking spots. if AirbaseCategory == Airbase.Category.HELIPAD or AirbaseCategory == Airbase.Category.SHIP then --TODO Figure out what's necessary in this case. @@ -1351,7 +1424,7 @@ function WAREHOUSE:_SpawnAssetAircraft(asset, request, parking) local coord=parking[i].Coordinate --Core.Point#COORDINATE local terminal=parking[i].TerminalID --#number - coord:MarkToAll(string.format("spawnplace unit %s terminal %d", unit.name, terminal)) + coord:MarkToAll(string.format("Spawnplace unit %s terminal %d", unit.name, terminal)) unit.x=coord.x unit.y=coord.z @@ -1364,27 +1437,26 @@ function WAREHOUSE:_SpawnAssetAircraft(asset, request, parking) end -- Set general spawnpoint position. - local abc=self.airbase:GetCoordinate() - spawnpoint.x = template.units[1].x - spawnpoint.y = template.units[1].y - spawnpoint.alt = template.units[1].alt + --local abc=self.airbase:GetCoordinate() + --spawnpoint.x = template.units[1].x + --spawnpoint.y = template.units[1].y + --spawnpoint.alt = template.units[1].alt -- And template position. template.x = template.units[1].x template.y = template.units[1].y -- Uncontrolled spawning. - template.uncontrolled=true + template.uncontrolled=uncontrolled -- Debug info. - self:E({airtemplate=template}) + self:T2({airtemplate=template}) - -- Spawn group. local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP - -- Activate group. - group:Activate() + -- Activate group - should only be necessary for late activated groups. + --group:Activate() return group end @@ -1402,6 +1474,7 @@ function WAREHOUSE:_SpawnAssetPrepareTemplate(asset, request) -- Create an own copy of the template! local template=UTILS.DeepCopy(asset.template) + --local template=asset.template -- Set unique name. template.name=self:_Alias(asset, request) @@ -1412,12 +1485,18 @@ function WAREHOUSE:_SpawnAssetPrepareTemplate(asset, request) -- Nillify the group ID. template.groupId=nil + + -- For group units, visible needs to be false. + if asset.category==Group.Category.GROUND then + template.visible=false + end -- No late activation. template.lateActivation=false -- Set and empty route. template.route = {} + template.route.routeRelativeTOT=true template.route.points = {} -- Handle units. @@ -1429,7 +1508,7 @@ function WAREHOUSE:_SpawnAssetPrepareTemplate(asset, request) -- Nillify the unit ID. unit.unitId=nil - -- Set unit name. + -- Set unit name: -01, -02, ... unit.name=string.format("%s-%02d", template.name , i) end @@ -1606,40 +1685,43 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- No transport unit requested. Assets go by themselfes. if Request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then - env.info("FF selfpropelled") + self:I(self.wid..string.format("Got selfpropelled request for %d assets.",_spawngroups:Count())) for _,_spawngroup in pairs(_spawngroups:GetSetObjects()) do + -- Group intellisense. local group=_spawngroup --Wrapper.Group#GROUP -- Route cargo to their destination. if _cargocategory==Group.Category.GROUND then - env.info("FF route ground "..group:GetName()) + self:I(self.wid..string.format("Route ground group %s.", group:GetName())) -- Random place in the spawn zone of the requesting warehouse. local ToCoordinate=Request.warehouse.spawnzone:GetRandomCoordinate() - ToCoordinate:MarkToAll("Destination") + ToCoordinate:MarkToAll(string.format("Destination of group %s", group:GetName())) -- Route ground. self:_RouteGround(group, Request) - elseif _cargocategory==Group.Category.AIRPLANE or _cargocategory==Group.Category.HELICOPTER then - env.info("FF route aircraft "..group:GetName()) + elseif _cargocategory==Group.Category.AIRPLANE or _cargocategory==Group.Category.HELICOPTER then + self:I(self.wid..string.format("Route airborne group %s.", group:GetName())) -- Route plane the the requesting warehouses airbase. -- Actually, the route is already set. We only need to activate the uncontrolled group. self:_RouteAir(group, Request.airbase) elseif _cargocategory==Group.Category.SHIP then + self:E("ERROR: self propelled ship not implemented yet!") + elseif _cargocategory==Group.Category.TRAIN then - env.info("FF route train "..group:GetName()) + self:I(self.wid..string.format("Route train group %s.", group:GetName())) -- Route train to the rail connection of the requesting warehouse. self:_RouteTrain(group, Request.warehouse.rail) else - self:E(self.wid..string.format("ERROR: unknown category %s for self propelled cargo %s!",tostring(_cargocategory), tostring(group:GetName()))) + self:E(self.wid..string.format("ERROR: unknown category %s for self propelled cargo %s!", tostring(_cargocategory), tostring(group:GetName()))) end end @@ -1732,7 +1814,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) local _alias=self:_Alias(_assetitem, Request) -- Spawn plane at airport in uncontrolled state. - local spawngroup=self:_SpawnAssetAircraft(_assetitem, Pending, Parking[_assetitem.uid]) + local spawngroup=self:_SpawnAssetAircraft(_assetitem, Pending, Parking[_assetitem.uid], true) if spawngroup then -- Set state of warehouse so we can retrieve it later. @@ -1768,8 +1850,8 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Spawn with ALIAS here or DCS crashes! local _alias=self:_Alias(_assetitem, Request) - -- Spawn plane at airport in uncontrolled state. - local spawngroup=self:_SpawnAssetAircraft(_assetitem, Pending, Parking[_assetitem.uid]) + -- Spawn plane at airport in controlled state. They need to fly to the spawn zone. + local spawngroup=self:_SpawnAssetAircraft(_assetitem, Pending, Parking[_assetitem.uid], false) if spawngroup then -- Set state of warehouse so we can retrieve it later. @@ -1795,7 +1877,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Home zone. --CargoTransport:Setairbase(self.airbase) - --CargoTransport:SetHomeZone(self.spawnzone) + CargoTransport:SetHomeZone(self.spawnzone) elseif Request.transporttype==WAREHOUSE.TransportType.APC then ----------- @@ -1861,12 +1943,12 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) --- Function called when cargo has arrived and was unloaded. function CargoTransport:OnAfterUnloaded(From, Event, To, Carrier, Cargo) - env.info("FF: OnAfterUnloaded") - self:E({From=From}) - self:E({Event=Event}) - self:E({To=To}) - self:E({Carrier=Carrier}) - self:E({Cargo=Cargo}) + self:I("FF OnAfterUnloaded:") + self:I({From=From}) + self:I({Event=Event}) + self:I({To=To}) + self:I({Carrier=Carrier}) + self:I({Cargo=Cargo}) -- Get group obejet. local group=Cargo:GetObject() --Wrapper.Group#GROUP @@ -1970,16 +2052,22 @@ function WAREHOUSE:_SpawnAssetRequest(Request) --TODO: spawn only so many groups as there are parking spots. Adjust request and create a new one with the reduced number! -- Spawn air units. - _group=self:_SpawnAssetAircraft(_assetitem, Request, Parking[_assetitem.uid]) + _group=self:_SpawnAssetAircraft(_assetitem, Request, Parking[_assetitem.uid], UnControlled) elseif _assetitem.category==Group.Category.TRAIN then -- Spawn train. if self.rail then --TODO: Rail should only get one asset because they would spawn on top! - _group=_spawn:SpawnFromCoordinate(self.rail) + --_group=_spawn:SpawnFromCoordinate(self.rail) end + self:E(self.wid.."ERROR: Spawning of TRAIN assets not possible yet!") + + elseif _assetitem.category==Group.Category.SHIP then + self:E(self.wid.."ERROR: Spawning of SHIP assets not possible yet!") + else + end -- Add group to group set and asset list. @@ -2170,10 +2258,10 @@ end -- @param #WAREHOUSE.Pendingitem request Pending self request. function WAREHOUSE:onafterSelfRequest(From, Event, To, groupset, request) - self:E(self.wid..string.format("Assets spawned at warehouse %s after self request!", self.alias)) + -- Debug info. + self:I(self.wid..string.format("Assets spawned at warehouse %s after self request!", self.alias)) - env.info("FF spawned groupas at self request") - -- Put assets in new warehouse. + -- Debug info. for _,_group in pairs(groupset:GetSetObjects()) do local group=_group --Wrapper.Group#GROUP local text=string.format("Group name = %s, IsAlive=%s.", tostring(group:GetName()), tostring(group:IsAlive())) @@ -2339,9 +2427,6 @@ function WAREHOUSE:_RouteGround(group, request) local _speed=group:GetSpeedMax()*0.7 -- Create task. - -- DONE: It might be necessary to ALWAYS route the group to the road connection first. - -- At the moment, the random spawn point might give another first road point which could also be a dead end like in Kobuliti(?). - -- TODO: Might be necessary to include the current coordinate as first waypoint?! -- Waypoints for road-to-road connection. local Waypoints, canroad = group:TaskGroundOnRoad(request.warehouse.road, _speed, "Off Road", false, self.road) @@ -2371,24 +2456,28 @@ function WAREHOUSE:_RouteGround(group, request) end ---- Route the airplane from one airbase another. +--- Route the airplane from one airbase another. Activates uncontrolled aircraft and sets ROE/ROT for ferry flights. -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP Aircraft Airplane group to be routed. function WAREHOUSE:_RouteAir(aircraft) if aircraft and aircraft:IsAlive()~=nil then - -- Start command. - local StartCommand = {id = 'Start', params = {}} - aircraft:SetCommand(StartCommand) + -- Debug info. + self:T2(self.wid..string.format("RouteAir aircraft group %s alive=%s", aircraft:GetName(), tostring(aircraft:IsAlive()))) + + -- Give start command to activate uncontrolled aircraft. + aircraft:SetCommand({id='Start', params={}}) + -- Debug info. + self:T2(self.wid..string.format("RouteAir aircraft group %s alive=%s (after start command)", aircraft:GetName(), tostring(aircraft:IsAlive()))) + -- Set ROE and alaram state. aircraft:OptionROEReturnFire() aircraft:OptionROTPassiveDefense() - else - self:E(string.format("ERROR: aircraft %s cannot be routed since it does not exist or is not alive %s!", tostring(Aircraft:GetName()), tostring(Aircraft:IsAlive()))) + self:E(string.format("ERROR: aircraft %s cannot be routed since it does not exist or is not alive %s!", tostring(aircraft:GetName()), tostring(aircraft:IsAlive()))) end end @@ -2422,9 +2511,9 @@ end -- @param Wrapper.Group#GROUP group The group that arrived. -- @param #WAREHOUSE self function WAREHOUSE._Arrived(group, warehouse) - env.info(warehouse.wid..string.format("Group %s arrived", tostring(group:GetName()))) + env.info(warehouse.wid..string.format("Group %s arrived at destination.", tostring(group:GetName()))) - --Trigger delivered event. + --Trigger "Arrived" event. warehouse:__Arrived(1, group) end @@ -2438,7 +2527,7 @@ function WAREHOUSE:_ArrivedSimple(group) --local self:_GetIDsFromGroup(group) env.info(self.wid..string.format("Group %s arrived at warehouse ", tostring(group:GetName()))) - --Trigger delivered event. + --Trigger "Arrived event. self:__Arrived(1, group) end @@ -2484,7 +2573,7 @@ function WAREHOUSE:_OnEventArrived(EventData) self:__Arrived(dt, group) else - self:E(string.format("Group that arrived did not belong to a warehouse. Warehouse ID=%s, Asset ID=%s, Request ID=%s.", tostring(wid), tostring(aid), tostring(rid))) + self:T3(string.format("Group that arrived did not belong to a warehouse. Warehouse ID=%s, Asset ID=%s, Request ID=%s.", tostring(wid), tostring(aid), tostring(rid))) end end end @@ -2495,13 +2584,17 @@ end -- @param #WAREHOUSE self -- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventBirth(EventData) - self:T3(self.wid..string.format("Warehouse %s captured event birth!", self.alias)) + self:T3(self.wid..string.format("Warehouse %s (id=%s) captured event birth!", self.alias, self.uid)) if EventData and EventData.IniGroup then local group=EventData.IniGroup + -- env.info(string.format("FF birth of group %s (alive=%s) unit %s", tostring(EventData.IniGroupName), tostring(EventData.IniGroup:IsAlive()), tostring(EventData.IniUnitName))) + -- Note: Remember, group:IsAlive might(?) not return true here. local wid,aid,rid=self:_GetIDsFromGroup(group) if wid==self.uid then self:E(self.wid..string.format("Warehouse %s captured event birth of its asset unit %s.", self.alias, EventData.IniUnitName)) + else + --self:T3({wid=wid, uid=self.uid, match=(wid==self.uid), tw=type(wid), tu=type(self.uid)}) end end end @@ -2570,7 +2663,7 @@ end -- @param #WAREHOUSE self -- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventCrashOrDead(EventData) - self:E(self.wid..string.format("Warehouse %s captured event dead or crash!",self.alias)) + self:T3(self.wid..string.format("Warehouse %s captured event dead or crash!",self.alias)) if EventData and EventData.IniUnit then @@ -2581,6 +2674,15 @@ function WAREHOUSE:_OnEventCrashOrDead(EventData) self:Destroyed() end end + + -- Check if an asset unit was destroyed. + if EventData and EventData.IniGroup then + local group=EventData.IniGroup + local wid,aid,rid=self:_GetIDsFromGroup(group) + if wid==self.uid then + self:E(self.wid..string.format("Warehouse %s captured event dead or crash of its asset unit %s.", self.alias, EventData.IniUnitName)) + end + end end @@ -2778,7 +2880,7 @@ end -- @param #table queue The queue which is holding the requests to check. -- @return #boolean If true, request can be executed. If false, something is not right. function WAREHOUSE:_CheckRequestConsistancy(queue) - env.info("FF checking request consistancy!") + self:T3(self.wid..string.format("Number of queued requests = %d", #queue)) -- Requests to delete. local invalid={} @@ -2786,6 +2888,9 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) for _,_request in pairs(queue) do local request=_request --#WAREHOUSE.Queueitem + -- Debug info. + self:T2(self.wid..string.format("Checking request = %d.", request.uid)) + -- Let's assume everything is fine. local valid=true @@ -2803,9 +2908,16 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) -- Request from enemy coalition? if self.coalition~=request.warehouse.coalition then self:E(self.wid..string.format("ERROR: Incorrect request. Requesting warehouse is of wrong coaltion! Own coalition %d. Requesting warehouse %d", self.coalition, request.warehouse.coalition)) + valid=false + end + + -- Is receiving warehouse stopped? + if request.warehouse:IsStopped() then + self:E(self.wid..string.format("ERROR: Incorrect request. Requesting warehouse is stopped!")) valid=false end - + + -- Asset is air, ground etc. local asset_air=false local asset_plane=false local asset_helo=false @@ -2830,7 +2942,7 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) -- Only one train due to finding spawn placen on rail! --nAsset=1 else - self:E("ERROR: incorrect request. Asset Descriptor missmatch! Has to be Group.Cagetory.AIRPLANE, ...") + self:E("ERROR: Incorrect request. Asset Descriptor missmatch! Has to be Group.Cagetory.AIRPLANE, ...") valid=false end @@ -2851,7 +2963,7 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) elseif request.assetdescval==WAREHOUSE.Attribute.INFANTRY then asset_ground=true elseif request.assetdescval==WAREHOUSE.Attribute.OTHER then - self:E("ERROR: incorrect request. Asset attribute WAREHOUSE.Attribute.OTHER is not valid!") + self:E("ERROR: Incorrect request. Asset attribute WAREHOUSE.Attribute.OTHER is not valid!") valid=false elseif request.assetdescval==WAREHOUSE.Attribute.SHIP then asset_naval=true @@ -2870,7 +2982,7 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) elseif request.assetdescval==WAREHOUSE.Attribute.TRUCK then asset_ground=true else - self:E("ERROR: incorrect request. Unknown asset attribute!") + self:E("ERROR: Incorrect request. Unknown asset attribute!") valid=false end end @@ -2882,13 +2994,14 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) ------------------------------------------- -- Case where the units go my themselves -- ------------------------------------------- + if asset_air then if asset_plane then -- No airplane to or from FARPS. if request.category==Airbase.Category.HELIPAD or self.category==Airbase.Category.HELIPAD then - self:E("ERROR: incorrect request. Asset aircraft requestst but warehouse or requestor is HELIPAD/FARP!") + self:E("ERROR: Incorrect request. Asset airplane requested but warehouse or requestor is HELIPAD/FARP!") valid=false end @@ -2897,10 +3010,10 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) elseif asset_helo then - -- Helos need a FARP or AIRBASE or SHIP for spawning. Event if they go there they "cannot" be spawned again. + -- Helos need a FARP or AIRBASE or SHIP for spawning. Also at the the receiving warehouse. So even if they could go there they "cannot" be spawned again. -- Unless I allow spawning of helos in the the spawn zone. But one should place at least a FARP there. if self.category==-1 or request.category==-1 then - self:E("ERROR: incorrect request. Helos need a AIRBASE/HELIPAD/SHIP as home/destinaion base!") + self:E("ERROR: Incorrect request. Helos need a AIRBASE/HELIPAD/SHIP as home/destination base!") valid=false end @@ -2908,7 +3021,7 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) -- All aircraft need an airbase of any type as depature or destination. if self.airbase==nil or request.airbase==nil then - self:E("ERROR: incorrect request. Either warehouse or requesting warehouse does not have any kind of airbase!") + self:E("ERROR: Incorrect request. Either warehouse or requesting warehouse does not have any kind of airbase!") valid=false end @@ -2917,57 +3030,42 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) -- No ground assets directly to or from ships. -- TODO: May needs refinement if warehouse is on land and requestor is ship in harbour?! if (request.category==Airbase.Category.SHIP or self.category==Airbase.Category.SHIP) then - self:E("ERROR: incorrect request. Ground asset requested but warehouse or requestor is SHIP!") + self:E("ERROR: Incorrect request. Ground asset requested but warehouse or requestor is SHIP!") valid=false end if asset_train then -- Check if there is a valid path on rail. - valid=self:HasConnectionRail(request.warehouse) - --[[ - if self.rail and request.warehouse.rail then - local onrail=self.rail:GetPathOnRoad(request.warehouse.rail, false, true) - if onrail==nil then - self:E("ERROR: incorrect request. No valid path on rail for train assets!") - valid=false - end - else - self:E("ERROR: incorrect request. Either warehouse or requesting warehouse have no connection to rail!") - valid=false + local hasrail=self:HasConnectionRail(request.warehouse) + if not hasrail then + self:E("ERROR: Incorrect request. No valid path on rail for train assets!") + valid=false end - ]] else -- Check if there is a valid path on road. - valid=self:HasConnectionRoad(request.warehouse) - --[[ - if self.road and request.warehouse.road then - local onroad=self.road:GetPathOnRoad(request.warehouse.road, false, false) - if onroad==nil then - self:E("ERROR: incorrect request. No valid path on road for ground assets!") - valid=false - end - else - self:E("ERROR: incorrect request. Either warehouse or requesting warehouse have no connection to road!") - valid=false - end - ]] + local hasroad=self:HasConnectionRoad(request.warehouse) + if not hasroad then + self:E("ERROR: Incorrect request. No valid path on road for ground assets!") + valid=false + end end elseif asset_naval then - self:E("ERROR: incorrect request. Naval units not supported yet!") + self:E("ERROR: Incorrect request. Naval units not supported yet!") valid=false end - else - - -- Assests need a transport. - + else + ------------------------------- + -- Assests need a transport --- + ------------------------------- + if request.transporttype==WAREHOUSE.TransportType.AIRPLANE then -- Airplanes only to AND from airdromes. if self.category~=Airbase.Category.AIRDROME or request.category~=Airbase.Category.AIRDROME then - self:E("ERROR: incorrect request. Warehouse or requestor does not have an airdrome. No transport by plane possible!") + self:E("ERROR: Incorrect request. Warehouse or requestor does not have an airdrome. No transport by plane possible!") valid=false end @@ -2979,44 +3077,51 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) -- No transport to or from ships if self.category==Airbase.Category.SHIP or request.category==Airbase.Category.SHIP then - self:E("ERROR: incorrect request. Warehouse or requestor is SHIP. No transport by APC possible!") + self:E("ERROR: Incorrect request. Warehouse or requestor is SHIP. No transport by APC possible!") + valid=false + end + + -- Check if there is a valid path on road. + local hasroad=self:HasConnectionRoad(request.warehouse) + if not hasroad then + self:E("ERROR: Incorrect request. No valid path on road for ground transport assets!") valid=false end elseif request.transporttype==WAREHOUSE.TransportType.HELICOPTER then - -- Transport by helicopters ==> need airbase for spawning but not for delivering to the zone. + -- Transport by helicopters ==> need airbase for spawning but not for delivering to the spawn zone of the receiver. if self.category==-1 then - self:E("ERROR: incorrect request. Warehouse has no airbase. Transport by helicopter not possible!") + self:E("ERROR: Incorrect request. Warehouse has no airbase. Transport by helicopter not possible!") valid=false end elseif request.transporttype==WAREHOUSE.TransportType.SHIP then -- Transport by ship. - self:E("ERROR: incorrect request. Transport by SHIP not implemented yet!") + self:E("ERROR: Incorrect request. Transport by SHIP not implemented yet!") valid=false elseif request.transporttype==WAREHOUSE.TransportType.TRAIN then - -- Only one train due to limited spawn place. - --nTransport=1 - -- Transport by train. - self:E("ERROR: incorrect request. Transport by TRAIN not implemented yet!") + self:E("ERROR: Incorrect request. Transport by TRAIN not implemented yet!") valid=false else -- No match. - self:E("ERROR: incorrect request. Transport type unknown!") + self:E("ERROR: Incorrect request. Transport type unknown!") valid=false end end -- Add request as unvalid and delete it later. - if not valid then + if valid==false then + self:E(self.wid..string.format("Got invalid request id=%d.", request.uid)) table.insert(invalid, request) + else + self:T3(self.wid..string.format("Got valid request id=%d.", request.uid)) end end -- loop queue items. @@ -3024,7 +3129,7 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) -- Delete invalid requests. for _,_request in pairs(invalid) do - self:E(self.wid..string.format("Deleting invalid request %d",_request.uid)) + self:E(self.wid..string.format("Deleting invalid request id=%d.",_request.uid)) self:_DeleteQueueItem(_request, self.queue) end @@ -3037,14 +3142,23 @@ end -- @return #boolean If true, request can be executed. If false, something is not right. function WAREHOUSE:_CheckRequestNow(request) + -- Assume request is okay and check scenarios. local okay=true + -- Check if receiving warehouse is running. + if not request.warehouse:IsRunning() then + local text=string.format("Warehouse %s: Request denied! Receiving warehouse %s is not running. Current state %s.", self.alias, request.warehouse.alias, request.warehouse:GetState()) + MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:E(self.wid..text) + okay=false + end + -- Check if number of requested assets is in stock. local _assets,_nassets,_enough=self:_FilterStock(self.stock, request.assetdesc, request.assetdescval, request.nasset) -- Check if enough assets are in stock. if not _enough then - local text=string.format("Request denied! Not enough (cargo) assets currently available.") + local text=string.format("Warehouse %s: Request denied! Not enough (cargo) assets currently available.", self.alias) MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:E(self.wid..text) okay=false @@ -3061,7 +3175,7 @@ function WAREHOUSE:_CheckRequestNow(request) if self.airbase and (_assetcategory==Group.Category.AIRPLANE or _assetcategory==Group.Category.HELICOPTER) then local Parking=self:_FindParkingForAssets(self.airbase,_assets) if Parking==nil then - local text=string.format("Request denied! Not enough free parking spots for all assets at the moment.") + local text=string.format("Warehouse %s: Request denied! Not enough free parking spots for all assets at the moment.", self.alias) MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:E(self.wid..text) okay=false @@ -3078,7 +3192,7 @@ function WAREHOUSE:_CheckRequestNow(request) -- Check if enough transport units are available. if not _enough then - local text=string.format("Request denied! Not enough transport units currently available.") + local text=string.format("Warehouse %s: Request denied! Not enough transport assets currently available.", self.alias) MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:E(self.wid..text) okay=false @@ -3095,7 +3209,7 @@ function WAREHOUSE:_CheckRequestNow(request) if self.airbase and (_transportcategory==Group.Category.AIRPLANE or _transportcategory==Group.Category.HELICOPTER) then local Parking=self:_FindParkingForAssets(self.airbase,_transports) if Parking==nil then - local text=string.format("Request denied! Not enough free parking spots for all transports at the moment.") + local text=string.format("Warehouse %s: Request denied! Not enough free parking spots for all transports at the moment.", self.alias) MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:E(self.wid..text) okay=false @@ -3237,7 +3351,8 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) local _vec3=static:getPoint() local _coord=COORDINATE:NewFromVec3(_vec3) local _name=static:getName() - local _size=self:_GetObjectSize(static:GetDCSObject()) + --env.info("FF static name = "..tostring(_name)) + local _size=self:_GetObjectSize(static) table.insert(obstacles[_termid],{coord=_coord, size=_size, name=_name, type="static"}) end @@ -3246,7 +3361,7 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) local _vec3=scenery:getPoint() local _coord=COORDINATE:NewFromVec3(_vec3) local _name=scenery:getTypeName() - local _size=self:_GetObjectSize(scenery:GetDCSObject()) + local _size=self:_GetObjectSize(scenery) table.insert(obstacles[_termid],{coord=_coord, size=_size, name=_name, type="scenery"}) end @@ -3818,7 +3933,7 @@ end -- @return #table Table of flightplan coordinates. function WAREHOUSE:_GetFlightplan(asset, departure, destination) - -- Parameters + -- Parameters in SI units. local Vmax=asset.speedmax/3.6 local Range=asset.range local _category=asset.category diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 081c632bf..8b7f34a26 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -661,8 +661,9 @@ function GROUP:GetDCSUnits() end ---- Activates a GROUP. +--- Activates a late activated GROUP. -- @param #GROUP self +-- @return #GROUP self function GROUP:Activate() self:F2( { self.GroupName } ) trigger.action.activateGroup( self:GetDCSObject() ) diff --git a/Moose Development/Moose/Wrapper/Object.lua b/Moose Development/Moose/Wrapper/Object.lua index f67ec389e..f57ff73e8 100644 --- a/Moose Development/Moose/Wrapper/Object.lua +++ b/Moose Development/Moose/Wrapper/Object.lua @@ -54,8 +54,7 @@ end --- Returns the unit's unique identifier. -- @param Wrapper.Object#OBJECT self --- @return DCS#Object.ID ObjectID --- @return #nil The DCS Object is not existing or alive. +-- @return DCS#Object.ID ObjectID or #nil if the DCS Object is not existing or alive. Note that the ID is passed as a string and not a number. function OBJECT:GetID() local DCSObject = self:GetDCSObject() From 508e35aec3a21078051295b530e5abc86a03eb99 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 27 Aug 2018 00:10:05 +0200 Subject: [PATCH 284/420] Warehouse v0.2.7 --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 2 + .../Moose/AI/AI_Cargo_Dispatcher.lua | 7 +- .../Moose/AI/AI_Cargo_Helicopter.lua | 25 ++- Moose Development/Moose/Core/Zone.lua | 1 + .../Moose/Functional/Warehouse.lua | 208 +++++++++++------- .../Moose/Wrapper/Controllable.lua | 9 +- Moose Development/Moose/Wrapper/Group.lua | 3 +- 7 files changed, 158 insertions(+), 97 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 47b900cd0..58a2e7589 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -804,6 +804,7 @@ end -- @param #AI_CARGO_APC self function AI_CARGO_APC._BackHome(APC, self) --Trigger BackHome event. + env.info(string.format("FF APC %s is back home task function!",APC:GetName())) APC:SmokeGreen() self:__BackHome(1) end @@ -815,5 +816,6 @@ end -- @param Event -- @param To function AI_CARGO_APC:onafterBackHome( APC, From, Event, To ) + env.info(string.format("FF APC %s is back home event!",APC:GetName())) APC:SmokeRed() end \ No newline at end of file diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 5bda17ffe..05264bfa6 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -402,7 +402,12 @@ function AI_CARGO_DISPATCHER:onafterMonitor() function AI_Cargo.OnAfterUnloaded( AI_Cargo, Carrier, From, Event, To, Cargo ) self:Unloaded( Carrier, Cargo ) - end + end + + -- FF added back home event. + function AI_Cargo.OnAfterBackHome( AI_Cargo, Carrier, From, Event, To) + self:BackHome( Carrier ) + end end -- The Pickup sequence ... diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 540d4b064..e115a09dc 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -241,8 +241,11 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To ) end end - if self.RouteDeploy == true then - if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 10 then + if self.RouteDeploy == true then + local height=Helicopter:GetHeight( true ) + local velocity=Helicopter:GetVelocityKMH() + env.info(string.format("FF helo in air %s, height = %d m, velocity = %d km/h", tostring(Helicopter:InAir()), height, velocity)) + if height <= 10 and velocity < 10 then self:Unload( true ) self.RouteDeploy = false self.Transporting = false @@ -823,15 +826,21 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... Helicopter:WayPointInitialize( Route ) - local Tasks = {} - + local Tasks = {} Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() ) - Tasks[#Tasks+1] = Helicopter:TaskFunction("AI_CARGO_HELICOPTER._BackHome", self) - Route[#Route].task = Helicopter:TaskCombo( Tasks ) - - Route[#Route+1] = WaypointTo + -- FF + --[[ + local Tasks2 = {} + Tasks2[#Tasks2+1] = Helicopter:TaskFunction("AI_CARGO_HELICOPTER._BackHome", self) + + Route[#Route+1] = WaypointTo + Route[#Route].task = Helicopter:TaskCombo( Tasks2 ) + -- FF + ]] + + Route[#Route+1] = WaypointTo -- Now route the helicopter Helicopter:Route( Route, 0 ) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 207587aee..4d28f6ebc 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -1078,6 +1078,7 @@ function ZONE_UNIT:GetVec2() local ZoneVec2 = self.ZoneUNIT:GetVec2() if ZoneVec2 then + local heading if self.relative_to_unit then heading = ( self.ZoneUNIT:GetHeading() or 0.0 ) * math.pi / 180.0 else diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 0be2f17a5..c9fca1ca5 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -69,7 +69,7 @@ -- -- The MOOSE warehouse adds a new logistic component to the DCS World. *Assets*, i.e. ground, airborne and naval units, can be transferred from one place -- to another in a realistic and highly automatic fashion. In contrast to a "DCS warehouse" these assets have a physical representation in game. In particular, --- this means they can be destroyed during the transport, add more life to the DCS world etc. +-- this means they can be destroyed during the transport and add more life to the DCS world. -- -- Or, in other words, on a scale between 1 and 10, how important do you reckon it is to have the right assets at the right place in a conflict? -- @@ -417,7 +417,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.2.6" +WAREHOUSE.version="0.2.7" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -503,32 +503,32 @@ function WAREHOUSE:New(warehouse, alias) self.spawnzone=ZONE_RADIUS:New(string.format("Warehouse %s spawn zone", self.warehouse:GetName()), warehouse:GetVec2(), 200) -- Start State. - self:SetStartState("Stopped") + self:SetStartState("NotReadyYet") -- Add FSM transitions. - -- From State --> Event --> To State - self:AddTransition("Stopped", "Load", "Stopped") -- TODO Load the warehouse state. No sure if it should be in stopped state. - self:AddTransition("Stopped", "Start", "Running") -- Start the warehouse. - self:AddTransition("*", "Status", "*") -- Status update. - self:AddTransition("*", "AddAsset", "*") -- Add asset to warehouse stock. - self:AddTransition("*", "AddRequest", "*") -- New request from other warehouse. - self:AddTransition("Running", "Request", "*") -- Process a request. Only in running mode. - self:AddTransition("Attacked", "Request", "*") -- Process a request. Only in running mode. - self:AddTransition("*", "Unloaded", "*") -- Cargo has been unloaded from the carrier. - self:AddTransition("*", "Arrived", "*") -- Cargo group has arrived at destination. - self:AddTransition("*", "Delivered", "*") -- All cargo groups of a request have been delivered to the requesting warehouse. - self:AddTransition("Running", "SelfRequest", "*") -- Request to warehouse itself. Requested assets are only spawned but not delivered anywhere. - self:AddTransition("Attacked", "SelfRequest", "*") -- Request to warehouse itself. Also possible when warehouse is under attack! - self:AddTransition("Running", "Pause", "Paused") -- TODO Pause the processing of new requests. Still possible to add assets and requests. - self:AddTransition("Paused", "Unpause", "Running") -- TODO Unpause the warehouse. Queued requests are processed again. - self:AddTransition("*", "Stop", "Stopped") -- TODO Stop the warehouse. - self:AddTransition("*", "Save", "*") -- TODO Save the warehouse state to disk. - self:AddTransition("*", "Attacked", "Attacked") -- TODO Warehouse is under attack by enemy coalition. - self:AddTransition("Attacked", "Defeated", "Running") -- TODO Attack by other coalition was defeated! - self:AddTransition("Attacked", "Captured", "Running") -- TODO Warehouse was captured by another coalition. It must have been attacked first. - self:AddTransition("*", "AirbaseCaptured", "*") -- TODO Airbase was captured by other coalition. - self:AddTransition("*", "AirbaseRecaptured", "*") -- TODO Airbase was re-captured from other coalition. - self:AddTransition("*", "Destroyed", "*") -- TODO Warehouse was destoryed. All assets in stock are gone and warehouse is stopped. + -- From State --> Event --> To State + self:AddTransition("NotReadyYet", "Load", "NotReadyYet") -- TODO Load the warehouse state. No sure if it should be in stopped state. + self:AddTransition("NotReadyYet", "Start", "Running") -- Start the warehouse. + self:AddTransition("*", "Status", "*") -- Status update. + self:AddTransition("*", "AddAsset", "*") -- Add asset to warehouse stock. + self:AddTransition("*", "AddRequest", "*") -- New request from other warehouse. + self:AddTransition("Running", "Request", "*") -- Process a request. Only in running mode. + self:AddTransition("Attacked", "Request", "*") -- Process a request. Only in running mode. + self:AddTransition("*", "Unloaded", "*") -- Cargo has been unloaded from the carrier. + self:AddTransition("*", "Arrived", "*") -- Cargo or transport group has arrived. + self:AddTransition("*", "Delivered", "*") -- All cargo groups of a request have been delivered to the requesting warehouse. + self:AddTransition("Running", "SelfRequest", "*") -- Request to warehouse itself. Requested assets are only spawned but not delivered anywhere. + self:AddTransition("Attacked", "SelfRequest", "*") -- Request to warehouse itself. Also possible when warehouse is under attack! + self:AddTransition("Running", "Pause", "Paused") -- TODO Pause the processing of new requests. Still possible to add assets and requests. + self:AddTransition("Paused", "Unpause", "Running") -- TODO Unpause the warehouse. Queued requests are processed again. + self:AddTransition("*", "Stop", "Stopped") -- TODO Stop the warehouse. + self:AddTransition("*", "Save", "*") -- TODO Save the warehouse state to disk. + self:AddTransition("*", "Attacked", "Attacked") -- TODO Warehouse is under attack by enemy coalition. + self:AddTransition("Attacked", "Defeated", "Running") -- TODO Attack by other coalition was defeated! + self:AddTransition("Attacked", "Captured", "Running") -- TODO Warehouse was captured by another coalition. It must have been attacked first. + self:AddTransition("*", "AirbaseCaptured", "*") -- TODO Airbase was captured by other coalition. + self:AddTransition("*", "AirbaseRecaptured", "*") -- TODO Airbase was re-captured from other coalition. + self:AddTransition("*", "Destroyed", "*") -- TODO Warehouse was destoryed. All assets in stock are gone and warehouse is stopped. ------------------------ --- Pseudo Functions --- @@ -1356,31 +1356,20 @@ function WAREHOUSE:_SpawnAssetAircraft(asset, request, parking, uncontrolled) else - -- First route point is the warehouse airbase. - template.route.points[1]=self.airbase:GetCoordinate():WaypointAir("BARO", COORDINATE.WaypointType.TakeOffParking, COORDINATE.WaypointAction.FromParkingArea, 0, true, self.airbase, nil, "Spawnpoint") - - --[[ - -- Link. - local spawnpoint=template.route.points[1] + local hotstart=true - -- Set initial waypoint type/action ==> cold start. - spawnpoint.type = COORDINATE.WaypointType.TakeOffParking - spawnpoint.action = COORDINATE.WaypointAction.FromParkingArea + -- Cold start (default). + local _type=COORDINATE.WaypointType.TakeOffParking + local _action=COORDINATE.WaypointAction.FromParkingArea - --spawnpoint.type = COORDINATE.WaypointType.TakeOffParkingHot - --spawnpoint.action = COORDINATE.WaypointAction.FromParkingAreaHot - - -- Set airbase ID. - if AirbaseCategory == Airbase.Category.HELIPAD or AirbaseCategory == Airbase.Category.SHIP then - spawnpoint.helipadId = AirbaseID - spawnpoint.linkUnit = AirbaseID - spawnpoint.airdromeId = nil - elseif AirbaseCategory == Airbase.Category.AIRDROME then - spawnpoint.airdromeId = AirbaseID - spawnpoint.linkUnit = nil - spawnpoint.helipadId = nil + -- Hot start. + if hotstart then + _type=COORDINATE.WaypointType.TakeOffParkingHot + _action=COORDINATE.WaypointAction.FromParkingAreaHot end - ]] + + -- First route point is the warehouse airbase. + template.route.points[1]=self.airbase:GetCoordinate():WaypointAir("BARO",_type,_action, 0, true, self.airbase, nil, "Spawnpoint") end @@ -1963,9 +1952,17 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) --- On after BackHome event. function CargoTransport:OnAfterBackHome(From, Event, To, Carrier) + -- Intellisense. + local carrier=Carrier --Wrapper.Group#GROUP + -- Get warehouse state. - local warehouse=Carrier:GetState(Carrier, "WAREHOUSE") --#WAREHOUSE - Carrier:SmokeRed() + local warehouse=carrier:GetState(carrier, "WAREHOUSE") --#WAREHOUSE + carrier:SmokeWhite() + + -- Debug info. + local text=string.format("Carrier %s is back home at warehouse %s.", tostring(Carrier:GetName()), tostring(warehouse.warehouse:GetName())) + MESSAGE:New(text, 5):ToAllIf(warehouse.Debug) + warehouse:I(warehouse.wid..text) -- Add carrier back to warehouse stock. Actual unit is destroyed. warehouse:AddAsset(Carrier) @@ -2139,7 +2136,7 @@ end -- @param Wrapper.Group#GROUP group The group that was delivered. function WAREHOUSE:onafterArrived(From, Event, To, group) - self:E(self.wid..string.format("Cargo %s arrived!", tostring(group:GetName()))) + self:I(self.wid..string.format("Cargo %s arrived!", tostring(group:GetName()))) group:SmokeOrange() -- Update pending request. @@ -2150,8 +2147,10 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) -- Number of cargo assets still in group set. local ncargo=request.cargogroupset:Count() - -- Info - self:E(self.wid..string.format("Cargo %d of %s arrived at warehouse %s. Assets still to deliver %d.",request.ndelivered, tostring(request.nasset), request.warehouse.alias, ncargo)) + -- Debug message. + local text=string.format("Cargo %d of %s arrived at warehouse %s. Assets still to deliver %d.",request.ndelivered, tostring(request.nasset), request.warehouse.alias, ncargo) + MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Debug) + self:I(self.wid..text) -- Route mobile ground group to the warehouse. Group has 60 seconds to get there or it is despawned and added as asset to the new warehouse regardless. if group:IsGround() and group:GetSpeedMax()>1 then @@ -2229,18 +2228,13 @@ end function WAREHOUSE:onafterDelivered(From, Event, To, request) -- Debug info - self:E(self.wid..string.format("All assets from warehouse %s delivered to warehouse %s!", self.alias, request.warehouse.alias)) - + local text=string.format("Warehouse %s: All assets delivered to warehouse %s!", self.alias, request.warehouse.alias) + MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:I(self.wid..text) + + -- Make some noice :) self:_Fireworks(request.warehouse.coordinate) - --[[ - -- Fireworks! - for i=1,91 do - local color=math.random(0,3) - request.warehouse.coordinate:Flare(color, i-1) - end - ]] - -- Remove pending request: self:_DeleteQueueItem(request, self.pending) @@ -2287,7 +2281,11 @@ end -- @param DCS#coalition.side Coalition which is attacking the warehouse. -- @param DCS#country.id Country which is attacking the warehouse. function WAREHOUSE:onafterAttacked(From, Event, To, Coalition, Country) - self:E(self.wid..string.format("Our warehouse is under attack!")) + + -- Warning. + local text=string.format("Warehouse %s: We are under attack!", self.alias) + MESSAGE:New(text, 20):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:I(self.wid..text) -- Spawn all ground units in the spawnzone? self:AddRequest(self, WAREHOUSE.Descriptor.CATEGORY, Group.Category.GROUND, "all", nil, nil , 0) @@ -2299,7 +2297,11 @@ end -- @param #string Event Event. -- @param #string To To state. function WAREHOUSE:onafterDefeated(From, Event, To) - self:E(self.wid..string.format("Attack was defeated!")) + + -- Message. + local text=string.format("Warehouse %s: Enemy attack was defeated!", self.alias) + MESSAGE:New(text, 20):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:I(self.wid..text) --if self.defenderrequest then for _,request in pairs(self.defending) do @@ -2339,7 +2341,11 @@ end -- @param DCS#coalition.side Coalition which captured the warehouse. -- @param DCS#country.id Country which has captured the warehouse. function WAREHOUSE:onafterCaptured(From, Event, To, Coalition, Country) - self:E(self.wid..string.format("Our warehouse was captured by coalition %d!", Coalition)) + + -- Message. + local text=string.format("Warehouse %s: We were captured by enemy coalition (%d)!", self.alias, Coalition) + MESSAGE:New(text, 20):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:I(self.wid..text) -- Respawn warehouse with new coalition/country. self.warehouse:ReSpawn(Country) @@ -2379,8 +2385,16 @@ end -- @param #string To To state. -- @param DCS#coalition.side Coalition which captured the warehouse. function WAREHOUSE:onafterAirbaseCaptured(From, Event, To, Coalition) - self:E(self.wid..string.format("Our airbase %s was captured by coalition %d!", self.airbasename, Coalition)) + + -- Message. + local text=string.format("Warehouse %s: Our airbase %s was captured by the enemy (coalition=%d)!", self.alias, self.airbasename, Coalition) + MESSAGE:New(text, 20):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:I(self.wid..text) + + -- Debug smoke. self.airbase:GetCoordinate():SmokeRed() + + -- Set airbase to nil and category to no airbase. self.airbase=nil self.category=-1 -- -1 indicates no airbase. end @@ -2392,9 +2406,17 @@ end -- @param #string To To state. -- @param DCS#coalition.side Coalition which captured the warehouse. function WAREHOUSE:onafterAirbaseRecaptured(From, Event, To, Coalition) - self:E(self.wid..string.format("We re-capturd our airbase %s from coalition %d!", self.airbasename, Coalition)) + + -- Message. + local text=string.format("Warehouse %s: We recaptured our airbase %d from the enemy (coalition=%d)!", self.alias, self.airbasename, Coalition) + MESSAGE:New(text, 20):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:I(self.wid..text) + + -- Set airbase and category. self.airbase=AIRBASE:FindByName(self.airbasename) self.category=self.airbase:GetDesc().category + + -- Debug smoke. self.airbase:GetCoordinate():SmokeGreen() end @@ -2405,7 +2427,12 @@ end -- @param #string Event Event. -- @param #string To To state. function WAREHOUSE:onafterDestroyed(From, Event, To) - self:E(self.wid..string.format("Our warehouse was destroyed!")) + + -- Message. + local text=string.format("Warehouse %s was destroyed!", self.alias) + MESSAGE:New(text, 20):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:I(self.wid..text) + -- Stop warehouse FSM. self:Stop() end @@ -2563,7 +2590,9 @@ function WAREHOUSE:_OnEventArrived(EventData) if wid~=nil and aid~=nil and rid~=nil then -- Debug info. - self:E(self.wid..string.format("Air asset group %s arrived.", group:GetName())) + local text=string.format("Air asset group %s arrived at warehouse %s.", group:GetName(), self.alias) + --MESSAGE:New + self:E(self.wid..text) -- Trigger arrived event for this group. Note that each unit of a group will trigger this event. So the onafterArrived function needs to take care of that. -- Actually, we only take the first unit of the group that arrives. If it does, we assume the whole group arrived, which might not be the case, since @@ -2640,6 +2669,24 @@ function WAREHOUSE:_OnEventLanding(EventData) local wid,aid,rid=self:_GetIDsFromGroup(group) if wid==self.uid then self:E(self.wid..string.format("Warehouse %s captured event landing of its asset unit %s.", self.alias, EventData.IniUnitName)) + + -- Get request of this group + local request=self:_GetRequestOfGroup(group,self.pending) + + -- If request is nil, the cargo has been delivered. + -- TODO: I might need to add a delivered table, to be better able to get this right. + if request==nil then + + -- Check if helicopter landed in spawn zone. If so, we call it a day and add it back to stock. + if group:GetCategory()==Group.Category.HELICOPTER then + if self.spawnzone:IsCoordinateInZone(EventData.IniUnit:GetCoordinate()) then + group:SmokeWhite() + self:__AddAsset(30, group) + end + end + + end + end end end @@ -2772,7 +2819,7 @@ function WAREHOUSE:_CheckConquered() local _country=unit:GetCountry() -- Debug info. - self:E(self.wid..string.format("Unit %s in warehouse zone of radius=%d m. Coalition=%d, country=%d. Distance = %d m.",unit:GetName(), radius,_coalition,_country, distance)) + self:T2(self.wid..string.format("Unit %s in warehouse zone of radius=%d m. Coalition=%d, country=%d. Distance = %d m.",unit:GetName(), radius,_coalition,_country, distance)) -- Add up units for each side. if _coalition==coalition.side.BLUE then @@ -2791,7 +2838,7 @@ function WAREHOUSE:_CheckConquered() end -- Debug info. - self:E(self.wid..string.format("Ground troops in warehouse zone: blue=%d, red=%d, neutral=%d", Nblue, Nred, Nneutral)) + self:T(self.wid..string.format("Ground troops in warehouse zone: blue=%d, red=%d, neutral=%d", Nblue, Nred, Nneutral)) -- Figure out the new coalition if any. @@ -2911,7 +2958,8 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) valid=false end - -- Is receiving warehouse stopped? + -- Is receiving warehouse stopped? Actually, running state is checked later. So I leave this for now in case the warehouse was not started yet. + -- TODO: Introduce another FSM start state to be able to distinguish between a warehouse that was not started yet or was started and then stopped! if request.warehouse:IsStopped() then self:E(self.wid..string.format("ERROR: Incorrect request. Requesting warehouse is stopped!")) valid=false @@ -3031,7 +3079,7 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) -- TODO: May needs refinement if warehouse is on land and requestor is ship in harbour?! if (request.category==Airbase.Category.SHIP or self.category==Airbase.Category.SHIP) then self:E("ERROR: Incorrect request. Ground asset requested but warehouse or requestor is SHIP!") - valid=false + --valid=false end if asset_train then @@ -3042,11 +3090,13 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) valid=false end else - -- Check if there is a valid path on road. - local hasroad=self:HasConnectionRoad(request.warehouse) - if not hasroad then - self:E("ERROR: Incorrect request. No valid path on road for ground assets!") - valid=false + if self.warehouse:GetName()~=request.warehouse.warehouse:GetName() then + -- Check if there is a valid path on road. + local hasroad=self:HasConnectionRoad(request.warehouse) + if not hasroad then + self:E("ERROR: Incorrect request. No valid path on road for ground assets!") + valid=false + end end end elseif asset_naval then diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index b607b4643..d5bff0bc0 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1605,7 +1605,7 @@ end -- RouteToZone( GroundGroup, ZoneList[1] ) -- function CONTROLLABLE:TaskFunction( FunctionString, ... ) - self:F2( { FunctionString, arg } ) + self:E({TaskFunction=FunctionString, arguments=arg}) local DCSTask @@ -1616,17 +1616,12 @@ function CONTROLLABLE:TaskFunction( FunctionString, ... ) local ArgumentKey = '_' .. tostring( arg ):match("table: (.*)") self:SetState( self, ArgumentKey, arg ) DCSScript[#DCSScript+1] = "local Arguments = MissionControllable:GetState( MissionControllable, '" .. ArgumentKey .. "' ) " - --DCSScript[#DCSScript+1] = "MissionControllable:ClearState( MissionControllable, '" .. ArgumentKey .. "' ) " DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable, unpack( Arguments ) )" else DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable )" end - DCSTask = self:TaskWrappedAction( - self:CommandDoScript( - table.concat( DCSScript ) - ) - ) + DCSTask = self:TaskWrappedAction(self:CommandDoScript(table.concat( DCSScript ))) self:T( DCSTask ) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 8b7f34a26..2e088d308 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1606,8 +1606,7 @@ end --- Returns true if the first unit of the GROUP is in the air. -- @param Wrapper.Group#GROUP self --- @return #boolean true if in the first unit of the group is in the air. --- @return #nil The GROUP is not existing or not alive. +-- @return #boolean true if in the first unit of the group is in the air or #nil if the GROUP is not existing or not alive. function GROUP:InAir() self:F2( self.GroupName ) From a9a040626ef0cd4126a02132506f06829233a63a Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 27 Aug 2018 16:46:46 +0200 Subject: [PATCH 285/420] Warehouse 0.2.7w --- .../Moose/Functional/Warehouse.lua | 390 +++++++++++++++--- 1 file changed, 329 insertions(+), 61 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index c9fca1ca5..e07e84a40 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -45,6 +45,8 @@ -- @field #table queue Table holding all queued requests. Table entries are of type @{#WAREHOUSE.Queueitem}. -- @field #table pending Table holding all pending requests, i.e. those that are currently in progress. Table elements are of type @{#WAREHOUSE.Pendingitem}. -- @field #table defending Table holding all defending requests, i.e. self requests that were if the warehouse is under attack. Table elements are of type @{#WAREHOUSE.Pendingitem}. +-- @field Core.Zone#ZONE portzone Zone defining the port of a warehouse. This is where naval assets are spawned. +-- @field #table shippinglanes Table holding the user defined shipping between warehouses. -- @extends Core.Fsm#FSM --- Manages ground assets of an airbase and offers the possibility to transport them to another airbase or warehouse. @@ -71,10 +73,6 @@ -- to another in a realistic and highly automatic fashion. In contrast to a "DCS warehouse" these assets have a physical representation in game. In particular, -- this means they can be destroyed during the transport and add more life to the DCS world. -- --- Or, in other words, on a scale between 1 and 10, how important do you reckon it is to have the right assets at the right place in a conflict? --- --- The warehouse adresses exactly this "problem" by simulating it in a realistic way while at the same time adding more life and dynamics to the DCS World. --- -- This comes along with some additional interesting stategic aspects since capturing/defending and destroying/protecting an enemy or your -- own warehous becomes of critical importance for the development of a conflict. -- @@ -83,23 +81,22 @@ -- -- ## What is a warehouse? -- A warehouse is an abstract object represented by a physical (static) building that can hold virtual assets in stock. --- It can but it must not be associated with a particular airbase. The associated airbase can be an airdrome, a Helipad/FARP or a ship. +-- It can (but it must not) be associated with a particular airbase. The associated airbase can be an airdrome, a Helipad/FARP or a ship. -- -- If another warehouse requests assets, the corresponding troops are spawned at the warehouse and being transported to the requestor or go their -- by themselfs. Once arrived at the requesting warehouse, the assets go into the stock of the requestor and can be activated/deployed when necessary. -- -- ## What assets can be stored? --- Any kind of ground or airborne asset can be stored. Ships not supported at the moment due to the fact that airbases are bound to airbases which are --- normally not located near the sea. +-- Any kind of ground, airborne or naval asset can be stored. -- -- ## What means of transportation are available? -- Firstly, all mobile assets can be send from warehouse to another on their own. -- --- * Ground vehicles will use the road infrastructure --- * Airborne units are get a flightplan from the airbase the sending warehouse to the airbase of the receiving warehouse. This already implies that for airborne --- assets both warehouses need an airbase. If either one of the warehouses does not have an associated airbase, transportation of airborne assest is not possible. +-- * Ground vehicles will use the road infrastructure. So a good road connection for both warehouses is important. +-- * Airborne units get a flightplan from the airbase of the sending warehouse to the airbase of the receiving warehouse. This already implies that for airborne +-- assets both warehouses need an airbase. If either one of the warehouses does not have an associated airbase, direct transportation of airborne assest is not possible. -- * Naval units can be exchanged between warehouses which posses a port/habour. Also shipping lanes must be specified manually but the user since DCS does not provide these. --- * Trains use the available railroad infrastructure and both warehouses must have a connection to the railroad. Unfortunately, however, trains are not yet implemented to +-- * Trains (would) use the available railroad infrastructure and both warehouses must have a connection to the railroad. Unfortunately, however, trains are not yet implemented to -- a reasonable degree in DCS at the moment and hence cannot be used yet. -- -- Furthermore, ground assets can be transferred between warehouses by transport units. These are APCs, helicopters and airplanes. The transportation process is modelled @@ -111,7 +108,7 @@ -- A MOOSE warehouse must be represented in game by a phyical *static* object. For example, the mission editor already has warehouse as static object available. -- This would be a good first choice but any static object will do. -- --- The positioning of the warehouse static object is very important for a couple of reasons. Firtly, a warehouse needs a good infrastructure so that spawned assets +-- The positioning of the warehouse static object is very important for a couple of reasons. Firstly, a warehouse needs a good infrastructure so that spawned assets -- have a proper road connection or can reach the associated airbase easily. -- -- Once the static warehouse object is placed in the mission editor it can be used as a MOOSE warehouse by the @{#WAREHOUSE.New}(*warehousestatic*, *alias*) constructor, @@ -138,7 +135,8 @@ -- Note that you can also add assets with a delay by using the @{#WAREHOUSE.__AddAsset}(*delay*, *group*, *ngroups*, *foceattribute*), where *delay* is the delay in seconds before the asset is added. -- -- By default, the generalized attribute of the asset is determined automatically from the DCS descriptor attributes. However, this might not always result in the desired outcome. --- Therefore, it is possible, to force a generalized attribute for the asset with the third optional parameter *forceattribute*, which is of type @{#WAREHOUSE.Attribute}. +-- Therefore, it is possible, to force a generalized attribute for the asset with the third optional parameter *forceattribute*, which is of type @{#WAREHOUSE.Attribute}. +-- -- -- # Requesting Assets -- @@ -223,7 +221,7 @@ -- end -- -- The variable *groupset* is a @{Core.Set#SET_GOUP} object and holds all asset groups from the request. The code above shows, how the mission designer can access the groups --- for further tasking. Here, the groups are only smoked but, of course, you can use them for whatever task you imagine. +-- for further tasking. Here, the groups are only smoked but, of course, you can use them for whatever task you fancy. -- -- Note that airborne groups are spawned in uncontrolled state and need to be activated first before they can start their assigned mission. -- @@ -300,6 +298,8 @@ WAREHOUSE = { queue = {}, pending = {}, defending = {}, + portzone = nil, + shippinglanes = {}, } --- Item of the warehouse stock table. @@ -315,6 +315,7 @@ WAREHOUSE = { -- @field #number speedmax Maximum speed in km/h the unit can do. -- @field DCS#Object.Desc DCSdesc All DCS descriptors. -- @field #WAREHOUSE.Attribute attribute Generalized attribute of the group. +-- @field #boolean istransport If true, the asset is able to transport troops. --- Item of the warehouse queue table. -- @type WAREHOUSE.Queueitem @@ -360,7 +361,8 @@ WAREHOUSE.Descriptor = { -- @field #string TRANSPORT_PLANE Airplane with transport capability. Usually bigger, i.e. needs larger airbases and parking spots. -- @field #string TRANSPORT_HELO Helicopter with transport capability. -- @field #string TRANSPORT_APC Amoured Personell Carrier. --- @field #string FIGHER Fighter, interceptor, ... airplane. +-- @field #string TRANSPORT_SHIP Ship defined for troop transport. Since most ships can, this attribute must be manually requestet in the AddAddet() function. +-- @field #string FIGHTER Fighter, interceptor, ... airplane. -- @field #string TANKER Airplane which can refuel other aircraft. -- @field #string AWACS Airborne Early Warning and Control System. -- @field #string ARTILLERY Artillery assets. @@ -368,13 +370,16 @@ WAREHOUSE.Descriptor = { -- @field #string BOMBER Aircraft which can be used for bombing. -- @field #string TANK Tanks. -- @field #string TRUCK Unarmed ground vehicles. --- @field #string TRAIN Trains. --- @field #string SHIP Naval assets. +-- @field #string TRAIN Trains. Not that trains are **not** yet properly implemented in DCS and cannot be used currently. +-- @field #string AIRCRAFTCARRIER Ship able to carrier aircraft. +-- @field #string WARSHIP Armed war ship, e.g. cruisers, destroyers, firgates and corvettes. +-- @filed #string UNARMED_SHIP Any unarmed naval vessel. -- @field #string OTHER Anything that does not fall into any other category. WAREHOUSE.Attribute = { TRANSPORT_PLANE="Transport_Plane", TRANSPORT_HELO="Transport_Helo", TRANSPORT_APC="Transport_APC", + TRANSPORT_SHIP="Transport_Ship", FIGHTER="Fighter", TANKER="Tanker", AWACS="AWACS", @@ -385,7 +390,9 @@ WAREHOUSE.Attribute = { TANK="Tank", TRUCK="Truck", TRAIN="Train", - SHIP="Ship", + AIRCRAFTCARRIER="Aircraftcarrier", + WAR_SHIP="Warship", + UNARMED_SHIP="Unarmedship", OTHER="Other", } @@ -395,7 +402,7 @@ WAREHOUSE.Attribute = { -- @field #string HELICOPTER Transports are conducted by helicopters. -- @field #string APC Transports are conducted by APCs. -- @field #string SHIP Transports are conducted by ships. --- @field #string TRAIN Transports are conducted by trains. +-- @field #string TRAIN Transports are conducted by trains. Not yet implemented. -- @field #string SELFPROPELLED Assets go to their destination by themselves. No transport carrier needed. WAREHOUSE.TransportType = { AIRPLANE = "Transport_Plane", @@ -417,7 +424,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.2.7" +WAREHOUSE.version="0.2.7w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -834,6 +841,78 @@ function WAREHOUSE:SetRailConnection(coordinate) return self end +--- Set the port zone for this warehouse. +-- The port zone is the zone, where all naval assets of the warehouse are spawned. +-- @param #WAREHOUSE self +-- @param Core.Zone#ZONE The zone defining the naval port of the warehouse. +-- @return #WAREHOUSE self +function WAREHOUSE:SetPortZone(zone) + self.portzone=zone + return self +end + +--- Add a shipping lane to another warehouse. +-- Note that both warehouses must have a port zone defined before a shipping lane can be added. +-- Shipping lane is taken from the waypoints of a (late activated) template group. So set up a group, e.g. a ship or a helicopter, and place its +-- waypoints along the shipping lane you want to add. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE remotewarehouse The remote warehouse to where the shipping lane is added +-- @param Wrapper.Group#GROUP group Waypoints of this group will define the shipping lane between to warehouses. +-- @return #WAREHOUSE self +function WAREHOUSE:AddShippingLane(remotewarehouse, group) + + -- Check that port zones are defined. + if self.portzone==nil or remotewarehouse.portzone==nil then + self:E(self.wid..string.format("ERROR: Sending or receiving warehouse does not have a port zone defined. Adding shipping lane not possible!")) + return + end + + -- Get route from template. + local lanepoints=group:GetTemplateRoutePoints() + + -- First and last waypoints + local laneF=lanepoints[1] + local laneL=lanepoints[#lanepoints] + + -- Get corresponding coordinates. + local coordF=COORDINATE:New(laneF.x, 0, laneF.y) + local coordL=COORDINATE:New(laneL.x, 0, laneL.y) + + -- Figure out which point is closer to the port of this warehouse. + local distF=self.portzone:GetCoordinate():Get2DDistance(coordF) + local distL=self.portzone:GetCoordinate():Get2DDistance(coordL) + + -- Add the shipping lane. Need to take care of the wrong "direction". + local lane={} + lane.towarehouse=remotewarehouse.warehouse:GetName() + lane.coordinates={} + if distF need airbase for spawning but not for delivering to the spawn zone of the receiver. + if self.category==-1 then + self:E("ERROR: Incorrect request. Warehouse has no airbase. Transport by helicopter not possible!") + valid=false + end + + elseif request.transporttype==WAREHOUSE.TransportType.SHIP then + + -- Transport by ship. + self:E("ERROR: Incorrect request. Transport by SHIP not implemented yet!") + valid=false + + elseif request.transporttype==WAREHOUSE.TransportType.TRAIN then + + -- Transport by train. + self:E("ERROR: Incorrect request. Transport by TRAIN not implemented yet!") + valid=false + + else + -- No match. + self:E("ERROR: Incorrect request. Transport type unknown!") + valid=false + end + + end + + -- Add request as unvalid and delete it later. + if valid==false then + self:E(self.wid..string.format("Got invalid request id=%d.", request.uid)) + else + self:T3(self.wid..string.format("Got valid request id=%d.", request.uid)) + end + + return valid +end + + --- Checks if the request can be fullfilled right now. -- Check for current parking situation, number of assets and transports currently in stock. -- @param #WAREHOUSE self @@ -3285,14 +3533,36 @@ function WAREHOUSE:_CheckQueue() -- Search for a request we can execute. local request=nil --#WAREHOUSE.Queueitem + + local invalid={} + local gotit=false for _,_qitem in ipairs(self.queue) do local qitem=_qitem --#WAREHOUSE.Queueitem + + -- Check if request is valid in general. + local valid=self:_CheckRequestValid(qitem) + + -- Check if request is possible now. local okay=self:_CheckRequestNow(qitem) - if okay==true then + + -- Remember invalid request and delete later in order not to confuse the loop. + if not valid then + table.insert(invalid,request) + end + + -- Get the first valid request that can be executed now. + if okay and valid and not gotit then request=qitem - break + gotit=true + --break end end + + -- Delete invalid requests. + for _,_request in pairs(invalid) do + self:E(self.wid..string.format("Deleting invalid request id=%d.",_request.uid)) + self:_DeleteQueueItem(_request, self.queue) + end -- Execute request. return request @@ -3685,7 +3955,7 @@ function WAREHOUSE:_HasAttribute(groupname, attribute) local group=GROUP:FindByName(groupname) if group then - local groupattribute=self:_HasAttribute(groupname,attribute) + local groupattribute=self:_GetAttribute(groupname) return groupattribute==attribute end @@ -3707,35 +3977,33 @@ function WAREHOUSE:_GetAttribute(groupname) -- Get generalized attributes. -- TODO: need to work on ships and trucks and SAMs and ... -- Also the Yak-52 for example is OTHER since it only has the attribute "Battleplanes". + + -- Airplanes local transportplane=group:HasAttribute("Transports") and group:HasAttribute("Planes") - local transporthelo=group:HasAttribute("Transport helicopters") - local transportapc=group:HasAttribute("Infantry carriers") local fighter=group:HasAttribute("Fighters") or group:HasAttribute("Interceptors") or group:HasAttribute("Multirole fighters") local tanker=group:HasAttribute("Tankers") local awacs=group:HasAttribute("AWACS") - local artillery=group:HasAttribute("Artillery") - local infantry=group:HasAttribute("Infantry") - local attackhelicopter=group:HasAttribute("Attack helicopters") local bomber=group:HasAttribute("Bombers") + + -- Helicopters + local transporthelo=group:HasAttribute("Transport helicopters") + local attackhelicopter=group:HasAttribute("Attack helicopters") + + -- Ground + local transportapc=group:HasAttribute("Infantry carriers") + local artillery=group:HasAttribute("Artillery") + local infantry=group:HasAttribute("Infantry") local tank=group:HasAttribute("Old Tanks") or group:HasAttribute("Modern Tanks") local truck=group:HasAttribute("Trucks") and not group:GetCategory()==Group.Category.TRAIN + + -- Naval + local aircraftcarrier=group:HasAttribute("Aircraft Carriers") + local warship=group:HasAttribute("Armed ships") + local ship=group:HasAttribute("Ships") + + -- Train local train=group:GetCategory()==Group.Category.TRAIN - -- Debug output. - --[[ - env.info(string.format("transport pane = %s", tostring(transportplane))) - env.info(string.format("transport helo = %s", tostring(transporthelo))) - env.info(string.format("transport apc = %s", tostring(transportapc))) - env.info(string.format("figther = %s", tostring(fighter))) - env.info(string.format("tanker = %s", tostring(tanker))) - env.info(string.format("awacs = %s", tostring(awacs))) - env.info(string.format("artillery = %s", tostring(artillery))) - env.info(string.format("infantry = %s", tostring(infantry))) - env.info(string.format("attack helo = %s", tostring(attackhelicopter))) - env.info(string.format("bomber = %s", tostring(bomber))) - env.info(string.format("tank = %s", tostring(tank))) - env.info(string.format("truck = %s", tostring(truck))) - ]] if transportplane then attribute=WAREHOUSE.Attribute.TRANSPORT_PLANE From 8e16fbd000e4a9cb7ff7b74521a73d20aa2d5b92 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Tue, 28 Aug 2018 00:34:26 +0200 Subject: [PATCH 286/420] Warehouse v0.2.8 - Added first version of naval assets (self propelled). - Changed WAREHOUSE.Attribute --- .../Moose/Functional/Warehouse.lua | 554 ++++++++---------- 1 file changed, 229 insertions(+), 325 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index e07e84a40..f188ff3ea 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -358,42 +358,49 @@ WAREHOUSE.Descriptor = { --- Generalized asset attributes. Can be used to request assets with certain general characteristics. -- @type WAREHOUSE.Attribute --- @field #string TRANSPORT_PLANE Airplane with transport capability. Usually bigger, i.e. needs larger airbases and parking spots. --- @field #string TRANSPORT_HELO Helicopter with transport capability. --- @field #string TRANSPORT_APC Amoured Personell Carrier. --- @field #string TRANSPORT_SHIP Ship defined for troop transport. Since most ships can, this attribute must be manually requestet in the AddAddet() function. --- @field #string FIGHTER Fighter, interceptor, ... airplane. --- @field #string TANKER Airplane which can refuel other aircraft. --- @field #string AWACS Airborne Early Warning and Control System. --- @field #string ARTILLERY Artillery assets. --- @field #string INFANTRY Ground infantry assets. --- @field #string BOMBER Aircraft which can be used for bombing. --- @field #string TANK Tanks. --- @field #string TRUCK Unarmed ground vehicles. --- @field #string TRAIN Trains. Not that trains are **not** yet properly implemented in DCS and cannot be used currently. --- @field #string AIRCRAFTCARRIER Ship able to carrier aircraft. --- @field #string WARSHIP Armed war ship, e.g. cruisers, destroyers, firgates and corvettes. --- @filed #string UNARMED_SHIP Any unarmed naval vessel. --- @field #string OTHER Anything that does not fall into any other category. +-- @field #string AIR_TRANSPORTPLANE Airplane with transport capability. This can be used to transport other assets. +-- @field #string AIR_AWACS Airborne Early Warning and Control System. +-- @field #string AIR_FIGHTER Fighter, interceptor, ... airplane. +-- @field #string AIR_BOMBER Aircraft which can be used for strategic bombing. +-- @field #string AIR_TANKER Airplane which can refuel other aircraft. +-- @field #string AIR_TRANSPORTHELO Helicopter with transport capability. This can be used to transport other assets. +-- @field #string AIR_ATTACKHELO Attack helicopter. +-- @field #string AIR_OTHER Any airborne unit that does not fall into any other airborne category. +-- @field #string GROUND_APC Infantry carriers, in particular Amoured Personell Carrier. This can be used to transport other assets. +-- @field #string GROUND_TRUCK Unarmed ground vehicles. +-- @field #string GROUND_INFANTRY Ground infantry assets. +-- @field #string GROUND_ARTILLERY Artillery assets. +-- @field #string GROUND_TANK Tanks (modern or old). +-- @field #string GROUND_TRAIN Trains. Not that trains are **not** yet properly implemented in DCS and cannot be used currently. +-- @field #string GROUND_OTHER Any ground unit that does not fall into any other ground category. +-- @field #string NAVAL_AIRCRAFTCARRIER Aircraft carrier. +-- @field #string NAVAL_WARSHIP War ship, i.e. cruisers, destroyers, firgates and corvettes. +-- @field #string NAVAL_ARMEDSHIP Any armed ship that is not an aircraft carrier, a cruiser, destroyer, firgatte or corvette. +-- @field #string NAVAL_UNARMEDSHIP Any unarmed naval vessel. +-- @field #string NAVAL_OTHER Any naval unit that does not fall into any other naval category. +-- @field #string UNKNOWN Anything that does not fall into any other category. WAREHOUSE.Attribute = { - TRANSPORT_PLANE="Transport_Plane", - TRANSPORT_HELO="Transport_Helo", - TRANSPORT_APC="Transport_APC", - TRANSPORT_SHIP="Transport_Ship", - FIGHTER="Fighter", - TANKER="Tanker", - AWACS="AWACS", - ARTILLERY="Artillery", - ATTACKHELICOPTER="Attackhelicopter", - INFANTRY="Infantry", - BOMBER="Bomber", - TANK="Tank", - TRUCK="Truck", - TRAIN="Train", - AIRCRAFTCARRIER="Aircraftcarrier", - WAR_SHIP="Warship", - UNARMED_SHIP="Unarmedship", - OTHER="Other", + AIR_TRANSPORTPLANE="Air_TransportPlane", + AIR_AWACS="Air_AWACS", + AIR_FIGHTER="Air_Fighter", + AIR_BOMBER="Air_Bomber", + AIR_TANKER="Air_Tanker", + AIR_TRANSPORTHELO="Air_TransportHelo", + AIR_ATTACKHELO="Air_AttackHelo", + AIR_OTHER="Air_Other", + GROUND_APC="Ground_APC", + GROUND_TRUCK="Ground_Truck", + GROUND_INFANTRY="Ground_Infantry", + GROUND_ARTILLERY="Ground_Artillery", + GROUND_TANK="Ground_Tank", + GROUND_TRAIN="Ground_Train", + GROUND_OTHER="Ground_Other", + NAVAL_AIRCRAFTCARRIER="Naval_AircraftCarrier", + NAVAL_WARSHIP="Naval_WarShip", + NAVAL_ARMEDSHIP="Naval_ArmedShip", + NAVAL_UNARMEDSHIP="Naval_UnarmedShip", + NAVAL_OTHER="Naval_Other", + UNKNOWN="Unknown", } --- Cargo transport type. Defines how assets are transported to their destination. @@ -405,11 +412,11 @@ WAREHOUSE.Attribute = { -- @field #string TRAIN Transports are conducted by trains. Not yet implemented. -- @field #string SELFPROPELLED Assets go to their destination by themselves. No transport carrier needed. WAREHOUSE.TransportType = { - AIRPLANE = "Transport_Plane", - HELICOPTER = "Transport_Helo", - APC = "Transport_APC", - SHIP = "Ship", - TRAIN = "Train", + AIRPLANE = "Air_TransportPlane", + HELICOPTER = "Air_TransportHelo", + APC = "Ground_APC", + TRAIN = "Ground_Train", + SHIP = "Naval_UnarmedShip", SELFPROPELLED = "Selfpropelled", } @@ -424,7 +431,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.2.7w" +WAREHOUSE.version="0.2.8" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -844,7 +851,7 @@ end --- Set the port zone for this warehouse. -- The port zone is the zone, where all naval assets of the warehouse are spawned. -- @param #WAREHOUSE self --- @param Core.Zone#ZONE The zone defining the naval port of the warehouse. +-- @param Core.Zone#ZONE zone The zone defining the naval port of the warehouse. -- @return #WAREHOUSE self function WAREHOUSE:SetPortZone(zone) self.portzone=zone @@ -884,31 +891,32 @@ function WAREHOUSE:AddShippingLane(remotewarehouse, group) -- Add the shipping lane. Need to take care of the wrong "direction". local lane={} - lane.towarehouse=remotewarehouse.warehouse:GetName() - lane.coordinates={} + --lane.towarehouse=remotewarehouse.warehouse:GetName() + --lane.coordinates={} if distF need airbase for spawning but not for delivering to the spawn zone of the receiver. - if self.category==-1 then - self:E("ERROR: Incorrect request. Warehouse has no airbase. Transport by helicopter not possible!") - valid=false - end - - elseif request.transporttype==WAREHOUSE.TransportType.SHIP then - - -- Transport by ship. - self:E("ERROR: Incorrect request. Transport by SHIP not implemented yet!") - valid=false - - elseif request.transporttype==WAREHOUSE.TransportType.TRAIN then - - -- Transport by train. - self:E("ERROR: Incorrect request. Transport by TRAIN not implemented yet!") - valid=false - - else - -- No match. - self:E("ERROR: Incorrect request. Transport type unknown!") - valid=false - end - - end - - -- Add request as unvalid and delete it later. - if valid==false then - self:E(self.wid..string.format("Got invalid request id=%d.", request.uid)) - table.insert(invalid, request) - else - self:T3(self.wid..string.format("Got valid request id=%d.", request.uid)) - end - - end -- loop queue items. - + end end --- Check if a request is valid in general. If not, it will be removed from the queue. @@ -3339,13 +3215,16 @@ function WAREHOUSE:_CheckRequestValid(request) end if asset_train then + -- Check if there is a valid path on rail. local hasrail=self:HasConnectionRail(request.warehouse) if not hasrail then self:E("ERROR: Incorrect request. No valid path on rail for train assets!") valid=false end + else + if self.warehouse:GetName()~=request.warehouse.warehouse:GetName() then -- Check if there is a valid path on road. local hasroad=self:HasConnectionRoad(request.warehouse) @@ -3354,11 +3233,17 @@ function WAREHOUSE:_CheckRequestValid(request) valid=false end end - end + + end + elseif asset_naval then - self:E("ERROR: Incorrect request. Naval units not supported yet!") - valid=false + local shippinglane=self:HasConnectionNaval(request.warehouse) + + if not shippinglane then + self:E("ERROR: Incorrect request. No shipping lane has been defined between warehouses!") + valid=false + end end @@ -3597,14 +3482,17 @@ end --@return Wrapper.Airbase#AIRBASE.TerminalType Terminal type for this group. function WAREHOUSE:_GetTerminal(_attribute) + -- Default terminal is "large". local _terminal=AIRBASE.TerminalType.OpenBig - if _attribute==WAREHOUSE.Attribute.FIGHTER then + + + if _attribute==WAREHOUSE.Attribute.AIR_FIGHTER then -- Fighter ==> small. _terminal=AIRBASE.TerminalType.FighterAircraft - elseif _attribute==WAREHOUSE.Attribute.BOMBER or _attribute==WAREHOUSE.Attribute.TRANSPORT_PLANE or _attribute==WAREHOUSE.Attribute.TANKER or _attribute==WAREHOUSE.Attribute.AWACS then + elseif _attribute==WAREHOUSE.Attribute.AIR_BOMBER or _attribute==WAREHOUSE.Attribute.AIR_TRANSPORTPLANE or _attribute==WAREHOUSE.Attribute.AIR_TANKER or _attribute==WAREHOUSE.Attribute.AIR_AWACS then -- Bigger aircraft. _terminal=AIRBASE.TerminalType.OpenBig - elseif _attribute==WAREHOUSE.Attribute.TRANSPORT_HELO or _attribute==WAREHOUSE.Attribute.ATTACKHELICOPTER then + elseif _attribute==WAREHOUSE.Attribute.AIR_TRANSPORTHELO or _attribute==WAREHOUSE.Attribute.AIR_ATTACKHELO then -- Helicopter. _terminal=AIRBASE.TerminalType.HelicopterUsable end @@ -3963,6 +3851,7 @@ function WAREHOUSE:_HasAttribute(groupname, attribute) end --- Get the generalized attribute of a group. +-- Note that for a heterogenious group, the attribute is determined from the attribute of the first unit! -- @param #WAREHOUSE self -- @param #string groupname Name of the group. -- @return #WAREHOUSE.Attribute Generalized attribute of the group. @@ -3978,61 +3867,76 @@ function WAREHOUSE:_GetAttribute(groupname) -- TODO: need to work on ships and trucks and SAMs and ... -- Also the Yak-52 for example is OTHER since it only has the attribute "Battleplanes". - -- Airplanes + ----------- + --- Air --- + ----------- + -- Planes local transportplane=group:HasAttribute("Transports") and group:HasAttribute("Planes") - local fighter=group:HasAttribute("Fighters") or group:HasAttribute("Interceptors") or group:HasAttribute("Multirole fighters") - local tanker=group:HasAttribute("Tankers") local awacs=group:HasAttribute("AWACS") + local fighter=group:HasAttribute("Fighters") or group:HasAttribute("Interceptors") or group:HasAttribute("Multirole fighters") local bomber=group:HasAttribute("Bombers") - + local tanker=group:HasAttribute("Tankers") -- Helicopters local transporthelo=group:HasAttribute("Transport helicopters") local attackhelicopter=group:HasAttribute("Attack helicopters") - + + -------------- + --- Ground --- + -------------- -- Ground - local transportapc=group:HasAttribute("Infantry carriers") - local artillery=group:HasAttribute("Artillery") - local infantry=group:HasAttribute("Infantry") - local tank=group:HasAttribute("Old Tanks") or group:HasAttribute("Modern Tanks") + local apc=group:HasAttribute("Infantry carriers") local truck=group:HasAttribute("Trucks") and not group:GetCategory()==Group.Category.TRAIN - - -- Naval - local aircraftcarrier=group:HasAttribute("Aircraft Carriers") - local warship=group:HasAttribute("Armed ships") - local ship=group:HasAttribute("Ships") - + local infantry=group:HasAttribute("Infantry") + local artillery=group:HasAttribute("Artillery") + local tank=group:HasAttribute("Old Tanks") or group:HasAttribute("Modern Tanks") -- Train local train=group:GetCategory()==Group.Category.TRAIN + ------------- + --- Naval --- + ------------- + -- Ships + local aircraftcarrier=group:HasAttribute("Aircraft Carriers") + local warship=group:HasAttribute("Heavy armed ships") + local armedship=group:HasAttribute("Armed ships") + local unarmedship=group:HasAttribute("Unarmed ships") + + -- Define attribute. Order is important. if transportplane then - attribute=WAREHOUSE.Attribute.TRANSPORT_PLANE - elseif transporthelo then - attribute=WAREHOUSE.Attribute.TRANSPORT_HELO - elseif transportapc then - attribute=WAREHOUSE.Attribute.TRANSPORT_APC - elseif fighter then - attribute=WAREHOUSE.Attribute.FIGHTER - elseif tanker then - attribute=WAREHOUSE.Attribute.TANKER + attribute=WAREHOUSE.Attribute.AIR_TRANSPORTPLANE elseif awacs then - attribute=WAREHOUSE.Attribute.AWACS + attribute=WAREHOUSE.Attribute.AIR_AWACS + elseif fighter then + attribute=WAREHOUSE.Attribute.AIR_FIGHTER elseif bomber then - attribute=WAREHOUSE.Attribute.BOMBER - elseif artillery then - attribute=WAREHOUSE.Attribute.ARTILLERY - elseif infantry then - attribute=WAREHOUSE.Attribute.INFANTRY - elseif attackhelicopter then - attribute=WAREHOUSE.Attribute.ATTACKHELICOPTER - elseif tank then - attribute=WAREHOUSE.Attribute.TANK + attribute=WAREHOUSE.Attribute.AIR_BOMBER + elseif tanker then + attribute=WAREHOUSE.Attribute.AIR_TANKER + elseif transporthelo then + attribute=WAREHOUSE.Attribute.AIR_TRANSPORTHELO + elseif apc then + attribute=WAREHOUSE.Attribute.GROUND_APC elseif truck then - attribute=WAREHOUSE.Attribute.TRUCK + attribute=WAREHOUSE.Attribute.GROUND_TRUCK + elseif infantry then + attribute=WAREHOUSE.Attribute.GROUND_INFANTRY + elseif artillery then + attribute=WAREHOUSE.Attribute.GROUND_ARTILLERY + elseif tank then + attribute=WAREHOUSE.Attribute.GROUND_TANK elseif train then - attribute=WAREHOUSE.Attribute.TRAIN + attribute=WAREHOUSE.Attribute.GROUND_TRAIN + elseif aircraftcarrier then + attribute=WAREHOUSE.Attribute.NAVAL_AIRCRAFTCARRIER + elseif warship then + attribute=WAREHOUSE.Attribute.NAVAL_WARSHIP + elseif armedship then + attribute=WAREHOUSE.Attribute.NAVAL_ARMEDSHIP + elseif unarmedship then + attribute=WAREHOUSE.Attribute.NAVAL_UNARMEDSHIP else - attribute=WAREHOUSE.Attribute.OTHER + attribute=WAREHOUSE.Attribute.UNKNOWN end end From 1aabb1bfbd3c8dceb2b0e47727ba068090f9f13c Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 28 Aug 2018 07:16:42 +0200 Subject: [PATCH 287/420] - fixed cargo APC unloading in case of enemy presence and loading in case of enemy destroy or leave. - fixed boarding near range to 5 meters for cargo units. --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 5 ++--- Moose Development/Moose/Cargo/CargoUnit.lua | 2 +- Moose Development/Moose/Wrapper/Positionable.lua | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index c1c871cdd..466803656 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -423,7 +423,7 @@ function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To ) for _, Cargo in pairs( self.CargoSet:GetSet() ) do local Cargo = Cargo -- Cargo.Cargo#CARGO self:F( { IsUnLoaded = Cargo:IsUnLoaded(), IsDeployed = Cargo:IsDeployed(), Cargo:GetName(), APC:GetName() } ) - if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then + if Cargo:IsUnLoaded() then -- and not Cargo:IsDeployed() then if Cargo:IsInLoadRadius( APCUnit:GetCoordinate() ) then self:F( { "In radius", APCUnit:GetName() } ) @@ -551,7 +551,6 @@ function AI_CARGO_APC:onafterUnload( APC, From, Event, To, Deployed ) for _, Cargo in pairs( APCUnit:GetCargo() ) do if Cargo:IsLoaded() then Cargo:UnBoard() - Cargo:SetDeployed( true ) self:__Unboard( 10, Cargo, Deployed ) end end @@ -580,7 +579,6 @@ function AI_CARGO_APC:onafterUnboard( APC, From, Event, To, Cargo, Deployed ) for _, Cargo in pairs( APCUnit:GetCargo() ) do if Cargo:IsLoaded() then Cargo:UnBoard() - Cargo:SetDeployed( true ) self:__Unboard( 10, Cargo, Deployed ) return end @@ -605,6 +603,7 @@ function AI_CARGO_APC:onbeforeUnloaded( APC, From, Event, To, Cargo, Deployed ) self:F( { APC, From, Event, To, Cargo:GetName(), Deployed = Deployed } ) local AllUnloaded = true + Cargo:SetDeployed( true ) --Cargo:Regroup() diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index da9c73f2b..621a9a3a9 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -239,7 +239,7 @@ do -- CARGO_UNIT if not self.CargoInAir then -- If NearRadius is given, then use the given NearRadius, otherwise calculate the NearRadius -- based upon the Carrier bounding radius, which is calculated from the bounding rectangle on the Y axis. - local NearRadius = CargoCarrier:GetBoundingRadius( NearRadius ) + local NearRadius = CargoCarrier:GetBoundingRadius( NearRadius ) + 5 if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then self:Load( CargoCarrier, NearRadius, ... ) else diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index ac10a4f90..08889ea78 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -279,7 +279,7 @@ function POSITIONABLE:GetBoundingRadius() local Z = Box.max.z - Box.min.z local CX = X / 2 local CZ = Z / 2 - return math.max( CX, CZ ) + 3 + return math.max( CX, CZ ) end BASE:E( { "Cannot GetBoundingRadius", Positionable = self, Alive = self:IsAlive() } ) From 46cfcddf68a4b3f5344be3e569072be03c0ac560 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 28 Aug 2018 07:36:55 +0200 Subject: [PATCH 288/420] - Added cache to a ZONE_GROUP to prevent a zone to be unknown when enquired and the GROUP is destroyed. --- Moose Development/Moose/Core/Zone.lua | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 82c7a0be7..fd85bb5ee 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -644,14 +644,14 @@ function ZONE_RADIUS:Scan( ObjectCategories ) local CoalitionDCSUnit = ZoneObject:getCoalition() self.ScanData.Coalitions[CoalitionDCSUnit] = true self.ScanData.Units[ZoneObject] = ZoneObject - self:F( { Name = ZoneObject:getName(), Coalition = CoalitionDCSUnit } ) + self:F2( { Name = ZoneObject:getName(), Coalition = CoalitionDCSUnit } ) end if ObjectCategory == Object.Category.SCENERY then local SceneryType = ZoneObject:getTypeName() local SceneryName = ZoneObject:getName() self.ScanData.Scenery[SceneryType] = self.ScanData.Scenery[SceneryType] or {} self.ScanData.Scenery[SceneryType][SceneryName] = SCENERY:Register( SceneryName, ZoneObject ) - self:F( { SCENERY = self.ScanData.Scenery[SceneryType][SceneryName] } ) + self:F2( { SCENERY = self.ScanData.Scenery[SceneryType][SceneryName] } ) end end return true @@ -1174,6 +1174,7 @@ function ZONE_GROUP:New( ZoneName, ZoneGROUP, Radius ) self:F( { ZoneName, ZoneGROUP:GetVec2(), Radius } ) self._.ZoneGROUP = ZoneGROUP + self._.ZoneVec2Cache = self._.ZoneGROUP:GetVec2() -- Zone objects are added to the _DATABASE and SET_ZONE objects. _EVENTDISPATCHER:CreateEventNewZone( self ) @@ -1188,7 +1189,14 @@ end function ZONE_GROUP:GetVec2() self:F( self.ZoneName ) - local ZoneVec2 = self._.ZoneGROUP:GetVec2() + local ZoneVec2 = nil + + if self._.ZoneGROUP:IsAlive() then + ZoneVec2 = self._.ZoneGROUP:GetVec2() + self._.ZoneVec2Cache = ZoneVec2 + else + ZoneVec2 = self._.ZoneVec2Cache + end self:T( { ZoneVec2 } ) From 2112915dd26ae2a2749434c1806769c092b13830 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Tue, 28 Aug 2018 16:29:03 +0200 Subject: [PATCH 289/420] Warehouse v0.2.8w --- .../Moose/Functional/Warehouse.lua | 189 ++++++++++++++---- .../Moose/Wrapper/Positionable.lua | 3 +- 2 files changed, 153 insertions(+), 39 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index f188ff3ea..aad2c6c7e 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -3,14 +3,11 @@ -- -- Features: -- --- * Holds (virtual) assests such as intrantry groups in stock. +-- * Holds (virtual) assests in stock. -- * Manages requests of assets from other warehouses. --- * Take care of transportation to other warehouses and its accociated airbases. +-- * Realistic transportation of assets between warehouses. -- * Different means of automatic transportation (planes, helicopters, APCs, selfpropelled). --- --- # QUICK START GUIDE --- --- **WIP** +-- * Strategic components such as capturing, defending and destroying warehouses and their associated infrastructure. -- -- === -- @@ -65,7 +62,7 @@ -- -- === -- --- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_Main.JPG) +-- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_Main.jpg) -- -- # The Warehouse Concept -- @@ -108,6 +105,8 @@ -- A MOOSE warehouse must be represented in game by a phyical *static* object. For example, the mission editor already has warehouse as static object available. -- This would be a good first choice but any static object will do. -- +-- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_Static.png) +-- -- The positioning of the warehouse static object is very important for a couple of reasons. Firstly, a warehouse needs a good infrastructure so that spawned assets -- have a proper road connection or can reach the associated airbase easily. -- @@ -156,7 +155,7 @@ -- -- So for example: -- --- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.INFANTRY, 5, WAREHOUSE.TransportType.APC, 2, 20) +-- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 5, WAREHOUSE.TransportType.APC, 2, 20) -- -- Here, warehouse Kobuleti requests 5 infantry groups from warehouse Batumi. These "cargo" assets should be transported from Batumi to Kobuleti by 2 APCS. -- Note that the warehouse at Batumi needs to have at least five infantry groups and two APC groups in their stock if the request can be processed. @@ -195,7 +194,7 @@ -- Assets in the warehouse' stock can used for user defined tasks realtively easily. They can be spawned into the game by a "self request", i.e. the warehouse -- requests the assets from itself: -- --- warehouseBatumi:AddRequest(warehouseBatumi, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.INFANTRY, 5) +-- warehouseBatumi:AddRequest(warehouseBatumi, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 5) -- -- This would simply spawn five infantry groups in the spawn zone of the Batumi warehouse if/when they are available. -- @@ -225,6 +224,62 @@ -- -- Note that airborne groups are spawned in uncontrolled state and need to be activated first before they can start their assigned mission. -- +-- # Infrastructure +-- +-- A good infrastructure is important for a warehouse to be efficient. Therefore, the location of a warehouse should be chosen with care. +-- This can also help to avoid many DCS related issues such as units getting stuck in buildings, blocking taxi ways etc. +-- +-- ## Spawn Zone +-- +-- By default, the zone were ground assets are spawned is a circular zone around the physical location of the warehouse with a radius of 200 meters. However, the location of the +-- spawn zone can be set by the @{#WAREHOUSE.SetSpawnZone}(*zone*) functions. It is advisable to choose a zone which is clear of obstacles. +-- +-- The parameter *zone* is a MOOSE @{Core.Zone#ZONE} object. So one can, e.g., use trigger zones defined in the mission editor. If a cicular zone is not desired, one +-- can use a polygon zone (see @{Core.Zone#ZONE_POLYGON}). +-- +-- ## Road Connections +-- +-- Ground assets will use a road connection to travel from one warehouse to another. Therefore, a proper road connection is necessary. +-- +-- By default, the closest point on road to the center of the spawn zone is choses as road connection automatically. But only, if distance between the spawn zone +-- and the road connection is less than 3 km. +-- +-- The user can set the road connection manually with the @{#WAREHOUSE.SetRoadConnection} function. +-- +-- ## Rail Connections +-- +-- A rail connection is automatically defined as the closest point on a railway measured from the center of the spawn zone. But only, if the distance is less than 3 km. +-- +-- The mission designer can manually specify a rail connection with the @{#WAREHOUSE.SetRailConnection} function. +-- +-- **NOTE** however, that trains in DCS are currently not implemented in a way so that they can be used. +-- +-- ## Air Connections +-- +-- In order to use airborne assets, a warehouse needs to have an associated airbase. This can be an airdrome or a FARP/HELOPAD. +-- +-- If there is an airbase within 3 km range of the warehouse it is automatically set as the associated airbase. A user can set an airbase manually +-- with the @{#WAREHOUSE.SetAirbase} function. Keep in mind, that sometimes, ground units need to walk/drive from the spawn zone to the airport +-- to get to their transport carriers. +-- +-- ## Naval Connections +-- +-- Natively, DCS does not have the concept of a port/habour or shipping lanes. So in order to have a meaningful transfer of naval units between warehouses, these have to be +-- defined by the mission designer. +-- +-- ### Defining a Port +-- +-- A port in this context is the zone where all naval assets are spawned. This zone can be defined with the function @{#WAREHOUSE.SetPortZone}(*zone*), where the parameter +-- *zone* is a MOOSE zone. So again, this can be create from a trigger zone defined in the mission editor or if a general shape is desired by a @{Core.Zone#ZONE_POLYGON}. +-- +-- ### Defining Shipping Lanes +-- +-- A shipping lane between to warehouses can be defined by the @{#WAREHOUSE.AddShippingLane}(*remotewarehouse*, *group*) function. The first parameter *remotewarehouse* +-- is the warehouse which should be connected to the present warehouse. +-- +-- The parameter *group* should be a late activated group defined in the mission editor. The waypoints of this group are used as waypoints of the shipping lane. +-- +-- -- # Strategic Considerations -- -- Due to the fact that a warehouse holds (or can hold) a lot of valuable assets, it makes a (potentially) juicy target for enemy attacks. @@ -311,11 +366,13 @@ WAREHOUSE = { -- @field #string unittype Type of the first unit of the group as obtained by the Object.getTypeName() DCS API function. -- @field #number nunits Number of units in the group. -- @field #number range Range of the unit in meters. +-- @field #number speedmax Maximum speed in km/h the group can do. -- @field #number size Maximum size in length and with of the asset in meters. --- @field #number speedmax Maximum speed in km/h the unit can do. +-- @field #number weight The weight of the whole asset group in kilo gramms. -- @field DCS#Object.Desc DCSdesc All DCS descriptors. -- @field #WAREHOUSE.Attribute attribute Generalized attribute of the group. --- @field #boolean istransport If true, the asset is able to transport troops. +-- @field #boolean transporter If true, the asset is able to transport troops. +-- @field #number cargobay Weight in kg that fits in the cargo bay of one asset unit. --- Item of the warehouse queue table. -- @type WAREHOUSE.Queueitem @@ -431,15 +488,17 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.2.8" +WAREHOUSE.version="0.2.8w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- TODO: Take cargo weight into consideration, when selecting -- TODO: Add transport units from dispatchers back to warehouse stock once they completed their mission. --- TODO: Add harbours and ports. --- TODO: Add shipping lanes between warehouses. +-- DONE: Add ports for spawning naval assets. +-- TODO: Added habours as interface for transport to from warehouses? +-- DONE: Add shipping lanes between warehouses. -- TODO: Set ROE for spawned groups. -- TODO: Add possibility to add active groups. Need to create a pseudo template before destroy. -- TODO: Write documentation. @@ -450,7 +509,7 @@ WAREHOUSE.version="0.2.8" -- TODO: Add general message function for sending to coaliton or debug. -- TODO: Fine tune event handlers. -- TODO: Add save/load capability of warehouse <==> percistance after mission restart. --- TODO: Improve generalized attributes. +-- DONE: Improve generalized attributes. -- TODO: Add a time stamp when an asset is added to the stock and for requests -- DONE: If warehouse is destoyed, all asssets are gone. -- DONE: Add event handlers. @@ -1315,6 +1374,20 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute) local SpeedMax=group:GetSpeedMax() local RangeMin=group:GetRange() local smax,sx,sy,sz=_GetObjectSize(DCSdesc) + + -- Get weight in kg + local weight=0 + local DCSunits=DCSgroup:getUnits() + for _,unit in pairs(DCSunits) do + local desc=unit:getDesc() + local unitweight=desc.emptyWeight + weight=weight+unitweight + end + + for _,_unit in pairs(group:GetUnits()) do + local unit=_unit --Wrapper.Unit#UNIT + unit:GetCargoBayFreeWeight() + end -- Set/get the generalized attribute. local attribute=forceattribute or self:_GetAttribute(templategroupname) @@ -1335,12 +1408,14 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute) asset.template=UTILS.DeepCopy(_DATABASE.Templates.Groups[templategroupname].Template) asset.category=DCScategory asset.unittype=DCStype - asset.nunits=#asset.template.units - asset.attribute=attribute + asset.nunits=#asset.template.units asset.range=RangeMin asset.speedmax=SpeedMax - asset.size=smax + asset.size=smax + asset.weight=weight asset.DCSdesc=DCSdesc + asset.attribute=attribute + asset.transporter=false -- not used yet if i==1 then self:_AssetItemInfo(asset) @@ -1367,9 +1442,11 @@ function WAREHOUSE:_AssetItemInfo(asset) text=text..string.format("Attribute = %s\n", asset.attribute) text=text..string.format("Category = %d\n", asset.category) text=text..string.format("Units # = %d\n", asset.nunits) - text=text..string.format("Size max = %5.2f m\n", asset.size) text=text..string.format("Speed max = %5.2f km/h\n", asset.speedmax) text=text..string.format("Range max = %5.2f km\n", asset.range/1000) + text=text..string.format("Size max = %5.2f m\n", asset.size) + text=text..string.format("Weight total = %5.2f kg\n", asset.weight) + text=text..string.format("Cargo bay = %5.2f kg\n", asset.cargobay) self:E(self.wid..text) self:E({DCSdesc=asset.DCSdesc}) self:E({Template=asset.template}) @@ -1701,7 +1778,7 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request) -- Check if destination is in range. if asset.range0 then -- Get the attibute of the requested asset. @@ -3859,7 +3972,7 @@ function WAREHOUSE:_GetAttribute(groupname) local group=GROUP:FindByName(groupname) - local attribute=WAREHOUSE.Attribute.OTHER --#WAREHOUSE.Attribute + local attribute=WAREHOUSE.Attribute.UNKNOWN --#WAREHOUSE.Attribute if group then diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index ac10a4f90..906fd31d5 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -941,7 +941,8 @@ do -- Cargo -- end -- return self.__.CargoBayVolumeLimit - CargoVolume -- end --- +-- + --- Get Cargo Bay Free Weight in kg. -- @param #POSITIONABLE self -- @return #number CargoBayFreeWeight From a8d96d332f9eafc071e18845ac5e8d1a8310cdb8 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 29 Aug 2018 00:16:57 +0200 Subject: [PATCH 290/420] Warehouse v0.2.9 not working, need to find a way for transports --- .../Moose/Functional/Warehouse.lua | 189 ++++++++++++++++-- .../Moose/Wrapper/Positionable.lua | 2 +- 2 files changed, 175 insertions(+), 16 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index aad2c6c7e..41c709762 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -139,7 +139,7 @@ -- -- # Requesting Assets -- --- Assets of the warehouse can be requested by other MOOSE warehouses. A request will first be scrutinize to check if can be fullfilled at all. If the request is valid, it is +-- Assets of the warehouse can be requested by other MOOSE warehouses. A request will first be scrutinize to check if can be fulfilled at all. If the request is valid, it is -- put into the warehouse queue and processed as soon as possible. -- -- A request can be assed by the @{#WAREHOUSE.AddRequest}(*warehouse*, *AssetDescriptor*, *AssetDescriptorValue*, *nAsset*, *TransportType*, *nTransport*, *Prio*) function. @@ -488,7 +488,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.2.8w" +WAREHOUSE.version="0.2.9" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -1376,17 +1376,19 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute) local smax,sx,sy,sz=_GetObjectSize(DCSdesc) -- Get weight in kg + env.info("FF get weight") local weight=0 - local DCSunits=DCSgroup:getUnits() - for _,unit in pairs(DCSunits) do - local desc=unit:getDesc() - local unitweight=desc.emptyWeight - weight=weight+unitweight - end - + local cargobay=0 for _,_unit in pairs(group:GetUnits()) do local unit=_unit --Wrapper.Unit#UNIT - unit:GetCargoBayFreeWeight() + local Desc=unit:GetDesc() + self:E({UnitDesc=Desc}) + local unitweight=Desc.massEmpty + if unitweight then + weight=weight+unitweight + env.info("FF weight = "..weight) + end + cargobay=unit:GetCargoBayFreeWeight() end -- Set/get the generalized attribute. @@ -1416,6 +1418,7 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute) asset.DCSdesc=DCSdesc asset.attribute=attribute asset.transporter=false -- not used yet + asset.cargobay=cargobay if i==1 then self:_AssetItemInfo(asset) @@ -1696,6 +1699,87 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- On before "AddRequest" event. Checks some basic properties of the given parameters. +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #WAREHOUSE warehouse The warehouse requesting supply. +-- @param #WAREHOUSE.Descriptor AssetDescriptor Descriptor describing the asset that is requested. +-- @param AssetDescriptorValue Value of the asset descriptor. Type depends on descriptor, i.e. could be a string, etc. +-- @param #number nAsset Number of groups requested that match the asset specification. +-- @param #WAREHOUSE.TransportType TransportType Type of transport. +-- @param #number nTransport Number of transport units requested. +-- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. +-- @param #string Assignment A keyword or text that +-- @return #boolean If true, request is okay at first glance. +function WAREHOUSE:onbeforeAddRequest(From, Event, To, warehouse, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType, nTransport, Assignment, Prio) + + -- Defaults. + nAsset=nAsset or 1 + TransportType=TransportType or WAREHOUSE.TransportType.SELFPROPELLED + Prio=Prio or 50 + if nTransport==nil then + if TransportType==WAREHOUSE.TransportType.SELFPROPELLED then + nTransport=0 + else + nTransport=1 + end + end + + -- Request is okay. + local okay=true + + if AssetDescriptor==WAREHOUSE.Descriptor.ATTRIBUTE then + + -- Check if a valid attibute was given. + local gotit=false + for _,attribute in pairs(WAREHOUSE.Attribute) do + if AssetDescriptorValue==attribute then + gotit=true + end + end + if not gotit then + self:E(self.wid.."ERROR: Invalid request. Asset attribute is unknown!") + okay=false + end + + elseif AssetDescriptor==WAREHOUSE.Descriptor.CATEGORY then + + -- Check if a valid category was given. + local gotit=false + for _,category in pairs(Group.Category) do + if AssetDescriptorValue==category then + gotit=true + end + end + if not gotit then + self:E(self.wid.."ERROR: Invalid request. Asset category is unknown!") + okay=false + end + + elseif AssetDescriptor==WAREHOUSE.Descriptor.TEMPLATENAME then + + if type(AssetDescriptorValue)~="string" then + self:E(self.wid.."ERROR: Invalid request. Asset template name must be passed as a string!") + okay=false + end + + elseif AssetDescriptor==WAREHOUSE.Descriptor.UNITTYPE then + + if type(AssetDescriptorValue)~="string" then + self:E(self.wid.."ERROR: Invalid request. Asset unit type must be passed as a string!") + okay=false + end + + else + self:E(self.wid.."ERROR: Invalid request. Asset descriptor is not ATTRIBUTE, CATEGORY, TEMPLATENAME or UNITTYPE!") + okay=false + end + + return okay +end + --- On after "AddRequest" event. Add a request to the warehouse queue, which is processed when possible. -- @param #WAREHOUSE self -- @param #string From From state. @@ -1749,7 +1833,7 @@ end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- On before "Request" event. Checks if the request can be fullfilled. +--- On before "Request" event. Checks if the request can be fulfilled. -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. @@ -2076,7 +2160,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) local _alias=self:_Alias(_assetitem, Request) -- Spawn ground asset. - local spawngroup=self:_SpawnAssetGround(_assetitem, Request, self.spawnzone) + local spawngroup=self:_SpawnAssetGroundNaval(_assetitem, Request, self.spawnzone) if spawngroup then -- Set state of warehouse so we can retrieve it later. @@ -3431,10 +3515,10 @@ function WAREHOUSE:_CheckRequestValid(request) end ---- Checks if the request can be fullfilled right now. +--- Checks if the request can be fulfilled right now. -- Check for current parking situation, number of assets and transports currently in stock. -- @param #WAREHOUSE self --- @param #WAREHOUSE.Queueitem request The request to be checked. +-- @param #WAREHOUSE.Pendingitem request The request to be checked. -- @return #boolean If true, request can be executed. If false, something is not right. function WAREHOUSE:_CheckRequestNow(request) @@ -3478,10 +3562,22 @@ function WAREHOUSE:_CheckRequestNow(request) end end + request.assets=_assets + --request. + end -- Check that a transport units. if request.transporttype ~= WAREHOUSE.TransportType.SELFPROPELLED then + + + + + _transports=self:_GetTransportsForAssets(request) + + + + elseif false then -- Transports in stock. local _transports,_ntransports,_enough=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, request.transporttype, request.ntransport) @@ -3521,7 +3617,70 @@ function WAREHOUSE:_CheckRequestNow(request) return okay end ----Sorts the queue and checks if the request can be fullfilled. +---Get (optimized) transport carriers for the given assets to be transported. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE.Pendingitem Chosen request. +function WAREHOUSE:_GetTransportsForAssets(request) + + -- Get all transports of the requested type in stock. + local transports=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, request.transporttype) + + local cargoassets=request.assets + + -- Problems/questions + -- 1. Do we have at least one carrier big enough to transport the largest group? + -- If not ==> No transport possible since groups cannot be split! + -- If yes ==> Tranport possible. + -- 2. How many carriers do we need? + -- ntransport should be the max number. + + -- Example 8, 8, 5, 3 + -- Carriers: + -- 2 that can take 8 can be used for 3, 5, 8 + -- 1 that can take 6 can be used for 3, 5, - + -- 3 that can take 4 can be used for 3, -, - + -- 1 that can take 2 can be used for -, -, - + + -- So the problem becomes: + -- How do I minimize the number of "ways" with the constraint of a fixed number of carriers? + -- Extreme cases: + -- Use just one carrier that can carrier the largest group. I would have to drive n times to get all cargo from A to B. + -- + + -- The most simple way is to sort the transports in descending order wrt. to their cargo bay size. + -- Use largest carriers available until either number of cargo is done in one run or we hit max number of carriers available. + + -- sort transport carriers w.r.t. cargo bay size. + local function sort_transports(a,b) + return a.cargobay>b.cargobay + end + + -- sort cargo assets w.r.t. weight in assending order + local function sort_cargoassets(a,b) + return a.weight>b.weight + end + + table.sort(transports, sort_transports) + table.sort(cargoassets, sort_cargoassets) + + -- Very simple! Only take the largest transports that can carrier the largest cargo. + local used_transports={} + local maxcargoweight=cargoassets[1] + for i=1,#transports do + local transport=transports[i] --#WAREHOUSE.Assetitem + if transport.cargobay>maxcargoweight and #used_transports<=request.ntransport then + table.insert(used_transports, transport) + end + end + + for _,_transport in ipairs(used_transports) do + local transport=_transport --#WAREHOUSE.Assetitem + --env.info("transport used = ", transport.) + end + +end + +---Sorts the queue and checks if the request can be fulfilled. -- @param #WAREHOUSE self -- @return #WAREHOUSE.Queueitem Chosen request. function WAREHOUSE:_CheckQueue() diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 984ebfd65..66e5df18b 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -1010,7 +1010,7 @@ do -- Cargo ["TPZ"] = 10, } - local CargoBayWeightLimit = ( Weights[Desc.typeName] or 0 ) * 70 + local CargoBayWeightLimit = ( Weights[Desc.typeName] or 0 ) * 90 self.__.CargoBayWeightLimit = CargoBayWeightLimit end end From daaedd24d24db48b1a0d7c143f1cde55c11cb3bd Mon Sep 17 00:00:00 2001 From: FlightControl Date: Wed, 29 Aug 2018 08:07:13 +0200 Subject: [PATCH 291/420] Reworked the menu system for cargo transportation. The Route to Pickup cargo sub menu now shows boarding, loading or sling loading sub menus, and allows to handle much more cargo. --- Moose Development/Moose/Cargo/CargoCrate.lua | 6 ++++ Moose Development/Moose/Core/Database.lua | 14 +++++----- .../Moose/Tasking/Task_CARGO.lua | 28 +++++++++++++++++-- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua index 399c29ffb..53011f56d 100644 --- a/Moose Development/Moose/Cargo/CargoCrate.lua +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -159,6 +159,12 @@ do -- CARGO_CRATE return false end + --- Check if the cargo can be sling loaded. + -- @param #CARGO_CRATE self + function CARGO_CRATE:CanSlingload() + return false + end + --- Check if Cargo Crate is in the radius for the Cargo to be reported. -- @param #CARGO_CRATE self -- @param Core.Point#COORDINATE Coordinate diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 59e4676e4..e497e306d 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -355,7 +355,7 @@ do -- cargo return CargoFound end - --- Checks if the Template name has a ~CARGO tag. + --- Checks if the Template name has a #CARGO tag. -- If yes, the group is a cargo. -- @param #DATABASE self -- @param #string TemplateName @@ -364,7 +364,7 @@ do -- cargo TemplateName = env.getValueDictByKey( TemplateName ) - local Cargo = TemplateName:match( "~(CARGO)" ) + local Cargo = TemplateName:match( "#(CARGO)" ) return Cargo and Cargo == "CARGO" end @@ -379,10 +379,10 @@ do -- cargo for CargoGroupName, CargoGroup in pairs( Groups ) do self:I( { Cargo = CargoGroupName } ) if self:IsCargo( CargoGroupName ) then - local CargoInfo = CargoGroupName:match("~CARGO(.*)") + local CargoInfo = CargoGroupName:match("#CARGO(.*)") local CargoParam = CargoInfo and CargoInfo:match( "%((.*)%)") - local CargoName1 = CargoGroupName:match("(.*)~CARGO%(.*%)") - local CargoName2 = CargoGroupName:match(".*~CARGO%(.*%)(.*)") + local CargoName1 = CargoGroupName:match("(.*)#CARGO%(.*%)") + local CargoName2 = CargoGroupName:match(".*#CARGO%(.*%)(.*)") local CargoName = CargoName1 .. ( CargoName2 or "" ) local Type = CargoParam and CargoParam:match( "T=([%a%d ]+),?") local Name = CargoParam and CargoParam:match( "N=([%a%d]+),?") or CargoName @@ -396,9 +396,9 @@ do -- cargo for CargoStaticName, CargoStatic in pairs( self.STATICS ) do if self:IsCargo( CargoStaticName ) then - local CargoInfo = CargoStaticName:match("~CARGO(.*)") + local CargoInfo = CargoStaticName:match("#CARGO(.*)") local CargoParam = CargoInfo and CargoInfo:match( "%((.*)%)") - local CargoName = CargoStaticName:match("(.*)~CARGO") + local CargoName = CargoStaticName:match("(.*)#CARGO") local Type = CargoParam and CargoParam:match( "T=([%a%d ]+),?") local Category = CargoParam and CargoParam:match( "C=([%a%d ]+),?") local Name = CargoParam and CargoParam:match( "N=([%a%d]+),?") or CargoName diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 9225e5532..7cd0c5399 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -667,9 +667,12 @@ do -- TASK_CARGO Cargo:Report( "Load at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ) .. " within " .. Cargo.NearRadius .. ".", "reporting", TaskUnit:GetGroup() ) end else + --local Cargo = Cargo -- Cargo.CargoSlingload#CARGO_SLINGLOAD if Cargo:CanSlingload() == true then if Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then - Cargo:Report( "Ready for slingloading.", "slingload", TaskUnit:GetGroup() ) + Cargo:Report( "Ready for sling loading.", "slingload", TaskUnit:GetGroup() ) + local SlingloadMenu = MENU_GROUP:New( TaskGroup, "Slingload cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" ) + MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, SlingloadMenu, self.MenuLoadCargo, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() else Cargo:Report( "Slingload at " .. Cargo:GetCoordinate():ToString( TaskUnit:GetGroup() ) .. ".", "reporting", TaskUnit:GetGroup() ) end @@ -683,8 +686,29 @@ do -- TASK_CARGO else if not Cargo:IsDeployed() == true then local RouteToPickupMenu = MENU_GROUP:New( TaskGroup, "Route to pickup cargo", MenuControl ):SetTime( MenuTime ):SetTag( "Cargo" ) - MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, RouteToPickupMenu, self.MenuRouteToPickup, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() + --MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, RouteToPickupMenu, self.MenuRouteToPickup, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() Cargo:ReportResetAll( TaskUnit:GetGroup() ) + if Cargo:CanBoard() == true then + if not Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then + local BoardMenu = MENU_GROUP:New( TaskGroup, "Board cargo", RouteToPickupMenu ):SetTime( MenuTime ):SetTag( "Cargo" ) + MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, BoardMenu, self.MenuRouteToPickup, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() + end + else + if Cargo:CanLoad() == true then + if not Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then + local LoadMenu = MENU_GROUP:New( TaskGroup, "Load cargo", RouteToPickupMenu ):SetTime( MenuTime ):SetTag( "Cargo" ) + MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, LoadMenu, self.MenuRouteToPickup, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() + end + else + --local Cargo = Cargo -- Cargo.CargoSlingload#CARGO_SLINGLOAD + if Cargo:CanSlingload() == true then + if not Cargo:IsInLoadRadius( TaskUnit:GetPointVec2() ) then + local SlingloadMenu = MENU_GROUP:New( TaskGroup, "Slingload cargo", RouteToPickupMenu ):SetTime( MenuTime ):SetTag( "Cargo" ) + MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), Cargo.Name, SlingloadMenu, self.MenuRouteToPickup, self, Cargo ):SetTime(MenuTime):SetTag("Cargo"):SetRemoveParent() + end + end + end + end end end end From 7b338ca9d0188efd4a35463c905ed1ffad00950a Mon Sep 17 00:00:00 2001 From: FlightControl Date: Wed, 29 Aug 2018 08:18:03 +0200 Subject: [PATCH 292/420] Fixed the weight of an average infantry from 70 kg to 95 kg. --- Moose Development/Moose/Wrapper/Positionable.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 08889ea78..aa564c06a 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -1009,7 +1009,7 @@ do -- Cargo ["TPZ"] = 10, } - local CargoBayWeightLimit = ( Weights[Desc.typeName] or 0 ) * 70 + local CargoBayWeightLimit = ( Weights[Desc.typeName] or 0 ) * 95 self.__.CargoBayWeightLimit = CargoBayWeightLimit end end From ae04196584fc693b81f3c0fb217b897aa67ad376 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 29 Aug 2018 15:49:41 +0200 Subject: [PATCH 293/420] Warehouse v0.2.9w --- Moose Development/Moose/Functional/Warehouse.lua | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 41c709762..e86eea55b 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -488,7 +488,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.2.9" +WAREHOUSE.version="0.2.9w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -3562,21 +3562,19 @@ function WAREHOUSE:_CheckRequestNow(request) end end + -- Set chosen assets. request.assets=_assets - --request. + request.cargoattribute=_assetattribute + request.cargocategory=_assetcategory end -- Check that a transport units. if request.transporttype ~= WAREHOUSE.TransportType.SELFPROPELLED then - + local _transports=self:_GetTransportsForAssets(request) - _transports=self:_GetTransportsForAssets(request) - - - elseif false then -- Transports in stock. From 25777afdd188793696380ccfccefcc02cfa13243 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Wed, 29 Aug 2018 19:23:46 +0200 Subject: [PATCH 294/420] - Now the cargo is sorted from large to small for dispatching. - The carrier which has a unit that fits the group size, will be routed towards that cargo. - When boarding, the carrier(s) will then be boarded with any cargo group that is available that fits the remaining cargo bays. --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 5 +++-- .../Moose/AI/AI_Cargo_Dispatcher.lua | 22 +++++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 466803656..19e674583 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -420,7 +420,8 @@ function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To ) self.APC_Cargo = {} for _, APCUnit in pairs( APC:GetUnits() ) do local APCUnit = APCUnit -- Wrapper.Unit#UNIT - for _, Cargo in pairs( self.CargoSet:GetSet() ) do + --for _, Cargo in pairs( self.CargoSet:GetSet() ) do + for _, Cargo in UTILS.spairs( self.CargoSet:GetSet(), function( t, a, b ) return t[a]:GetWeight() > t[b]:GetWeight() end ) do local Cargo = Cargo -- Cargo.Cargo#CARGO self:F( { IsUnLoaded = Cargo:IsUnLoaded(), IsDeployed = Cargo:IsDeployed(), Cargo:GetName(), APC:GetName() } ) if Cargo:IsUnLoaded() then -- and not Cargo:IsDeployed() then @@ -472,7 +473,7 @@ function AI_CARGO_APC:onafterBoard( APC, From, Event, To, Cargo ) else for _, APCUnit in pairs( APC:GetUnits() ) do local APCUnit = APCUnit -- Wrapper.Unit#UNIT - for _, Cargo in pairs( self.CargoSet:GetSet() ) do + for _, Cargo in UTILS.spairs( self.CargoSet:GetSet(), function( t, a, b ) return t[a]:GetWeight() > t[b]:GetWeight() end ) do local Cargo = Cargo -- Cargo.Cargo#CARGO if Cargo:IsUnLoaded() then if Cargo:IsInLoadRadius( APCUnit:GetCoordinate() ) then diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 602868607..71359203b 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -411,7 +411,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() local PickupCargo = nil - for CargoName, Cargo in pairs( self.SetCargo:GetSet() ) do + for CargoName, Cargo in UTILS.spairs( self.SetCargo:GetSet(), function( t, a, b ) return t[a]:GetWeight() < t[b]:GetWeight() end ) do local Cargo = Cargo -- Cargo.Cargo#CARGO self:F( { Cargo = Cargo:GetName(), UnLoaded = Cargo:IsUnLoaded(), Deployed = Cargo:IsDeployed(), PickupCargo = self.PickupCargo[Carrier] ~= nil } ) if Cargo:IsUnLoaded() == true and Cargo:IsDeployed() == false then @@ -428,9 +428,23 @@ function AI_CARGO_DISPATCHER:onafterMonitor() end end if CoordinateFree == true then - self.PickupCargo[Carrier] = CargoCoordinate - PickupCargo = Cargo - break + -- Check if this cargo can be picked-up by at least one carrier unit of AI_Cargo. + local LargestLoadCapacity = 0 + for _, Carrier in pairs( Carrier:GetUnits() ) do + local LoadCapacity = Carrier:GetCargoBayFreeWeight() + if LargestLoadCapacity < LoadCapacity then + LargestLoadCapacity = LoadCapacity + end + end + -- So if there is aa carrier that has the required load capacity to load the total weight of the cargo, dispatch the carrier. + -- Otherwise break and go to the next carrier. + -- This will skip cargo which is too large to be able to be loaded by carriers + -- and will secure an efficient dispatching scheme. + if LargestLoadCapacity >= Cargo:GetWeight() then + self.PickupCargo[Carrier] = CargoCoordinate + PickupCargo = Cargo + break + end end end end From 6fbf584e812c247f47f3be30aa5703d305268172 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 29 Aug 2018 23:33:13 +0200 Subject: [PATCH 295/420] Warehouse v0.3.0 --- .../Moose/Functional/Warehouse.lua | 196 ++++++++++++------ 1 file changed, 128 insertions(+), 68 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index e86eea55b..a56574a36 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -1,5 +1,8 @@ --- **Functional** - (R2.5) - Simulation of logistics. --- +-- +-- The MOOSE warehouse concept simulates the organization and implementation of complex operations regarding the flow of assets between the point of origin and the point of consumption +-- in order to meet requirements of a potential conflict. In particular, this class is concerned with maintaining army supply lines while disrupting those of the enemy, since an armed +-- force without resources and transportation is defenseless. -- -- Features: -- @@ -8,6 +11,7 @@ -- * Realistic transportation of assets between warehouses. -- * Different means of automatic transportation (planes, helicopters, APCs, selfpropelled). -- * Strategic components such as capturing, defending and destroying warehouses and their associated infrastructure. +-- * Can be coupled to other MOOSE classes. -- -- === -- @@ -46,7 +50,7 @@ -- @field #table shippinglanes Table holding the user defined shipping between warehouses. -- @extends Core.Fsm#FSM ---- Manages ground assets of an airbase and offers the possibility to transport them to another airbase or warehouse. +--- Have your assets at the right place at the right time - or not! -- -- === -- @@ -381,24 +385,27 @@ WAREHOUSE = { -- @field #WAREHOUSE warehouse Requesting warehouse. -- @field Wrapper.Airbase#AIRBASE airbase Requesting airbase or airbase beloning to requesting warehouse. -- @field DCS#Airbase.Category category Category of the requesting airbase, i.e. airdrome, helipad/farp or ship. --- @field #WAREHOUSE.Descriptor assetdesc Descriptor of the requested asset. --- @field assetdescval Value of the asset descriptor. Type depends on descriptor. +-- @field #WAREHOUSE.Descriptor assetdesc Descriptor of the requested asset. Enumerator of type @{#WAREHOUSE.Descriptor}. +-- @field assetdescval Value of the asset descriptor. Type depends on "assetdesc" descriptor. -- @field #number nasset Number of asset groups requested. -- @field #WAREHOUSE.TransportType transporttype Transport unit type. --- @field #number ntransport Number of transport units requested. +-- @field #number ntransport Max. number of transport units requested. +-- @field #boolean toself Self request, i.e. warehouse requests assets from itself. +-- @field #table assets Table of self propelled (or cargo) and transport assets. Each element of the table is a @{#WAREHOUSE.Assetitem} and can be accessed by their asset ID. +-- @field #table cargoassets Table of cargo (or self propelled) assets. Each element of the table is a @{#WAREHOUSE.Assetitem}. +-- @field #number cargoattribute Attribute of cargo assets of type @{#WAREHOUSE.Attribute}. +-- @field #number cargocategory Category of cargo assets of type @{#WAREHOUSE.Category}. +-- @field #table transportassets Table of transport carrier assets. Each element of the table is a @{#WAREHOUSE.Assetitem}. +-- @field #number transportattribute Attribute of transport assets of type @{#WAREHOUSE.Attribute}. +-- @field #number transportcategory Category of transport assets of type @{#WAREHOUSE.Category}. --- Item of the warehouse pending queue table. -- @type WAREHOUSE.Pendingitem -- @extends #WAREHOUSE.Queueitem --- @field #table assets Table of assets - cargo or transport. Each element of the table is a @{#WAREHOUSE.Assetitem}. -- @field Core.Set#SET_GROUP cargogroupset Set of cargo groups do be delivered. -- @field #number ndelivered Number of groups delivered to destination. --- @field #number cargoattribute Attribute of cargo assets of type @{#WAREHOUSE.Attribute}. --- @field #number cargocategory Category of cargo assets of type @{#WAREHOUSE.Category}. -- @field Core.Set#SET_GROUP transportgroupset Set of cargo transport groups. --- @field #number transportattribute Attribute of transport assets of type @{#WAREHOUSE.Attribute}. --- @field #number transportcategory Category of transport assets of type @{#WAREHOUSE.Category}. --- @field #number ntransporthome Number of transports back home. transportattribute +-- @field #number ntransporthome Number of transports back home. --- Descriptors enumerator describing the type of the asset. -- @type WAREHOUSE.Descriptor @@ -488,13 +495,15 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.2.9w" +WAREHOUSE.version="0.3.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Take cargo weight into consideration, when selecting +-- TODO: Warehouse re-capturing not working?! +-- TODO: Naval assets dont go back into stock once arrived. +-- TODO: Take cargo weight into consideration, when selecting transport assets. -- TODO: Add transport units from dispatchers back to warehouse stock once they completed their mission. -- DONE: Add ports for spawning naval assets. -- TODO: Added habours as interface for transport to from warehouses? @@ -1846,8 +1855,8 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request) -- Distance from warehouse to requesting warehouse. local distance=self.coordinate:Get2DDistance(Request.warehouse.coordinate) - -- Filter the requested assets. - local _assets,_nasset,_enough=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval, Request.nasset) + -- Shortcut to cargoassets. + local _assets=Request.cargoassets if Request.nasset==0 then local text=string.format("Request denied! Zero assets were requested.") @@ -1875,12 +1884,14 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request) end - -- Asset is not in stock ==> request denied. - if not _enough then - local text=string.format("Request denied! Not enough assets currently in stock. Requested %s < %d in stock.", tostring(Request.nasset), _nasset) - MESSAGE:New(text, 10):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:E(self.wid..text) - return false + -- Init asset table. + Request.assets={} + + -- Init self request. + if self.warehouse:GetName()==Request.warehouse.warehouse:GetName() then + Request.toself=true + else + Request.toself=false end return true @@ -1901,33 +1912,31 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Pending request. Add cargo groups to request. local Pending=Request --#WAREHOUSE.Pendingitem - Pending.assets={} -- Spawn assets of this request. - local _spawngroups,_cargoassets=self:_SpawnAssetRequest(Pending) --Core.Set#SET_GROUP + local _spawngroups=self:_SpawnAssetRequest(Pending) --Core.Set#SET_GROUP -- Check if any group was spawned. If not, delete the request. if _spawngroups:Count()==0 then - -- Delete request from queue. - self:_DeleteQueueItem(Request, self.queue) self:E(self.wid..string.format("ERROR: Groups or request %d could not be spawned. Request is rejected and deleted from queue!", Request.uid)) + -- Delete request from queue. + self:_DeleteQueueItem(Request, self.queue) return end -- General type and category. - local _cargotype=_cargoassets[1].attribute --#WAREHOUSE.Attribute - local _cargocategory=_cargoassets[1].category --DCS#Group.Category + local _cargotype=Request.cargoattribute --#WAREHOUSE.Attribute + local _cargocategory=Request.cargocategory --DCS#Group.Category + -- Add groups to pending item. Pending.cargogroupset=_spawngroups - Pending.cargoattribute=_cargotype - Pending.cargocategory=_cargocategory ------------------------------------------------------------------------------------------------------------------------------------ -- Self request: assets are spawned at warehouse but not transported anywhere. ------------------------------------------------------------------------------------------------------------------------------------ -- Self request! Assets are only spawned but not routed or transported anywhere. - if self.warehouse:GetName()==Request.warehouse.warehouse:GetName() then + if Request.toself then self:E(self.wid..string.format("Selfrequest! Current status %s", self:GetState())) -- Add request to pending queue. @@ -2011,7 +2020,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) --TODO: make nearradius depended on transport type and asset type. local _loadradius=5000 - local _nearradius=35 + local _nearradius=nil if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then _loadradius=5000 @@ -2048,12 +2057,12 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Cargo dispatcher. local CargoTransport --AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER - -- Filter the requested transport assets. - local _assetstock=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, Request.transporttype, Request.ntransport) + -- Shortcut to transport assets. + local _assetstock=Request.transportassets -- General type and category. - local _transporttype=_assetstock[1].attribute --#WAREHOUSE.Attribute - local _transportcategory=_assetstock[1].category --DCS#Group.Category + local _transporttype=Request.transportattribute + local _transportcategory=Request.transportcategory -- Now we try to find all parking spots for all cargo groups in advance. Due to the for loop, the parking spots do not get updated while spawning. local Parking={} @@ -2251,8 +2260,6 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Add cargo groups to request. Pending.transportgroupset=TransportSet - Pending.transportattribute=_transporttype - Pending.transportcategory=_transportcategory -- Add request to pending queue. table.insert(self.pending, Pending) @@ -2267,34 +2274,26 @@ end -- @param #WAREHOUSE self -- @param #WAREHOUSE.Queueitem Request Information table of the request. -- @return Core.Set#SET_GROUP Set of groups that were spawned. --- @return #table List of spawned assets. function WAREHOUSE:_SpawnAssetRequest(Request) self:E({requestUID=Request.uid}) - -- Filter the requested cargo assets. - local _assetstock,_nasset,_enough=self:_FilterStock(self.stock, Request.assetdesc, Request.assetdescval, Request.nasset) - - self:E({num_assetstoc=#_assetstock, nasset=_nasset, enough=_enough}) - - -- No assets in stock :( - if not _enough then - return nil,nil,nil - end + -- Shortcut to cargo assets. + local _assetstock=Request.cargoassets -- General type and category. - local _cargotype=_assetstock[1].attribute --#WAREHOUSE.Attribute - local _cargocategory=_assetstock[1].category --DCS#Group.Category + local _cargotype=Request.cargoattribute --#WAREHOUSE.Attribute + local _cargocategory=Request.cargocategory --DCS#Group.Category -- Now we try to find all parking spots for all cargo groups in advance. Due to the for loop, the parking spots do not get updated while spawning. local Parking={} if _cargocategory==Group.Category.AIRPLANE or _cargocategory==Group.Category.HELICOPTER then - Parking=self:_FindParkingForAssets(self.airbase,_assetstock) + Parking=self:_FindParkingForAssets(self.airbase,_assetstock) end -- Spawn aircraft in uncontrolled state if request comes from the same warehouse. local UnControlled=false local AIOnOff=true - if self.warehouse:GetName()==Request.warehouse.warehouse:GetName() then + if Request.toself then UnControlled=true AIOnOff=false end @@ -2363,8 +2362,11 @@ function WAREHOUSE:_SpawnAssetRequest(Request) Request.assets[asset.uid]=asset self:_DeleteStockItem(asset) end + + -- Overwrite the assets with the actually spawned ones. + Request.cargoassets=_assets - return _groupset,_assets + return _groupset end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -2795,7 +2797,8 @@ function WAREHOUSE:_RouteNaval(group, request) end -- Task function triggering the arrived event at the last waypoint. - local TaskFunction = group:TaskFunction("WAREHOUSE._Arrived", self) + --local TaskFunction = group:TaskFunction("WAREHOUSE._Arrived", self) + local TaskFunction = self:_SimpleTaskFunction("WAREHOUSE:_ArrivedSimple", group) -- Put task function on last waypoint. local Waypoint = Waypoints[#Waypoints] @@ -2869,7 +2872,7 @@ end --- Task function for last waypoint. Triggering the "Arrived" event. -- @param Wrapper.Group#GROUP group The group that arrived. --- @param #WAREHOUSE self +-- @param #WAREHOUSE warehouse Warehouse self. function WAREHOUSE._Arrived(group, warehouse) env.info(warehouse.wid..string.format("Group %s arrived at destination.", tostring(group:GetName()))) @@ -3530,7 +3533,8 @@ function WAREHOUSE:_CheckRequestNow(request) local text=string.format("Warehouse %s: Request denied! Receiving warehouse %s is not running. Current state %s.", self.alias, request.warehouse.alias, request.warehouse:GetState()) MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:E(self.wid..text) - okay=false + + return false end -- Check if number of requested assets is in stock. @@ -3541,7 +3545,8 @@ function WAREHOUSE:_CheckRequestNow(request) local text=string.format("Warehouse %s: Request denied! Not enough (cargo) assets currently available.", self.alias) MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:E(self.wid..text) - okay=false + + return false end -- Check if at least one (cargo) asset is available. @@ -3558,12 +3563,13 @@ function WAREHOUSE:_CheckRequestNow(request) local text=string.format("Warehouse %s: Request denied! Not enough free parking spots for all assets at the moment.", self.alias) MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:E(self.wid..text) - okay=false + + return false end end -- Set chosen assets. - request.assets=_assets + request.cargoassets=_assets request.cargoattribute=_assetattribute request.cargocategory=_assetcategory @@ -3575,6 +3581,51 @@ function WAREHOUSE:_CheckRequestNow(request) local _transports=self:_GetTransportsForAssets(request) + -- Check if enough transport units are available. + if _transports==0 then + local text=string.format("Warehouse %s: Request denied! Not enough transport assets currently available.", self.alias) + MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:E(self.wid..text) + + return false + end + + -- Check if at least one transport asset is available. + if #_transports>0 then + + -- Get the attibute of the transport units. + local _transportattribute=_transports[1].attribute + local _transportcategory=_transports[1].category + + -- Check available parking for transport units. + if self.airbase and (_transportcategory==Group.Category.AIRPLANE or _transportcategory==Group.Category.HELICOPTER) then + local Parking=self:_FindParkingForAssets(self.airbase,_transports) + if Parking==nil then + local text=string.format("Warehouse %s: Request denied! Not enough free parking spots for all transports at the moment.", self.alias) + MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:E(self.wid..text) + + return false + end + end + + -- Set chosen assets. + request.transportassets=_transports + request.transportattribute=_transportattribute + request.transportcategory=_transportcategory + + else + + -- Not enough or the right transport carriers. + local text=string.format("Warehouse %s: Request denied! Not enough transport carriers available at the moment.", self.alias) + MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:E(self.wid..text) + + return false + + + end + elseif false then -- Transports in stock. @@ -3585,7 +3636,8 @@ function WAREHOUSE:_CheckRequestNow(request) local text=string.format("Warehouse %s: Request denied! Not enough transport assets currently available.", self.alias) MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:E(self.wid..text) - okay=false + + return false end -- Check if at least one transport asset is available. @@ -3602,7 +3654,8 @@ function WAREHOUSE:_CheckRequestNow(request) local text=string.format("Warehouse %s: Request denied! Not enough free parking spots for all transports at the moment.", self.alias) MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:E(self.wid..text) - okay=false + + return false end end @@ -3612,7 +3665,7 @@ function WAREHOUSE:_CheckRequestNow(request) end - return okay + return true end ---Get (optimized) transport carriers for the given assets to be transported. @@ -3623,7 +3676,7 @@ function WAREHOUSE:_GetTransportsForAssets(request) -- Get all transports of the requested type in stock. local transports=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, request.transporttype) - local cargoassets=request.assets + local cargoassets=request.cargoassets -- Problems/questions -- 1. Do we have at least one carrier big enough to transport the largest group? @@ -3647,6 +3700,7 @@ function WAREHOUSE:_GetTransportsForAssets(request) -- The most simple way is to sort the transports in descending order wrt. to their cargo bay size. -- Use largest carriers available until either number of cargo is done in one run or we hit max number of carriers available. + -- sort transport carriers w.r.t. cargo bay size. local function sort_transports(a,b) @@ -3663,7 +3717,9 @@ function WAREHOUSE:_GetTransportsForAssets(request) -- Very simple! Only take the largest transports that can carrier the largest cargo. local used_transports={} - local maxcargoweight=cargoassets[1] + + local maxcargoweight=cargoassets[1].weight + for i=1,#transports do local transport=transports[i] --#WAREHOUSE.Assetitem if transport.cargobay>maxcargoweight and #used_transports<=request.ntransport then @@ -3675,7 +3731,8 @@ function WAREHOUSE:_GetTransportsForAssets(request) local transport=_transport --#WAREHOUSE.Assetitem --env.info("transport used = ", transport.) end - + + return used_transports end ---Sorts the queue and checks if the request can be fulfilled. @@ -3726,17 +3783,20 @@ end --- Simple task function. Can be used to call a function which has the warehouse and the executing group as parameters. -- @param #WAREHOUSE self -- @param #string Function The name of the function to call passed as string. -function WAREHOUSE:_SimpleTaskFunction(Function) +-- @param Wrapper.Group#GROUP group The group which is meant. +function WAREHOUSE:_SimpleTaskFunction(Function, group) self:F2({Function}) -- Name of the warehouse (static) object. local warehouse=self.warehouse:GetName() + local groupname=group:GetName() -- Task script. local DCSScript = {} - DCSScript[#DCSScript+1] = string.format('env.info("WAREHOUSE: Simple task function called!") ') - DCSScript[#DCSScript+1] = string.format('local mygroup = GROUP:Find( ... ) ') -- The group that executes the task function. Very handy with the "...". - DCSScript[#DCSScript+1] = string.format('local mystatic=STATIC:FindByName(%s) ', warehouse) -- The static that holds the warehouse self object. + --DCSScript[#DCSScript+1] = string.format('env.info("WAREHOUSE: Simple task function called!") ') + --DCSScript[#DCSScript+1] = string.format('local mygroup = GROUP:Find( ... ) ') -- The group that executes the task function. Very handy with the "...". + DCSScript[#DCSScript+1] = string.format('local mygroup = GROUP:FindByName(%s) ', groupname) -- The group that executes the task function. Very handy with the "...". + DCSScript[#DCSScript+1] = string.format('local mystatic = STATIC:FindByName(%s) ', warehouse) -- The static that holds the warehouse self object. DCSScript[#DCSScript+1] = string.format('local warehouse = mygroup:GetState(mystatic, "WAREHOUSE") ') -- Get the warehouse self object from the static. DCSScript[#DCSScript+1] = string.format('%s(warehouse, mygroup)', Function) -- Call the function, e.g. myfunction.(warehouse,mygroup) From 42b04dedaaf667a633d71a6a31596a3aaea29d33 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Thu, 30 Aug 2018 06:07:26 +0200 Subject: [PATCH 296/420] Optimized boarding logic. Now it works correct. Adjust speed at the end to ensure that vehicles park closer to the cargo when carriers are in formation. --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 57 ++++++++++--------- .../Moose/Wrapper/Controllable.lua | 2 + 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 19e674583..73a94800d 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -418,8 +418,12 @@ function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To ) if APC and APC:IsAlive() then self.APC_Cargo = {} + self.APC_Cargo_Weight = {} for _, APCUnit in pairs( APC:GetUnits() ) do local APCUnit = APCUnit -- Wrapper.Unit#UNIT + + self.APC_Cargo_Weight[APCUnit] = APCUnit:GetCargoBayFreeWeight() + --for _, Cargo in pairs( self.CargoSet:GetSet() ) do for _, Cargo in UTILS.spairs( self.CargoSet:GetSet(), function( t, a, b ) return t[a]:GetWeight() > t[b]:GetWeight() end ) do local Cargo = Cargo -- Cargo.Cargo#CARGO @@ -428,7 +432,7 @@ function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To ) if Cargo:IsInLoadRadius( APCUnit:GetCoordinate() ) then self:F( { "In radius", APCUnit:GetName() } ) - local CargoBayFreeWeight = APCUnit:GetCargoBayFreeWeight() + local CargoBayFreeWeight = self.APC_Cargo_Weight[APCUnit] local CargoWeight = Cargo:GetWeight() self:F({CargoBayFreeWeight=CargoBayFreeWeight}) @@ -438,11 +442,12 @@ function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To ) APC:RouteStop() --Cargo:Ungroup() Cargo:Board( APCUnit, 25 ) - self:__Board( 1, Cargo ) + self:__Board( 1, Cargo, APCUnit ) -- So now this APCUnit has Cargo that is being loaded. -- This will be used further in the logic to follow and to check cargo status. self.APC_Cargo[APCUnit] = Cargo + self.APC_Cargo_Weight[APCUnit] = self.APC_Cargo_Weight[APCUnit] - CargoWeight Boarding = true break end @@ -462,35 +467,34 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param #string Cargo.Cargo#CARGO Cargo Cargo object. -function AI_CARGO_APC:onafterBoard( APC, From, Event, To, Cargo ) - self:F( { APC, From, Event, To, Cargo } ) +-- @param Cargo.Cargo#CARGO Cargo Cargo object. +-- @param Wrapper.Unit#UNIT APCUnit +function AI_CARGO_APC:onafterBoard( APC, From, Event, To, Cargo, APCUnit ) + self:F( { APC, From, Event, To, Cargo, APCUnit:GetName() } ) if APC and APC:IsAlive() then self:F({ IsLoaded = Cargo:IsLoaded(), Cargo:GetName(), APC:GetName() } ) if not Cargo:IsLoaded() then - self:__Board( 10, Cargo ) + self:__Board( 10, Cargo, APCUnit ) else - for _, APCUnit in pairs( APC:GetUnits() ) do - local APCUnit = APCUnit -- Wrapper.Unit#UNIT - for _, Cargo in UTILS.spairs( self.CargoSet:GetSet(), function( t, a, b ) return t[a]:GetWeight() > t[b]:GetWeight() end ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - if Cargo:IsUnLoaded() then - if Cargo:IsInLoadRadius( APCUnit:GetCoordinate() ) then - local CargoBayFreeWeight = APCUnit:GetCargoBayFreeWeight() - local CargoWeight = Cargo:GetWeight() - - self:F({CargoBayFreeWeight=CargoBayFreeWeight}) + for _, Cargo in UTILS.spairs( self.CargoSet:GetSet(), function( t, a, b ) return t[a]:GetWeight() > t[b]:GetWeight() end ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + if Cargo:IsUnLoaded() then + if Cargo:IsInLoadRadius( APCUnit:GetCoordinate() ) then + local CargoBayFreeWeight = self.APC_Cargo_Weight[APCUnit] + local CargoWeight = Cargo:GetWeight() + + self:F({CargoBayFreeWeight=CargoBayFreeWeight}) - -- Only when there is space within the bay to load the next cargo item! - if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then - Cargo:Board( APCUnit, 25 ) - self:__Board( 10, Cargo ) - -- So now this APCUnit has Cargo that is being loaded. - -- This will be used further in the logic to follow and to check cargo status. - self.APC_Cargo[APCUnit] = Cargo - return - end + -- Only when there is space within the bay to load the next cargo item! + if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then + Cargo:Board( APCUnit, 25 ) + self:__Board( 10, Cargo, APCUnit ) + -- So now this APCUnit has Cargo that is being loaded. + -- This will be used further in the logic to follow and to check cargo status. + self.APC_Cargo[APCUnit] = Cargo + self.APC_Cargo_Weight[APCUnit] = self.APC_Cargo_Weight[APCUnit] - CargoWeight + return end end end @@ -552,6 +556,7 @@ function AI_CARGO_APC:onafterUnload( APC, From, Event, To, Deployed ) for _, Cargo in pairs( APCUnit:GetCargo() ) do if Cargo:IsLoaded() then Cargo:UnBoard() + Cargo:SetDeployed( true ) self:__Unboard( 10, Cargo, Deployed ) end end @@ -580,6 +585,7 @@ function AI_CARGO_APC:onafterUnboard( APC, From, Event, To, Cargo, Deployed ) for _, Cargo in pairs( APCUnit:GetCargo() ) do if Cargo:IsLoaded() then Cargo:UnBoard() + Cargo:SetDeployed( true ) self:__Unboard( 10, Cargo, Deployed ) return end @@ -604,7 +610,6 @@ function AI_CARGO_APC:onbeforeUnloaded( APC, From, Event, To, Cargo, Deployed ) self:F( { APC, From, Event, To, Cargo:GetName(), Deployed = Deployed } ) local AllUnloaded = true - Cargo:SetDeployed( true ) --Cargo:Regroup() diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 84c25a1f4..a6f83c5c0 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -2062,6 +2062,8 @@ do -- Route methods local dist=ToCoordinate:Get2DDistance(PathOnRoad[#PathOnRoad-1]) if dist>10 then table.insert(route, ToCoordinate:WaypointGround(Speed, OffRoadFormation)) + table.insert(route, ToCoordinate:GetRandomCoordinateInRadius(10,5):WaypointGround(5, OffRoadFormation)) + table.insert(route, ToCoordinate:GetRandomCoordinateInRadius(10,5):WaypointGround(5, OffRoadFormation)) end end From 1ba84003d376fdebd5ec28421f9a1a0f08232614 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 30 Aug 2018 16:40:59 +0200 Subject: [PATCH 297/420] Warehouse v0.3.0w --- .../Moose/Functional/Warehouse.lua | 44 ++++--------------- 1 file changed, 9 insertions(+), 35 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index a56574a36..a1f1efdeb 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -54,18 +54,6 @@ -- -- === -- --- # Demo Missions --- --- ### None. --- --- === --- --- # YouTube Channel --- --- ### None. --- --- === --- -- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_Main.jpg) -- -- # The Warehouse Concept @@ -495,7 +483,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.3.0" +WAREHOUSE.version="0.3.0w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -1804,17 +1792,11 @@ end -- @param #string Assignment A keyword or text that function WAREHOUSE:onafterAddRequest(From, Event, To, warehouse, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType, nTransport, Assignment, Prio) - -- Defaults. - nAsset=nAsset or 1 - TransportType=TransportType or WAREHOUSE.TransportType.SELFPROPELLED - Prio=Prio or 50 - if nTransport==nil then - if TransportType==WAREHOUSE.TransportType.SELFPROPELLED then - nTransport=0 - else - nTransport=1 - end - end + -- Self request? + local toself=false + if self.warehouse:GetName()==warehouse:GetName() then + toself=true + end -- Increase id. self.queueid=self.queueid+1 @@ -1832,7 +1814,9 @@ function WAREHOUSE:onafterAddRequest(From, Event, To, warehouse, AssetDescriptor transporttype=TransportType, ntransport=nTransport, ndelivered=0, - ntransporthome=0 + ntransporthome=0, + assets={}, + toself=toself, } --#WAREHOUSE.Queueitem -- Add request to queue. @@ -1884,16 +1868,6 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request) end - -- Init asset table. - Request.assets={} - - -- Init self request. - if self.warehouse:GetName()==Request.warehouse.warehouse:GetName() then - Request.toself=true - else - Request.toself=false - end - return true end From d4d05f469336f987071ed1b55a261f66c94ab77e Mon Sep 17 00:00:00 2001 From: FlightControl Date: Thu, 30 Aug 2018 20:48:14 +0200 Subject: [PATCH 298/420] Updates on cargo handling of core engine. --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 14 +-- .../Moose/AI/AI_Cargo_Airplane.lua | 110 ++++++++++-------- .../Moose/AI/AI_Cargo_Helicopter.lua | 45 ++++--- 3 files changed, 89 insertions(+), 80 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 73a94800d..c56ee235d 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -418,11 +418,11 @@ function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To ) if APC and APC:IsAlive() then self.APC_Cargo = {} - self.APC_Cargo_Weight = {} for _, APCUnit in pairs( APC:GetUnits() ) do local APCUnit = APCUnit -- Wrapper.Unit#UNIT - self.APC_Cargo_Weight[APCUnit] = APCUnit:GetCargoBayFreeWeight() + local CargoBayFreeWeight = APCUnit:GetCargoBayFreeWeight() + self:F({CargoBayFreeWeight=CargoBayFreeWeight}) --for _, Cargo in pairs( self.CargoSet:GetSet() ) do for _, Cargo in UTILS.spairs( self.CargoSet:GetSet(), function( t, a, b ) return t[a]:GetWeight() > t[b]:GetWeight() end ) do @@ -432,10 +432,7 @@ function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To ) if Cargo:IsInLoadRadius( APCUnit:GetCoordinate() ) then self:F( { "In radius", APCUnit:GetName() } ) - local CargoBayFreeWeight = self.APC_Cargo_Weight[APCUnit] local CargoWeight = Cargo:GetWeight() - - self:F({CargoBayFreeWeight=CargoBayFreeWeight}) -- Only when there is space within the bay to load the next cargo item! if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then @@ -447,7 +444,6 @@ function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To ) -- So now this APCUnit has Cargo that is being loaded. -- This will be used further in the logic to follow and to check cargo status. self.APC_Cargo[APCUnit] = Cargo - self.APC_Cargo_Weight[APCUnit] = self.APC_Cargo_Weight[APCUnit] - CargoWeight Boarding = true break end @@ -477,14 +473,13 @@ function AI_CARGO_APC:onafterBoard( APC, From, Event, To, Cargo, APCUnit ) if not Cargo:IsLoaded() then self:__Board( 10, Cargo, APCUnit ) else + local CargoBayFreeWeight = APCUnit:GetCargoBayFreeWeight() + self:F({CargoBayFreeWeight=CargoBayFreeWeight}) for _, Cargo in UTILS.spairs( self.CargoSet:GetSet(), function( t, a, b ) return t[a]:GetWeight() > t[b]:GetWeight() end ) do local Cargo = Cargo -- Cargo.Cargo#CARGO if Cargo:IsUnLoaded() then if Cargo:IsInLoadRadius( APCUnit:GetCoordinate() ) then - local CargoBayFreeWeight = self.APC_Cargo_Weight[APCUnit] local CargoWeight = Cargo:GetWeight() - - self:F({CargoBayFreeWeight=CargoBayFreeWeight}) -- Only when there is space within the bay to load the next cargo item! if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then @@ -493,7 +488,6 @@ function AI_CARGO_APC:onafterBoard( APC, From, Event, To, Cargo, APCUnit ) -- So now this APCUnit has Cargo that is being loaded. -- This will be used further in the logic to follow and to check cargo status. self.APC_Cargo[APCUnit] = Cargo - self.APC_Cargo_Weight[APCUnit] = self.APC_Cargo_Weight[APCUnit] - CargoWeight return end end diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 219cdd8bf..a43d08bc2 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -355,35 +355,44 @@ function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Airbase, Sp end ---- On after Load event. Checks if cargo is inside the load radius and if so starts the boarding process. +--- On before Load event. Checks if cargo is inside the load radius and if so starts the boarding process. -- @param #AI_CARGO_AIRPLANE self -- @param Wrapper.Group#GROUP Airplane Transport plane. -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Wrapper.Point#COORDINATE Coordinate Place where the cargo is guided to if it is inside the load radius. -function AI_CARGO_AIRPLANE:onafterLoad( Airplane, From, Event, To, Coordinate ) +function AI_CARGO_AIRPLANE:onbeforeLoad( Airplane, From, Event, To, Coordinate ) + + local Boarding = false + if Airplane and Airplane:IsAlive() ~= nil then - for _,_Cargo in pairs( self.CargoSet:GetSet() ) do - self:F({_Cargo:GetName()}) - local Cargo=_Cargo --Cargo.Cargo#CARGO - local InRadius = Cargo:IsInLoadRadius( Coordinate ) - if InRadius then - - -- Is there a cargo still unloaded? - if Cargo:IsUnLoaded() == true then - - self:__Board( 5, Cargo ) - Cargo:Board( Airplane, 25 ) - break + for _, AirplaneUnit in pairs( Airplane:GetUnits() ) do + local AirplaneUnit = AirplaneUnit -- Wrapper.Unit#UNIT + for _,_Cargo in pairs( self.CargoSet:GetSet() ) do + self:F({_Cargo:GetName()}) + local Cargo=_Cargo --Cargo.Cargo#CARGO + local InRadius = Cargo:IsInLoadRadius( Coordinate ) + if InRadius then + + -- Is there a cargo still unloaded? + if Cargo:IsUnLoaded() == true then + + Cargo:Board( AirplaneUnit, 25 ) + self:__Board( 5, Cargo, AirplaneUnit ) + Boarding = true + break + end end + end - end end + return Boarding + end --- On after Board event. Cargo is inside the load radius and boarding is performed. @@ -392,17 +401,22 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -function AI_CARGO_AIRPLANE:onafterBoard( Airplane, From, Event, To, Cargo ) +-- @param Cargo.Cargo#CARGO Cargo Cargo object. +-- @param Wrapper.Unit#UNIT AirplaneUnit +function AI_CARGO_AIRPLANE:onafterBoard( Airplane, From, Event, To, Cargo, AirplaneUnit ) if Airplane and Airplane:IsAlive() then self:F({ IsLoaded = Cargo:IsLoaded() } ) if not Cargo:IsLoaded() then - self:__Board( 10, Cargo ) + self:__Board( 10, Cargo, AirplaneUnit ) else + local CargoBayFreeWeight = AirplaneUnit:GetCargoBayFreeWeight() + self:F({CargoBayFreeWeight=CargoBayFreeWeight}) + -- Check if another cargo can be loaded into the airplane. - for _,_Cargo in pairs( self.CargoSet:GetSet() ) do + for _,_Cargo in UTILS.spairs( self.CargoSet:GetSet(), function( t, a, b ) return t[a]:GetWeight() > t[b]:GetWeight() end ) do self:F({_Cargo:GetName()}) local Cargo =_Cargo --Cargo.Cargo#CARGO @@ -465,18 +479,20 @@ end function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To ) if Airplane and Airplane:IsAlive() then - local Cargos = Airplane:GetCargo() - for CargoID, Cargo in pairs( Cargos ) do - - local Angle = 180 - local CargoCarrierHeading = Airplane:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - self:T( { CargoCarrierHeading, CargoDeployHeading } ) - local CargoDeployCoordinate = Airplane:GetPointVec2():Translate( 150, CargoDeployHeading ) - - Cargo:UnBoard( CargoDeployCoordinate ) - Cargo:SetDeployed( true ) - self:__Unboard( 10, Cargo ) + for _, AirplaneUnit in pairs( Airplane:GetUnits() ) do + local Cargos = AirplaneUnit:GetCargo() + for CargoID, Cargo in pairs( Cargos ) do + + local Angle = 180 + local CargoCarrierHeading = Airplane:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + self:T( { CargoCarrierHeading, CargoDeployHeading } ) + local CargoDeployCoordinate = Airplane:GetPointVec2():Translate( 150, CargoDeployHeading ) + + Cargo:UnBoard( CargoDeployCoordinate ) + Cargo:SetDeployed( true ) + self:__Unboard( 10, Cargo ) + end end end @@ -496,22 +512,24 @@ function AI_CARGO_AIRPLANE:onafterUnboard( Airplane, From, Event, To, Cargo ) if not Cargo:IsUnLoaded() then self:__Unboard( 10, Cargo ) else - local Cargos = Airplane:GetCargo() - for CargoID, Cargo in pairs( Cargos ) do - if Cargo:IsLoaded() then - local Angle = 180 - local CargoCarrierHeading = Airplane:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - self:T( { CargoCarrierHeading, CargoDeployHeading } ) - local CargoDeployCoordinate = Airplane:GetPointVec2():Translate( 150, CargoDeployHeading ) - Cargo:UnBoard( CargoDeployCoordinate ) - Cargo:SetDeployed( true ) - - self:__Unboard( 10, Cargo ) - return - end - end - self:__Unloaded( 1, Cargo ) + for _, AirplaneUnit in pairs( Airplane:GetUnits() ) do + local Cargos = AirplaneUnit:GetCargo() + for CargoID, Cargo in pairs( Cargos ) do + if Cargo:IsLoaded() then + local Angle = 180 + local CargoCarrierHeading = Airplane:GetHeading() -- Get Heading of object in degrees. + local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) + self:T( { CargoCarrierHeading, CargoDeployHeading } ) + local CargoDeployCoordinate = Airplane:GetPointVec2():Translate( 150, CargoDeployHeading ) + Cargo:UnBoard( CargoDeployCoordinate ) + Cargo:SetDeployed( true ) + + self:__Unboard( 10, Cargo ) + return + end + end + self:__Unloaded( 1, Cargo ) + end end end end diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 0148e9c79..6c9d5a15e 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -390,6 +390,9 @@ function AI_CARGO_HELICOPTER:onbeforeLoad( Helicopter, From, Event, To) if Helicopter and Helicopter:IsAlive() then for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT + local CargoBayFreeWeight = HelicopterUnit:GetCargoBayFreeWeight() + self:F({CargoBayFreeWeight=CargoBayFreeWeight}) + for _, Cargo in pairs( self.CargoSet:GetSet() ) do local Cargo = Cargo -- Cargo.Cargo#CARGO self:F( { IsUnLoaded = Cargo:IsUnLoaded() } ) @@ -397,17 +400,14 @@ function AI_CARGO_HELICOPTER:onbeforeLoad( Helicopter, From, Event, To) if Cargo:IsInLoadRadius( HelicopterUnit:GetCoordinate() ) then self:F( { "In radius", HelicopterUnit:GetName() } ) - local CargoBayFreeWeight = HelicopterUnit:GetCargoBayFreeWeight() local CargoWeight = Cargo:GetWeight() - self:F({CargoBayFreeWeight=CargoBayFreeWeight}) - -- Only when there is space within the bay to load the next cargo item! if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then --Cargo:Ungroup() Cargo:Board( HelicopterUnit, 25 ) - self:__Board( 1, Cargo ) + self:__Board( 1, Cargo, HelicopterUnit ) self.Helicopter_Cargo[HelicopterUnit] = Cargo Boarding = true break @@ -430,32 +430,29 @@ end -- @param #string Event Event. -- @param #string To To state. -- @param Cargo.Cargo#CARGO Cargo Cargo object. -function AI_CARGO_HELICOPTER:onafterBoard( Helicopter, From, Event, To, Cargo ) - self:F( { Helicopter, From, Event, To, Cargo } ) +-- @param Wrapper.Unit#UNIT HelicopterUnit +function AI_CARGO_HELICOPTER:onafterBoard( Helicopter, From, Event, To, Cargo, HelicopterUnit ) + self:F( { Helicopter, From, Event, To, Cargo, HelicopterUnit } ) if Helicopter and Helicopter:IsAlive() then self:F({ IsLoaded = Cargo:IsLoaded() } ) if not Cargo:IsLoaded() then - self:__Board( 10, Cargo ) + self:__Board( 10, Cargo, HelicopterUnit ) else - for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do - local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT - for _, Cargo in pairs( self.CargoSet:GetSet() ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - if Cargo:IsUnLoaded() then - if Cargo:IsInLoadRadius( HelicopterUnit:GetCoordinate() ) then - local CargoBayFreeWeight = HelicopterUnit:GetCargoBayFreeWeight() - local CargoWeight = Cargo:GetWeight() + local CargoBayFreeWeight = HelicopterUnit:GetCargoBayFreeWeight() + self:F({CargoBayFreeWeight=CargoBayFreeWeight}) + for _, Cargo in pairs( self.CargoSet:GetSet() ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + if Cargo:IsUnLoaded() then + if Cargo:IsInLoadRadius( HelicopterUnit:GetCoordinate() ) then + local CargoWeight = Cargo:GetWeight() - self:F({CargoBayFreeWeight=CargoBayFreeWeight}) - - -- Only when there is space within the bay to load the next cargo item! - if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then - Cargo:Board( HelicopterUnit, 25 ) - self:__Board( 10, Cargo ) - self.Helicopter_Cargo[HelicopterUnit] = Cargo - return - end + -- Only when there is space within the bay to load the next cargo item! + if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then + Cargo:Board( HelicopterUnit, 25 ) + self:__Board( 10, Cargo, HelicopterUnit ) + self.Helicopter_Cargo[HelicopterUnit] = Cargo + return end end end From 97feeaeaf3048b3f3d722beb1c94ec9e56a89e89 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Thu, 30 Aug 2018 20:56:10 +0200 Subject: [PATCH 299/420] Update --- Moose Development/Moose/AI/AI_Cargo_Airplane.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index a43d08bc2..8ce9bcb42 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -428,7 +428,7 @@ function AI_CARGO_AIRPLANE:onafterBoard( Airplane, From, Event, To, Cargo, Airpl local InRadius = Cargo:IsInLoadRadius( Airplane:GetCoordinate() ) if InRadius then - local CargoBayFreeWeight = Airplane:GetCargoBayFreeWeight() + local CargoBayFreeWeight = AirplaneUnit:GetCargoBayFreeWeight() --local CargoBayFreeVolume = Airplane:GetCargoBayFreeVolume() local CargoWeight = Cargo:GetWeight() From d51690a3cfe81f13e456e2a683dc9f5761e1ca02 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Fri, 31 Aug 2018 00:42:15 +0200 Subject: [PATCH 300/420] Warehouse v0.3.1 Ships back to stock is now working. Fixed some bugs. --- .../Moose/Functional/Warehouse.lua | 197 ++++++++++-------- 1 file changed, 105 insertions(+), 92 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index a1f1efdeb..7e2afb5ee 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -48,6 +48,7 @@ -- @field #table defending Table holding all defending requests, i.e. self requests that were if the warehouse is under attack. Table elements are of type @{#WAREHOUSE.Pendingitem}. -- @field Core.Zone#ZONE portzone Zone defining the port of a warehouse. This is where naval assets are spawned. -- @field #table shippinglanes Table holding the user defined shipping between warehouses. +-- @field #boolean selfdefence When the warehouse is under attack, automatically spawn assets to defend the warehouse. -- @extends Core.Fsm#FSM --- Have your assets at the right place at the right time - or not! @@ -321,32 +322,33 @@ -- -- @field #WAREHOUSE WAREHOUSE = { - ClassName = "WAREHOUSE", - Debug = false, - Report = true, - warehouse = nil, - coalition = nil, - country = nil, - alias = nil, - zone = nil, - airbase = nil, - airbasename = nil, - category = -1, - coordinate = nil, - road = nil, - rail = nil, - spawnzone = nil, - wid = nil, - uid = nil, - markerid = nil, - dTstatus = 30, - queueid = 0, - stock = {}, - queue = {}, - pending = {}, - defending = {}, - portzone = nil, - shippinglanes = {}, + ClassName = "WAREHOUSE", + Debug = false, + Report = true, + warehouse = nil, + coalition = nil, + country = nil, + alias = nil, + zone = nil, + airbase = nil, + airbasename = nil, + category = -1, + coordinate = nil, + road = nil, + rail = nil, + spawnzone = nil, + wid = nil, + uid = nil, + markerid = nil, + dTstatus = 30, + queueid = 0, + stock = {}, + queue = {}, + pending = {}, + defending = {}, + portzone = nil, + shippinglanes = {}, + selfdefence = false, } --- Item of the warehouse stock table. @@ -369,15 +371,16 @@ WAREHOUSE = { --- Item of the warehouse queue table. -- @type WAREHOUSE.Queueitem -- @field #number uid Unique id of the queue item. --- @field #number prio Priority of the request. -- @field #WAREHOUSE warehouse Requesting warehouse. --- @field Wrapper.Airbase#AIRBASE airbase Requesting airbase or airbase beloning to requesting warehouse. --- @field DCS#Airbase.Category category Category of the requesting airbase, i.e. airdrome, helipad/farp or ship. -- @field #WAREHOUSE.Descriptor assetdesc Descriptor of the requested asset. Enumerator of type @{#WAREHOUSE.Descriptor}. -- @field assetdescval Value of the asset descriptor. Type depends on "assetdesc" descriptor. -- @field #number nasset Number of asset groups requested. -- @field #WAREHOUSE.TransportType transporttype Transport unit type. -- @field #number ntransport Max. number of transport units requested. +-- @field #string assignment A keyword or text that later be used to identify this request and postprocess the assets. +-- @field #number prio Priority of the request. Number between 1 (high) and 100 (low). +-- @field Wrapper.Airbase#AIRBASE airbase The airbase beloning to requesting warehouse if any. +-- @field DCS#Airbase.Category category Category of the requesting airbase, i.e. airdrome, helipad/farp or ship. -- @field #boolean toself Self request, i.e. warehouse requests assets from itself. -- @field #table assets Table of self propelled (or cargo) and transport assets. Each element of the table is a @{#WAREHOUSE.Assetitem} and can be accessed by their asset ID. -- @field #table cargoassets Table of cargo (or self propelled) assets. Each element of the table is a @{#WAREHOUSE.Assetitem}. @@ -483,14 +486,16 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.3.0w" +WAREHOUSE.version="0.3.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Warehouse re-capturing not working?! --- TODO: Naval assets dont go back into stock once arrived. +-- TODO: How to get a specific request once the cargo is delivered? Make addrequest addasset non FSM function? Callback for requests like in SPAWN? +-- TODO: Add autoselfdefence switch and user function. Default should be off. +-- DONE: Warehouse re-capturing not working?! +-- DONE: Naval assets dont go back into stock once arrived. -- TODO: Take cargo weight into consideration, when selecting transport assets. -- TODO: Add transport units from dispatchers back to warehouse stock once they completed their mission. -- DONE: Add ports for spawning naval assets. @@ -500,14 +505,14 @@ WAREHOUSE.version="0.3.0w" -- TODO: Add possibility to add active groups. Need to create a pseudo template before destroy. -- TODO: Write documentation. -- TODO: Handle the case when units of a group die during the transfer. Adjust template?! See Grouping in SPAWN. --- TODO: Handle cases with immobile units. +-- DONE: Handle cases with immobile units <== should be handled by dispatcher classes. -- TODO: Handle cargo crates. -- TODO: Handle cases for aircraft carriers and other ships. Place warehouse on carrier possible? On others probably not - exclude them? -- TODO: Add general message function for sending to coaliton or debug. -- TODO: Fine tune event handlers. -- TODO: Add save/load capability of warehouse <==> percistance after mission restart. -- DONE: Improve generalized attributes. --- TODO: Add a time stamp when an asset is added to the stock and for requests +-- TODO: Add a time stamp when an asset is added to the stock and for requests. -- DONE: If warehouse is destoyed, all asssets are gone. -- DONE: Add event handlers. -- DONE: Add AI_CARGO_AIRPLANE @@ -676,6 +681,7 @@ function WAREHOUSE:New(warehouse, alias) -- @param #number nAsset Number of groups requested that match the asset specification. -- @param #WAREHOUSE.TransportType TransportType Type of transport. -- @param #number nTransport Number of transport units requested. + -- @param #string Assignment A keyword or text that later be used to identify this request and postprocess the assets. -- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. --- Triggers the FSM event "AddRequest" with a delay. Add a request to the warehouse queue, which is processed when possible. @@ -688,6 +694,7 @@ function WAREHOUSE:New(warehouse, alias) -- @param #number nAsset Number of groups requested that match the asset specification. -- @param #WAREHOUSE.TransportType TransportType Type of transport. -- @param #number nTransport Number of transport units requested. + -- @param #string Assignment A keyword or text that later be used to identify this request and postprocess the assets. -- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. @@ -1708,21 +1715,9 @@ end -- @param #WAREHOUSE.TransportType TransportType Type of transport. -- @param #number nTransport Number of transport units requested. -- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. --- @param #string Assignment A keyword or text that +-- @param #string Assignment A keyword or text that later be used to identify this request and postprocess the assets. -- @return #boolean If true, request is okay at first glance. function WAREHOUSE:onbeforeAddRequest(From, Event, To, warehouse, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType, nTransport, Assignment, Prio) - - -- Defaults. - nAsset=nAsset or 1 - TransportType=TransportType or WAREHOUSE.TransportType.SELFPROPELLED - Prio=Prio or 50 - if nTransport==nil then - if TransportType==WAREHOUSE.TransportType.SELFPROPELLED then - nTransport=0 - else - nTransport=1 - end - end -- Request is okay. local okay=true @@ -1788,16 +1783,33 @@ end -- @param #number nAsset Number of groups requested that match the asset specification. -- @param #WAREHOUSE.TransportType TransportType Type of transport. -- @param #number nTransport Number of transport units requested. +-- @param #string Assignment A keyword or text that later be used to identify this request and postprocess the assets. -- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. --- @param #string Assignment A keyword or text that function WAREHOUSE:onafterAddRequest(From, Event, To, warehouse, AssetDescriptor, AssetDescriptorValue, nAsset, TransportType, nTransport, Assignment, Prio) + -- Defaults. + nAsset=nAsset or 1 + TransportType=TransportType or WAREHOUSE.TransportType.SELFPROPELLED + Prio=Prio or 50 + if nTransport==nil then + if TransportType==WAREHOUSE.TransportType.SELFPROPELLED then + nTransport=0 + else + nTransport=1 + end + end + + -- Not more transports than assets. + --if type(nAsset)=="number" then + -- nTransport=math.min(nAsset, nTransport) + --end + -- Self request? local toself=false - if self.warehouse:GetName()==warehouse:GetName() then + if self.warehouse:GetName()==warehouse.warehouse:GetName() then toself=true - end - + end + -- Increase id. self.queueid=self.queueid+1 @@ -1806,13 +1818,14 @@ function WAREHOUSE:onafterAddRequest(From, Event, To, warehouse, AssetDescriptor uid=self.queueid, prio=Prio, warehouse=warehouse, - airbase=warehouse.airbase, - category=warehouse.category, assetdesc=AssetDescriptor, assetdescval=AssetDescriptorValue, nasset=nAsset, transporttype=TransportType, ntransport=nTransport, + assignment=tostring(Assignment), + airbase=warehouse.airbase, + category=warehouse.category, ndelivered=0, ntransporthome=0, assets={}, @@ -2544,7 +2557,9 @@ function WAREHOUSE:onafterAttacked(From, Event, To, Coalition, Country) self:I(self.wid..text) -- Spawn all ground units in the spawnzone? - self:AddRequest(self, WAREHOUSE.Descriptor.CATEGORY, Group.Category.GROUND, "all", nil, nil , 0) + if self.selfdefence then + self:AddRequest(self, WAREHOUSE.Descriptor.CATEGORY, Group.Category.GROUND, "all", nil, nil , 0) + end end --- On after "Defeated" event. Warehouse defeated an attack by another coalition. Defender assets are added back to warehouse stock. @@ -2561,10 +2576,7 @@ function WAREHOUSE:onafterDefeated(From, Event, To) --if self.defenderrequest then for _,request in pairs(self.defending) do - - -- Get all assets that were deployed for defending the warehouse. - --local request=self.defenderrequest --#WAREHOUSE.Pendingitem - + -- Route defenders back to warehoue (for visual reasons only) and put them back into stock. for _,_group in pairs(request.cargogroupset:GetSetObjects()) do local group=_group --Wrapper.Group#GROUP @@ -2579,9 +2591,6 @@ function WAREHOUSE:onafterDefeated(From, Event, To) self:__AddAsset(60, group) end - -- Set defender request back to nil. - --self.defenderrequest=nil - --self:_DeleteQueueItem(request, self.defending) end @@ -2664,7 +2673,7 @@ end function WAREHOUSE:onafterAirbaseRecaptured(From, Event, To, Coalition) -- Message. - local text=string.format("Warehouse %s: We recaptured our airbase %d from the enemy (coalition=%d)!", self.alias, self.airbasename, Coalition) + local text=string.format("Warehouse %s: We recaptured our airbase %s from the enemy (coalition=%d)!", self.alias, self.airbasename, Coalition) MESSAGE:New(text, 20):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:I(self.wid..text) @@ -2771,9 +2780,8 @@ function WAREHOUSE:_RouteNaval(group, request) end -- Task function triggering the arrived event at the last waypoint. - --local TaskFunction = group:TaskFunction("WAREHOUSE._Arrived", self) - local TaskFunction = self:_SimpleTaskFunction("WAREHOUSE:_ArrivedSimple", group) - + local TaskFunction = self:_SimpleTaskFunction("warehouse:_ArrivedSimple", group) + -- Put task function on last waypoint. local Waypoint = Waypoints[#Waypoints] group:SetTaskWaypoint(Waypoint, TaskFunction) @@ -3046,40 +3054,39 @@ end -- @param #WAREHOUSE self -- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventBaseCaptured(EventData) + self:T3(self.wid..string.format("Warehouse %s captured event base captured!",self.alias)) - -- This warehouse does not have an airbase and never had one. So i could not be captured. + -- This warehouse does not have an airbase and never had one. So it could not have been captured. if self.airbasename==nil then - -- This warehouse never had an airbase so I cannot have been captured. return end - self:E(self.wid..string.format("Warehouse %s captured event base captured!",self.alias)) - if EventData and EventData.Place then -- Place is the airbase that was captured. local airbase=EventData.Place --Wrapper.Airbase#AIRBASE + -- Check that this airbase belongs or did belong to this warehouse. if EventData.PlaceName==self.airbasename then - -- Okay, this airbase belongs or did belong to this warehouse. - - self:I(self.wid..string.format("Airbase of warehouse %s was captured! ",self.alias)) - + -- New coalition of airbase after it was captured. - local coalitionAirbase=airbase:GetCoalition() + local NewCoalitionAirbase=airbase:GetCoalition() + -- Debug info + self:I(self.wid..string.format("Airbase of warehouse %s (coalition = %d) was captured! New owner coalition = %d.",self.alias, self.coalition, NewCoalitionAirbase)) + -- So what can happen? -- Warehouse is blue, airbase is blue and belongs to warehouse and red captures it ==> self.airbase=nil -- Warehouse is blue, airbase is blue self.airbase is nil and blue (re-)captures it ==> self.airbase=Event.Place if self.airbase==nil then - -- Warehouse lost this airbase previously and not it was re-captured. - if coalitionAirbase == self.coalition then - self:AirbaseRecaptured(coalitionAirbase) + -- New coalition is the same as of the warehouse ==> warehouse previously lost this airbase and now it was re-captured. + if NewCoalitionAirbase == self.coalition then + self:AirbaseRecaptured(NewCoalitionAirbase) end else -- Captured airbase belongs to this warehouse but was captured by other coaltion. - if coalitionAirbase ~= self.coalition then - self:AirbaseCaptured(coalitionAirbase) + if NewCoalitionAirbase ~= self.coalition then + self:AirbaseCaptured(NewCoalitionAirbase) end end @@ -3353,7 +3360,9 @@ function WAREHOUSE:_CheckRequestValid(request) else - -- Check if enough parking spots are available + -- Check if enough parking spots are available. This checks the spots available in general, i.e. not the free spots. + -- TODO: For FARPS/ships, is it possible to send more assets than parking spots? E.g. a FARPS has only four (or even one). + -- TODO: maybe only check if spots > 0 for the necessary terminal type? At least for FARPS. -- Get necessary terminal type. local termtype=self:_GetTerminal(asset.attribute) @@ -3362,15 +3371,18 @@ function WAREHOUSE:_CheckRequestValid(request) local np_departure=self.airbase:GetParkingSpotsNumber(termtype) local np_destination=request.airbase:GetParkingSpotsNumber(termtype) + -- Debug info. + self:E(string.format("Asset attribute = %s, terminal type = %d, spots at departure = %d, destination = %d", asset.attribute, termtype, np_departure, np_destination)) + -- Not enough parking at sending warehouse. if np_departure < request.nasset then - self:E("ERROR: Incorrect request. No enough parking spots of terminal type at warehouse.") + self:E(string.format("ERROR: Incorrect request. Not enough parking spots of terminal type %d at warehouse. Available spots = %d.", termtype, np_departure)) valid=false end -- Not enough parking at requesting warehouse. if np_destination < request.nasset then - self:E("ERROR: Incorrect request. No enough parking spots of terminal type at requesting warehouse.") + self:E(string.format("ERROR: Incorrect request. Not enough parking spots of terminal type %d at requesting warehouse. Available spots = %d.", termtype, np_destination)) valid=false end @@ -3483,7 +3495,7 @@ function WAREHOUSE:_CheckRequestValid(request) -- Add request as unvalid and delete it later. if valid==false then - self:E(self.wid..string.format("Got invalid request id=%d.", request.uid)) + self:E(self.wid..string.format("ERROR: Got invalid request id=%d.", request.uid)) else self:T3(self.wid..string.format("Got valid request id=%d.", request.uid)) end @@ -3733,7 +3745,7 @@ function WAREHOUSE:_CheckQueue() -- Remember invalid request and delete later in order not to confuse the loop. if not valid then - table.insert(invalid,request) + table.insert(invalid, qitem) end -- Get the first valid request that can be executed now. @@ -3748,7 +3760,7 @@ function WAREHOUSE:_CheckQueue() for _,_request in pairs(invalid) do self:E(self.wid..string.format("Deleting invalid request id=%d.",_request.uid)) self:_DeleteQueueItem(_request, self.queue) - end + end -- Execute request. return request @@ -3767,12 +3779,11 @@ function WAREHOUSE:_SimpleTaskFunction(Function, group) -- Task script. local DCSScript = {} - --DCSScript[#DCSScript+1] = string.format('env.info("WAREHOUSE: Simple task function called!") ') - --DCSScript[#DCSScript+1] = string.format('local mygroup = GROUP:Find( ... ) ') -- The group that executes the task function. Very handy with the "...". - DCSScript[#DCSScript+1] = string.format('local mygroup = GROUP:FindByName(%s) ', groupname) -- The group that executes the task function. Very handy with the "...". - DCSScript[#DCSScript+1] = string.format('local mystatic = STATIC:FindByName(%s) ', warehouse) -- The static that holds the warehouse self object. - DCSScript[#DCSScript+1] = string.format('local warehouse = mygroup:GetState(mystatic, "WAREHOUSE") ') -- Get the warehouse self object from the static. - DCSScript[#DCSScript+1] = string.format('%s(warehouse, mygroup)', Function) -- Call the function, e.g. myfunction.(warehouse,mygroup) + --DCSScript[#DCSScript+1] = string.format('env.info(\"WAREHOUSE: Simple task function called!\") ') + DCSScript[#DCSScript+1] = string.format('local mygroup = GROUP:FindByName(\"%s\") ', groupname) -- The group that executes the task function. Very handy with the "...". + DCSScript[#DCSScript+1] = string.format("local mystatic = STATIC:FindByName(\"%s\") ", warehouse) -- The static that holds the warehouse self object. + DCSScript[#DCSScript+1] = string.format('local warehouse = mystatic:GetState(mystatic, \"WAREHOUSE\") ') -- Get the warehouse self object from the static. + DCSScript[#DCSScript+1] = string.format('%s(mygroup)', Function) -- Call the function, e.g. myfunction.(warehouse,mygroup) -- Create task. local DCSTask = CONTROLLABLE.TaskWrappedAction(self, CONTROLLABLE.CommandDoScript(self, table.concat(DCSScript))) @@ -4218,7 +4229,9 @@ function WAREHOUSE:_GetAttribute(groupname) elseif tanker then attribute=WAREHOUSE.Attribute.AIR_TANKER elseif transporthelo then - attribute=WAREHOUSE.Attribute.AIR_TRANSPORTHELO + attribute=WAREHOUSE.Attribute.AIR_TRANSPORTHELO + elseif attackhelicopter then + attribute=WAREHOUSE.Attribute.AIR_ATTACKHELO elseif apc then attribute=WAREHOUSE.Attribute.GROUND_APC elseif truck then From 075fe729aa7cd379594614723e1893afd786a3d2 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Fri, 31 Aug 2018 16:21:13 +0200 Subject: [PATCH 301/420] Warehouse v0.3.1w --- .../Moose/Functional/Warehouse.lua | 190 ++++++++++-------- 1 file changed, 101 insertions(+), 89 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 7e2afb5ee..3e6f7f219 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -486,7 +486,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.3.1" +WAREHOUSE.version="0.3.1w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -3564,18 +3564,9 @@ function WAREHOUSE:_CheckRequestNow(request) -- Check that a transport units. if request.transporttype ~= WAREHOUSE.TransportType.SELFPROPELLED then - + -- Get best transports for this asset pack. local _transports=self:_GetTransportsForAssets(request) - -- Check if enough transport units are available. - if _transports==0 then - local text=string.format("Warehouse %s: Request denied! Not enough transport assets currently available.", self.alias) - MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:E(self.wid..text) - - return false - end - -- Check if at least one transport asset is available. if #_transports>0 then @@ -3607,47 +3598,12 @@ function WAREHOUSE:_CheckRequestNow(request) MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:E(self.wid..text) - return false - - + return false end - - elseif false then - - -- Transports in stock. - local _transports,_ntransports,_enough=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, request.transporttype, request.ntransport) - -- Check if enough transport units are available. - if not _enough then - local text=string.format("Warehouse %s: Request denied! Not enough transport assets currently available.", self.alias) - MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:E(self.wid..text) - - return false - end - - -- Check if at least one transport asset is available. - if _ntransports>0 then - - -- Get the attibute of the transport units. - local _transportattribute=_transports[1].attribute - local _transportcategory=_transports[1].category - - -- Check available parking for transport units. - if self.airbase and (_transportcategory==Group.Category.AIRPLANE or _transportcategory==Group.Category.HELICOPTER) then - local Parking=self:_FindParkingForAssets(self.airbase,_transports) - if Parking==nil then - local text=string.format("Warehouse %s: Request denied! Not enough free parking spots for all transports at the moment.", self.alias) - MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:E(self.wid..text) - - return false - end - end - - end else - -- self propelled case. + + -- Self propelled case. Nothing to do for now. end @@ -3661,63 +3617,119 @@ function WAREHOUSE:_GetTransportsForAssets(request) -- Get all transports of the requested type in stock. local transports=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, request.transporttype) - - local cargoassets=request.cargoassets - - -- Problems/questions - -- 1. Do we have at least one carrier big enough to transport the largest group? - -- If not ==> No transport possible since groups cannot be split! - -- If yes ==> Tranport possible. - -- 2. How many carriers do we need? - -- ntransport should be the max number. - - -- Example 8, 8, 5, 3 - -- Carriers: - -- 2 that can take 8 can be used for 3, 5, 8 - -- 1 that can take 6 can be used for 3, 5, - - -- 3 that can take 4 can be used for 3, -, - - -- 1 that can take 2 can be used for -, -, - - - -- So the problem becomes: - -- How do I minimize the number of "ways" with the constraint of a fixed number of carriers? - -- Extreme cases: - -- Use just one carrier that can carrier the largest group. I would have to drive n times to get all cargo from A to B. - -- - - -- The most simple way is to sort the transports in descending order wrt. to their cargo bay size. - -- Use largest carriers available until either number of cargo is done in one run or we hit max number of carriers available. - - - -- sort transport carriers w.r.t. cargo bay size. + + -- Copy asset. + local cargoassets=UTILS.DeepCopy(request.cargoassets) + + -- Sort transport carriers w.r.t. cargo bay size. local function sort_transports(a,b) - return a.cargobay>b.cargobay + return a.cargobaymax>b.cargobaymax end - -- sort cargo assets w.r.t. weight in assending order + -- Sort cargo assets w.r.t. weight in assending order. local function sort_cargoassets(a,b) return a.weight>b.weight end + -- Sort tables. table.sort(transports, sort_transports) table.sort(cargoassets, sort_cargoassets) + + -- Total cargo bay size of all groups. + env.info("Transport capability:") + local totalbay=0 + for i=1,#transports do + local transport=transports[i] --#WAREHOUSE.Assetitem + for j=1,transport.nunits do + totalbay=totalbay+transport.cargobay[j] + env.info(string.format("Cargo bay = %d (unit=%d)", transport.cargobay[j], j)) + end + end + env.info(string.format("Total capacity = %d", totalbay)) - -- Very simple! Only take the largest transports that can carrier the largest cargo. + -- Total cargo weight. + env.info("Cargo weight:") + local totalweight=0 + for i=1,#cargoassets do + local asset=cargoassets[i] --#WAREHOUSE.Assetitem + totalweight=totalweight+asset.weight + env.info(string.format("weight = %d", asset.weight)) + end + env.info(string.format("Total weight = %d", totalweight)) + + -- Transports used. local used_transports={} - local maxcargoweight=cargoassets[1].weight - + -- Loop over all transport groups, largest cargobaymax to smallest. for i=1,#transports do - local transport=transports[i] --#WAREHOUSE.Assetitem - if transport.cargobay>maxcargoweight and #used_transports<=request.ntransport then + + -- Shortcut for carrier and cargo bay + local transport=transports[i] + + -- Cargo put into carrier. + local putintocarrier={} + + -- Cargo assigned to this transport group? + local used=false + + -- Loop over all units + for k=1,transport.nunits do + + -- Get cargo bay of this carrier. + local cargobay=transport.cargobay[k] + + -- Loop over cargo assets. + for j,asset in pairs(cargoassets) do + + -- How many times does the cargo fit into the carrier? + local n=cargobay/asset.weight + + -- Cargo fits into carrier + if n>=1 then + -- Reduce remaining cargobay. + cargobay=cargobay-asset.weight + env.info(string.format("%s unit %d loads cargo uid=%d: bayempty=%02d, bayloaded = %02d - weight=%02d", transport.templatename, k, asset.uid, transport.cargobay[k], cargobay, asset.weight)) + + -- Remember this cargo and remove it so it does not get loaded into other carriers. + table.insert(putintocarrier, j) + + -- This transport group is used. + used=true + end + + end -- loop over assets + end -- loop over units + + -- Remove cargo assets from list. Needs to be done back-to-front in oder not to confuse the loop. + for j=#putintocarrier,1, -1 do + local nput=putintocarrier[j] + + local cargo=cargoassets[nput] + env.info(string.format("cargo id=%d assigned for carrier id=%d", cargo.uid, transport.uid)) + + table.remove(cargoassets, nput) + end + + -- Cargo was assined for this carrier. + if used then table.insert(used_transports, transport) end + + -- Max number of transport groups reached? + if #used_transports>=request.ntransport then + break + end end - for _,_transport in ipairs(used_transports) do - local transport=_transport --#WAREHOUSE.Assetitem - --env.info("transport used = ", transport.) - end - + -- Debug info. + env.info("Used Transports:") + for _,transport in pairs(used_transports) do + env.info(string.format("%s, cargobaymax=%d, nunits=%d", transport.templatename, transport.cargobaymax, transport.nunits)) + for _,cargobay in pairs(transport.cargobay) do + env.info(string.format("cargobay %d", cargobay)) + end + end + return used_transports end From 89051d543955c32888665d8e388e7cb6f4198f0d Mon Sep 17 00:00:00 2001 From: FlightControl Date: Fri, 31 Aug 2018 17:05:03 +0200 Subject: [PATCH 302/420] I've published the trucks weight adaptions. Can you try in 5 minutes the trucks? --- Moose Development/Moose/Wrapper/Positionable.lua | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index aa564c06a..aabd88670 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -1007,6 +1007,17 @@ do -- Cargo ["MLRS FDDM"] = 4, ["MTLB"] = 25, ["TPZ"] = 10, + ["Ural-4320 APA-5D"] = 10, + ["GAZ-66"] = 8, + ["GAZ-3307"] = 12, + ["GAZ-3308"] = 14, + ["Tigr_233036"] = 6, + ["KAMAZ Truck"] = 12, + ["KrAZ6322"] = 12, + ["M 818"] = 12, + ["Ural-375"] = 12, + ["Ural-4320-31"] = 14, + ["Ural-4320T"] = 14, } local CargoBayWeightLimit = ( Weights[Desc.typeName] or 0 ) * 95 From 8d41e4699c746dbcb7c4264258c0fc670bea3536 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Fri, 31 Aug 2018 17:30:23 +0200 Subject: [PATCH 303/420] Patched weight issues with mortars. 5000 kg is a bit too much for a mortar i think. Ed to fix this. --- Moose Development/Moose/Cargo/Cargo.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 82015511e..f1f869fd4 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -1032,7 +1032,11 @@ do -- CARGO_REPRESENTABLE self:I( { Desc = Desc } ) local Weight = math.random( 80, 120 ) if Desc then - Weight = Desc.massEmpty + if Desc.typeName == "2B11 mortar" then + Weight = 210 + else + Weight = Desc.massEmpty + end end self:SetWeight( Weight ) From ed3345b00a4ca93a4d9e6cbf17ffe1e3c3df445e Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sat, 1 Sep 2018 06:50:18 +0200 Subject: [PATCH 304/420] Documentation of cargo declaration in the mission editor using #CARGO tag. --- Moose Development/Moose/Cargo/Cargo.lua | 45 ++++++++++++++++--------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index f1f869fd4..a8cd46b66 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -142,8 +142,17 @@ -- These cargo objects can then be automatically incorporated within cargo set(s)!!! -- In other words, your mission would be reduced to about a few lines of code, providing you with a full dynamic cargo handling mission! -- +-- MOOSE can create automatically cargo objects, if the name of the cargo contains the **\#CARGO** tag. +-- When a mission starts, MOOSE will scan all group and static objects it found for the presence of the \#CARGO tag. +-- When found, MOOSE will declare the object as cargo (create in the background a CARGO_ object, like CARGO_GROUP, CARGO_CRATE or CARGO_SLINGLOAD. +-- The creation of these CARGO_ objects will allow to be filtered and automatically added in SET_CARGO objects. +-- In other words, with very minimal code as explained in the above code section, you are able to create vast amounts of cargo objects just from within the editor. +-- -- What I talk about is this: -- +-- -- BEFORE THIS SCRIPT STARTS, MOOSE WILL ALREADY HAVE SCANNED FOR OBJECTS WITH THE #CARGO TAG IN THE NAME. +-- -- FOR EACH OF THESE OBJECT, MOOSE WILL HAVE CREATED CARGO_ OBJECTS LIKE CARGO_GROUP, CARGO_CRATE AND CARGO_SLINGLOAD. +-- -- HQ = GROUP:FindByName( "HQ", "Bravo" ) -- -- CommandCenter = COMMANDCENTER @@ -156,34 +165,40 @@ -- -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, TransportGroups ) -- --- -- -- This is the most important now. You setup a new SET_CARGO filtering the relevant type. -- -- The actual cargo objects are now created by MOOSE in the background. --- -- Each cargo is setup in the Mission Editor using the ~CARGO tag in the group name. +-- -- Each cargo is setup in the Mission Editor using the #CARGO tag in the group name. -- -- This allows a truly dynamic setup. -- local CargoSetWorkmaterials = SET_CARGO:New():FilterTypes( "Workmaterials" ):FilterStart() -- -- local WorkplaceTask = TaskDispatcher:AddTransportTask( "Build a Workplace", CargoSetWorkmaterials, "Transport the workers, engineers and the equipment near the Workplace." ) -- TaskDispatcher:SetTransportDeployZone( WorkplaceTask, ZONE:New( "Workplace" ) ) -- --- Helos = { SPAWN:New( "Helicopters 1" ), SPAWN:New( "Helicopters 2" ), SPAWN:New( "Helicopters 3" ), SPAWN:New( "Helicopters 4" ), SPAWN:New( "Helicopters 5" ) } --- --- EnemyHelos = { SPAWN:New( "Enemy Helicopters 1" ), SPAWN:New( "Enemy Helicopters 2" ), SPAWN:New( "Enemy Helicopters 3" ) } --- --- function WorkplaceTask:OnAfterCargoDeployed( From, Event, To, TaskUnit, Cargo, DeployZone ) --- Helos[ math.random(1,#Helos) ]:Spawn() --- EnemyHelos[ math.random(1,#EnemyHelos) ]:Spawn() --- --- end --- --- Here the `CargoSetWorkmaterials` is provided as a parameter to the cargo task dispatcher object WorkplaceTask`. +-- The above code example has the `CargoSetWorkmaterials`, which is a SET_CARGO collection and will include the CARGO_ objects of the type "Workmaterials". -- And there is NO cargo object actually declared within the script! However, if you would open the mission, there would be hundreds of cargo objects... -- --- HOW? => Through a naming convention introduced. Name infantry groups in a special manner, and it can behave as MOOSE cargo! +-- The \#CARGO tag even allows for several options to be specified, which are important to learn. +-- For example, the following #CARGO naming in the group name of the object, will create a group cargo object for MOOSE. -- --- 5.1) Name MOOSE cargo objects within the mission editor! +-- `Infantry #CARGO(T=Workmaterials,RR=500,NR=25)´ -- +-- This will create a cargo object: -- +-- * with the group name `Infantry #CARGO` +-- * is of type `Workmaterials` +-- * will report when a carrier is within 500 meters +-- * will board to carriers when the carrier is within 500 meters from the cargo object +-- * will dissapear when the cargo is within 25 meters from the carrier during boarding +-- +-- So the overall syntax of the #CARGO naming tag and arguments are: +-- +-- `GroupName #CARGO(T=CargoTypeName,RR=Range,NR=Range)` +-- +-- * **T=** Provide a text that contains the type name of the cargo object. This type name can be used to filter cargo within a SET_CARGO object. +-- * **RR=** Provide the minimal range in meters when the report to the carrier, and board to the carrier. +-- Note that this option is optional, so can be omitted. The default value of the RR is 250 meters. +-- * **NR=** Provide the maximum range in meters when the cargo units will be boarded within the carrier during boarding. +-- Note that this option is optional, so can be omitted. The default value of the RR is 10 meters. -- -- === -- From c1dee544931dadb8c77aa439db229edd8fa67562 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sun, 2 Sep 2018 00:07:49 +0200 Subject: [PATCH 305/420] Warehouse v0.3.2 Added new flightplan. Fixed some bugs. --- .../Moose/Functional/Warehouse.lua | 419 ++++++++++++------ 1 file changed, 278 insertions(+), 141 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 3e6f7f219..9feccd938 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -366,7 +366,9 @@ WAREHOUSE = { -- @field DCS#Object.Desc DCSdesc All DCS descriptors. -- @field #WAREHOUSE.Attribute attribute Generalized attribute of the group. -- @field #boolean transporter If true, the asset is able to transport troops. --- @field #number cargobay Weight in kg that fits in the cargo bay of one asset unit. +-- @field #table cargobay Array of cargo bays of all units in an asset group. +-- @field #number cargobaytot Total weight in kg that fits in the cargo bay of all asset group units. +-- @field #number cargobaymax Largest cargo bay of all units in the group. --- Item of the warehouse queue table. -- @type WAREHOUSE.Queueitem @@ -442,19 +444,19 @@ WAREHOUSE.Attribute = { AIR_TANKER="Air_Tanker", AIR_TRANSPORTHELO="Air_TransportHelo", AIR_ATTACKHELO="Air_AttackHelo", - AIR_OTHER="Air_Other", + AIR_OTHER="Air_OtherAir", GROUND_APC="Ground_APC", GROUND_TRUCK="Ground_Truck", GROUND_INFANTRY="Ground_Infantry", GROUND_ARTILLERY="Ground_Artillery", GROUND_TANK="Ground_Tank", GROUND_TRAIN="Ground_Train", - GROUND_OTHER="Ground_Other", + GROUND_OTHER="Ground_OtherGround", NAVAL_AIRCRAFTCARRIER="Naval_AircraftCarrier", NAVAL_WARSHIP="Naval_WarShip", NAVAL_ARMEDSHIP="Naval_ArmedShip", NAVAL_UNARMEDSHIP="Naval_UnarmedShip", - NAVAL_OTHER="Naval_Other", + NAVAL_OTHER="Naval_OtherNaval", UNKNOWN="Unknown", } @@ -486,7 +488,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.3.1w" +WAREHOUSE.version="0.3.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -1276,7 +1278,8 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu group=GROUP:FindByName(group) end - self:E(string.format("Adding %d assets of group %s.", n, group:GetName())) + -- Debug info. + self:I(self.wid..string.format("Adding %d assets of group %s to warehouse %s.", n, tostring(group:GetName()), self.alias)) if group then @@ -1314,7 +1317,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu -- Need to create a "zombie" template group maybe? if group:IsAlive()==true then self:E(self.wid..string.format("Destroying group %s.", group:GetName())) - group:Destroy() + group:Destroy(true) end end @@ -1382,8 +1385,10 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute) -- Get weight in kg env.info("FF get weight") local weight=0 - local cargobay=0 - for _,_unit in pairs(group:GetUnits()) do + local cargobay={} + local cargobaytot=0 + local cargobaymax=0 + for _i,_unit in pairs(group:GetUnits()) do local unit=_unit --Wrapper.Unit#UNIT local Desc=unit:GetDesc() self:E({UnitDesc=Desc}) @@ -1391,8 +1396,14 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute) if unitweight then weight=weight+unitweight env.info("FF weight = "..weight) - end - cargobay=unit:GetCargoBayFreeWeight() + end + local bay=unit:GetCargoBayFreeWeight() + env.info("FF cargo bay = "..bay) + table.insert(cargobay, bay) + cargobaytot=cargobaytot+bay + if bay>cargobaymax then + cargobaymax=bay + end end -- Set/get the generalized attribute. @@ -1423,6 +1434,8 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute) asset.attribute=attribute asset.transporter=false -- not used yet asset.cargobay=cargobay + asset.cargobaytot=cargobaytot + asset.cargobaymax=cargobaymax if i==1 then self:_AssetItemInfo(asset) @@ -1453,7 +1466,8 @@ function WAREHOUSE:_AssetItemInfo(asset) text=text..string.format("Range max = %5.2f km\n", asset.range/1000) text=text..string.format("Size max = %5.2f m\n", asset.size) text=text..string.format("Weight total = %5.2f kg\n", asset.weight) - text=text..string.format("Cargo bay = %5.2f kg\n", asset.cargobay) + text=text..string.format("Cargo bay tot = %5.2f kg\n", asset.cargobaytot) + text=text..string.format("Cargo bay max = %5.2f kg\n", asset.cargobaymax) self:E(self.wid..text) self:E({DCSdesc=asset.DCSdesc}) self:E({Template=asset.template}) @@ -1575,7 +1589,7 @@ function WAREHOUSE:_SpawnAssetAircraft(asset, request, parking, uncontrolled) local AirbaseCategory = self.category -- Check enough parking spots. - if AirbaseCategory == Airbase.Category.HELIPAD or AirbaseCategory == Airbase.Category.SHIP then + if AirbaseCategory==Airbase.Category.HELIPAD or AirbaseCategory==Airbase.Category.SHIP then --TODO Figure out what's necessary in this case. else @@ -1585,6 +1599,7 @@ function WAREHOUSE:_SpawnAssetAircraft(asset, request, parking, uncontrolled) self:E(text) return nil end + end -- Position the units. @@ -2010,15 +2025,15 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) local _nearradius=nil if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then - _loadradius=5000 + _loadradius=10000 elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then - _loadradius=500 + _loadradius=1000 elseif Request.transporttype==WAREHOUSE.TransportType.APC then - _loadradius=100 + _loadradius=1000 end -- Empty cargo group set. - CargoGroups = SET_CARGO:New() + CargoGroups = SET_CARGO:New():FilterDeads() -- Add cargo groups to set. for _i,_group in pairs(_spawngroups:GetSetObjects()) do @@ -2214,6 +2229,8 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Get group obejet. local group=Cargo:GetObject() --Wrapper.Group#GROUP + + --Cargo:Load() -- Get warehouse state. local warehouse=Carrier:GetState(Carrier, "WAREHOUSE") --#WAREHOUSE @@ -2273,8 +2290,8 @@ function WAREHOUSE:_SpawnAssetRequest(Request) -- Now we try to find all parking spots for all cargo groups in advance. Due to the for loop, the parking spots do not get updated while spawning. local Parking={} - if _cargocategory==Group.Category.AIRPLANE or _cargocategory==Group.Category.HELICOPTER then - Parking=self:_FindParkingForAssets(self.airbase,_assetstock) + if _cargocategory==Group.Category.AIRPLANE or _cargocategory==Group.Category.HELICOPTER then + Parking=self:_FindParkingForAssets(self.airbase,_assetstock) or {} end -- Spawn aircraft in uncontrolled state if request comes from the same warehouse. @@ -2312,7 +2329,11 @@ function WAREHOUSE:_SpawnAssetRequest(Request) --TODO: spawn only so many groups as there are parking spots. Adjust request and create a new one with the reduced number! -- Spawn air units. - _group=self:_SpawnAssetAircraft(_assetitem, Request, Parking[_assetitem.uid], UnControlled) + if Parking[_assetitem.uid] then + _group=self:_SpawnAssetAircraft(_assetitem, Request, Parking[_assetitem.uid], UnControlled) + else + _group=self:_SpawnAssetAircraft(_assetitem, Request, nil, UnControlled) + end elseif _assetitem.category==Group.Category.TRAIN then @@ -2905,17 +2926,22 @@ function WAREHOUSE:_OnEventArrived(EventData) -- If all IDs are good we can assume it is a warehouse asset. if wid~=nil and aid~=nil and rid~=nil then - -- Debug info. - local text=string.format("Air asset group %s arrived at warehouse %s.", group:GetName(), self.alias) - --MESSAGE:New - self:E(self.wid..text) - - -- Trigger arrived event for this group. Note that each unit of a group will trigger this event. So the onafterArrived function needs to take care of that. - -- Actually, we only take the first unit of the group that arrives. If it does, we assume the whole group arrived, which might not be the case, since - -- some units might still be taxiing or whatever. Therefore, we add 10 seconds for each additional unit of the group until the first arrived event is triggered. - local nunits=#group:GetUnits() - local dt=10*(nunits-1)+1 -- one unit = 1 sec, two units = 11 sec, three units = 21 sec before we call the group arrived. - self:__Arrived(dt, group) + -- Check that warehouse ID is right. + if self.uid==wid then + + -- Debug info. + local text=string.format("Air asset group %s arrived at warehouse %s.", group:GetName(), self.alias) + MESSAGE:New(text, 20):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:I(self.wid..text) + + -- Trigger arrived event for this group. Note that each unit of a group will trigger this event. So the onafterArrived function needs to take care of that. + -- Actually, we only take the first unit of the group that arrives. If it does, we assume the whole group arrived, which might not be the case, since + -- some units might still be taxiing or whatever. Therefore, we add 10 seconds for each additional unit of the group until the first arrived event is triggered. + local nunits=#group:GetUnits() + local dt=10*(nunits-1)+1 -- one unit = 1 sec, two units = 11 sec, three units = 21 sec before we call the group arrived. + self:__Arrived(dt, group) + + end else self:T3(string.format("Group that arrived did not belong to a warehouse. Warehouse ID=%s, Asset ID=%s, Request ID=%s.", tostring(wid), tostring(aid), tostring(rid))) @@ -3375,13 +3401,15 @@ function WAREHOUSE:_CheckRequestValid(request) self:E(string.format("Asset attribute = %s, terminal type = %d, spots at departure = %d, destination = %d", asset.attribute, termtype, np_departure, np_destination)) -- Not enough parking at sending warehouse. + --if (np_departure < request.nasset) and not (self.category==Airbase.Category.SHIP or self.category==Airbase.Category.HELIPAD) then if np_departure < request.nasset then self:E(string.format("ERROR: Incorrect request. Not enough parking spots of terminal type %d at warehouse. Available spots = %d.", termtype, np_departure)) valid=false end -- Not enough parking at requesting warehouse. - if np_destination < request.nasset then + --if np_destination < request.nasset then + if np_destination == 0 then -- TODO: maybe this is just right for FAPS/SHIPS self:E(string.format("ERROR: Incorrect request. Not enough parking spots of terminal type %d at requesting warehouse. Available spots = %d.", termtype, np_destination)) valid=false end @@ -3544,7 +3572,10 @@ function WAREHOUSE:_CheckRequestNow(request) -- Check available parking for air asset units. if self.airbase and (_assetcategory==Group.Category.AIRPLANE or _assetcategory==Group.Category.HELICOPTER) then + local Parking=self:_FindParkingForAssets(self.airbase,_assets) + + --if Parking==nil and not (self.category==Airbase.Category.HELIPAD) then if Parking==nil then local text=string.format("Warehouse %s: Request denied! Not enough free parking spots for all assets at the moment.", self.alias) MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) @@ -3552,6 +3583,7 @@ function WAREHOUSE:_CheckRequestNow(request) return false end + end -- Set chosen assets. @@ -3636,26 +3668,26 @@ function WAREHOUSE:_GetTransportsForAssets(request) table.sort(cargoassets, sort_cargoassets) -- Total cargo bay size of all groups. - env.info("Transport capability:") + self:T2(self.wid.."Transport capability:") local totalbay=0 for i=1,#transports do local transport=transports[i] --#WAREHOUSE.Assetitem for j=1,transport.nunits do totalbay=totalbay+transport.cargobay[j] - env.info(string.format("Cargo bay = %d (unit=%d)", transport.cargobay[j], j)) + self:T2(self.wid..string.format("Cargo bay = %d (unit=%d)", transport.cargobay[j], j)) end end - env.info(string.format("Total capacity = %d", totalbay)) + self:T2(self.wid..string.format("Total capacity = %d", totalbay)) - -- Total cargo weight. - env.info("Cargo weight:") - local totalweight=0 + -- Total cargo weight of all assets to transports. + self:T2(self.wid.."Cargo weight:") + local totalcargoweight=0 for i=1,#cargoassets do local asset=cargoassets[i] --#WAREHOUSE.Assetitem - totalweight=totalweight+asset.weight - env.info(string.format("weight = %d", asset.weight)) + totalcargoweight=totalcargoweight+asset.weight + self:T2(self.wid..string.format("weight = %d", asset.weight)) end - env.info(string.format("Total weight = %d", totalweight)) + self:T2(self.wid..string.format("Total weight = %d", totalcargoweight)) -- Transports used. local used_transports={} @@ -3688,7 +3720,7 @@ function WAREHOUSE:_GetTransportsForAssets(request) if n>=1 then -- Reduce remaining cargobay. cargobay=cargobay-asset.weight - env.info(string.format("%s unit %d loads cargo uid=%d: bayempty=%02d, bayloaded = %02d - weight=%02d", transport.templatename, k, asset.uid, transport.cargobay[k], cargobay, asset.weight)) + self:T3(self.wid..string.format("%s unit %d loads cargo uid=%d: bayempty=%02d, bayloaded = %02d - weight=%02d", transport.templatename, k, asset.uid, transport.cargobay[k], cargobay, asset.weight)) -- Remember this cargo and remove it so it does not get loaded into other carriers. table.insert(putintocarrier, j) @@ -3705,7 +3737,7 @@ function WAREHOUSE:_GetTransportsForAssets(request) local nput=putintocarrier[j] local cargo=cargoassets[nput] - env.info(string.format("cargo id=%d assigned for carrier id=%d", cargo.uid, transport.uid)) + self:T2(self.wid..string.format("Cargo id=%d assigned for carrier id=%d", cargo.uid, transport.uid)) table.remove(cargoassets, nput) end @@ -3716,19 +3748,26 @@ function WAREHOUSE:_GetTransportsForAssets(request) end -- Max number of transport groups reached? - if #used_transports>=request.ntransport then + if #used_transports >= request.ntransport then break end end -- Debug info. - env.info("Used Transports:") - for _,transport in pairs(used_transports) do - env.info(string.format("%s, cargobaymax=%d, nunits=%d", transport.templatename, transport.cargobaymax, transport.nunits)) - for _,cargobay in pairs(transport.cargobay) do - env.info(string.format("cargobay %d", cargobay)) - end - end + local text=string.format("Used Transports for request %d to warehouse %s:\n", request.uid, request.warehouse.alias) + local totalcargobay=0 + for _i,_transport in pairs(used_transports) do + local transport=_transport --#WAREHOUSE.Assetitem + text=text..string.format("%d) %s: cargobay tot = %d kg, cargobay max = %d kg, nunits=%d\n", _i, transport.unittype, transport.cargobaytot, transport.cargobaymax, transport.nunits) + totalcargobay=totalcargobay+transport.cargobaytot + --for _,cargobay in pairs(transport.cargobay) do + -- env.info(string.format("cargobay %d", cargobay)) + --end + end + text=text..string.format("Total cargo bay capacity = %.1f kg\n", totalcargobay) + text=text..string.format("Total cargo weight = %.1f kg\n", totalcargoweight) + text=text..string.format("Minimum number of runs = %.1f", totalcargoweight/totalcargobay) + self:I(self.wid..text) return used_transports end @@ -3753,10 +3792,11 @@ function WAREHOUSE:_CheckQueue() local valid=self:_CheckRequestValid(qitem) -- Check if request is possible now. - local okay=self:_CheckRequestNow(qitem) - - -- Remember invalid request and delete later in order not to confuse the loop. - if not valid then + local okay=false + if valid then + okay=self:_CheckRequestNow(qitem) + else + -- Remember invalid request and delete later in order not to confuse the loop. table.insert(invalid, qitem) end @@ -4408,11 +4448,23 @@ function WAREHOUSE:_GetStockAssetsText(messagetoall) -- Get assets in stock. local _data=self:GetStockInfo(self.stock) + --[[ + local function _sort(a,b) + return aDeltaholdingMax then + h_holding=math.abs(DeltaholdingMax) + end + + -- This is the height ASL of the holding point we want to fly to. local Hh_holding=H_holding+h_holding - - -- Distance from holding point to final destination. - local d_holding=Pholding:Get2DDistance(Pdestination) - -- GENERAL - local heading=Pdeparture:HeadingTo(Pdestination) - local d_total=Pdeparture:Get2DDistance(Pholding) + --------------------------- + --- Max Flight Altitude --- + --------------------------- + + -- Get max flight altitude relative to H_departure. + local h_max=self:_MakeFlightplan(d_total, AlphaClimb, AlphaDescent, H_departure, H_holding, h_holding) - -------------------------------------------- - - -- Height difference between departure and destination. - local deltaH=math.abs(H_departure-Hh_holding) - - -- Slope between departure and destination. - local phi = math.atan(deltaH/d_total) - - -- Adjusted climb/descent angles. - local phi_climb - local phi_descent - if (H_departure > Hh_holding) then - phi_climb=AlphaClimb+phi - phi_descent=AlphaDescent-phi - else - phi_climb=AlphaClimb-phi - phi_descent=AlphaDescent+phi - end - - -- Total distance including slope. - local D_total=math.sqrt(deltaH*deltaH+d_total*d_total) - - -- SSA triangle for sloped case. - local gamma=math.rad(180)-phi_climb-phi_descent - local a = D_total*math.sin(phi_climb)/math.sin(gamma) - local b = D_total*math.sin(phi_descent)/math.sin(gamma) - local hphi_max = b*math.sin(phi_climb) - local hphi_max2 = a*math.sin(phi_descent) - - -- Height of triangle. - local h_max1 = b*math.sin(AlphaClimb) - local h_max2 = a*math.sin(AlphaDescent) - - -- Max height relative to departure or destination. - local h_max - if (H_departure > Hh_holding) then - h_max=math.min(h_max1, h_max2) - else - h_max=math.max(h_max1, h_max2) - end - - -- Max flight level aircraft can reach for given angles and distance. + -- Max flight level ASL aircraft can reach for given angles and distance. local FLmax = h_max+H_departure --CRUISE -- Min cruise alt is just above holding point at destination or departure height, whatever is larger. local FLmin=math.max(H_departure, Hh_holding) - - -- For helicopters we take cruise alt between 50 to 1000 meters above ground. Default cruise alt is ~150 m. - if _category==Group.Category.HELICOPTER then - FLmin=math.max(H_departure, H_destination)+50 - FLmax=math.max(H_departure, H_destination)+1000 - end -- Ensure that FLmax not above its service ceiling. FLmax=math.min(FLmax, ceiling) @@ -4652,28 +4761,38 @@ function WAREHOUSE:_GetFlightplan(asset, departure, destination) -- Climb and descent heights. local h_climb = FLcruise - H_departure local h_descent = FLcruise - Hh_holding - - -- Distances. + + -- Get distances. local d_climb = h_climb/math.tan(AlphaClimb) local d_descent = h_descent/math.tan(AlphaDescent) local d_cruise = d_total-d_climb-d_descent -- Debug. local text=string.format("Flight plan:\n") - text=text..string.format("Vx max = %d\n", Vmax) - text=text..string.format("Vx climb = %d\n", VxClimb) - text=text..string.format("Vx cruise = %d\n", VxCruise) - text=text..string.format("Vx descent = %d\n", VxDescent) - text=text..string.format("Vx holding = %d\n", VxHolding) - text=text..string.format("Vx final = %d\n", VxFinal) - text=text..string.format("Dist climb = %d\n", d_climb) - text=text..string.format("Dist cruise = %d\n", d_cruise) - text=text..string.format("Dist descent = %d\n", d_descent) - text=text..string.format("Dist total = %d\n", d_total) - text=text..string.format("FL min = %d\n", FLmin) - text=text..string.format("FL cruise * = %d\n", FLcruise) - text=text..string.format("FL max = %d\n", FLmax) - text=text..string.format("Ceiling = %d\n", ceiling) + text=text..string.format("Vx max = %.2f km/h\n", Vmax*3.6) + text=text..string.format("Vx climb = %.2f km/h\n", VxClimb*3.6) + text=text..string.format("Vx cruise = %.2f km/h\n", VxCruise*3.6) + text=text..string.format("Vx descent = %.2f km/h\n", VxDescent*3.6) + text=text..string.format("Vx holding = %.2f km/h\n", VxHolding*3.6) + text=text..string.format("Vx final = %.2f km/h\n", VxFinal*3.6) + text=text..string.format("Vy max = %.2f m/s\n", Vymax) + text=text..string.format("Vy climb = %.2f m/s\n", VyClimb) + text=text..string.format("Alpha Climb = %.2f Deg\n", math.deg(AlphaClimb)) + text=text..string.format("Alpha Descent = %.2f Deg\n", math.deg(AlphaDescent)) + text=text..string.format("Dist climb = %.3f km\n", d_climb/1000) + text=text..string.format("Dist cruise = %.3f km\n", d_cruise/1000) + text=text..string.format("Dist descent = %.3f km\n", d_descent/1000) + text=text..string.format("Dist total = %.3f km\n", d_total/1000) + text=text..string.format("h_climb = %.3f km\n", h_climb/1000) + text=text..string.format("h_desc = %.3f km\n", h_descent/1000) + text=text..string.format("h_holding = %.3f km\n", h_holding/1000) + text=text..string.format("h_max = %.3f km\n", h_max/1000) + text=text..string.format("FL min = %.3f km\n", FLmin/1000) + text=text..string.format("FL expect = %.3f km\n", FLcruise_expect/1000) + text=text..string.format("FL cruise * = %.3f km\n", FLcruise/1000) + text=text..string.format("FL max = %.3f km\n", FLmax/1000) + text=text..string.format("Ceiling = %.3f km\n", ceiling/1000) + text=text..string.format("Max range = %.3f km\n", Range/1000) env.info(text) -- Ensure that cruise distance is positve. Can be slightly negative in special cases. And we don't want to turn back. @@ -4681,6 +4800,10 @@ function WAREHOUSE:_GetFlightplan(asset, departure, destination) d_cruise=100 end + ------------------------ + --- Create Waypoints --- + ------------------------ + -- Waypoints and coordinates local wp={} local c={} @@ -4721,7 +4844,21 @@ function WAREHOUSE:_GetFlightplan(asset, departure, destination) --- Final destination. c[#c+1]=Pdestination wp[#wp+1]=Pcruise2:WaypointAir("RADIO", COORDINATE.WaypointType.Land, COORDINATE.WaypointAction.Landing, VxFinal, true, destination, nil, "Final Destination") - + + + -- Mark points at waypoints for debugging. + if self.Debug then + for i,coord in pairs(c) do + local coord=coord --Core.Point#COORDINATE + env.info(i) + local dist=0 + if i>1 then + dist=coord:Get2DDistance(c[i-1]) + end + coord:MarkToAll(string.format("Waypoint %i, dist = %.2f km",i, dist/1000)) + end + end + return wp,c end From c9e44dd86511637538f54170954b7b41be45791a Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sun, 2 Sep 2018 23:49:02 +0200 Subject: [PATCH 306/420] Warehosue v0.3.3 --- .../Moose/AI/AI_Cargo_Airplane.lua | 2 +- .../Moose/AI/AI_Cargo_Dispatcher.lua | 52 +-- .../Moose/AI/AI_Cargo_Helicopter.lua | 16 +- Moose Development/Moose/Core/Set.lua | 2 +- .../Moose/Functional/Warehouse.lua | 295 ++++++++++++------ 5 files changed, 239 insertions(+), 128 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 8ce9bcb42..be200d882 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -132,7 +132,7 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) AirplaneUnit:SetCargoBayWeightLimit() end - self.Relocating = true + self.Relocating = false --FF should be false or set according to state of airplane! return self end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 94e67686a..777c3cff8 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -127,8 +127,8 @@ function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo ) self:AddTransition( "*", "BackHome", "*" ) --FF self.MonitorTimeInterval = 30 - self.DeployRadiusInner = 200 - self.DeployRadiusOuter = 500 + self.DeployInnerRadius = 200 + self.DeployOuterRadius = 500 self.PickupCargo = {} self.CarrierHome = {} @@ -231,19 +231,6 @@ function AI_CARGO_DISPATCHER:SetHomeBase( HomeBase ) end ---- Set the home base. --- When there is nothing anymore to pickup, the carriers will return to their home airbase. There they will await new orders. --- @param #AI_CARGO_DISPATCHER self --- @param Wrapper.Airbase#AIRBASE HomeBase The airbase where the carrier will go to, once they completed all pending assignments. --- @return #AI_CARGO_DISPATCHER self -function AI_CARGO_DISPATCHER:SetHomeBase( HomeBase ) - - self.HomeBase = HomeBase - - return self -end - - --- Sets or randomizes the pickup location for the carrier around the cargo coordinate in a radius defined an outer and optional inner radius. -- This radius is influencing the location where the carrier will land to pickup the cargo. -- There are two aspects that are very important to remember and take into account: @@ -368,12 +355,13 @@ end -- @param #AI_CARGO_DISPATCHER self function AI_CARGO_DISPATCHER:onafterMonitor() - env.info("FF number of cargo set = "..self.SetCargo:Count()) - for CarrierGroupName, Carrier in pairs( self.SetCarrier:GetSet() ) do + env.info("FF cargo dispatcher carrier group "..CarrierGroupName) + local Carrier = Carrier -- Wrapper.Group#GROUP local AI_Cargo = self.AI_Cargo[Carrier] if not AI_Cargo then + env.info("FF not AI CARGO") -- ok, so this Carrier does not have yet an AI_CARGO handling object... -- let's create one and also declare the Loaded and UnLoaded handlers. @@ -404,10 +392,15 @@ function AI_CARGO_DISPATCHER:onafterMonitor() self:Unloaded( Carrier, Cargo ) end - -- FF added back home event. + -- FF added BackHome event. function AI_Cargo.OnAfterBackHome( AI_Cargo, Carrier, From, Event, To) self:BackHome( Carrier ) end + + -- FF added RTB event. + function AI_Cargo.OnAfterRTB( AI_Cargo, Carrier, From, Event, To, Airbase) + self:RTB( Carrier, Airbase ) + end end -- The Pickup sequence ... @@ -459,6 +452,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() end if PickupCargo then + self.CarrierHome[Carrier] = nil local PickupCoordinate = PickupCargo:GetCoordinate():GetRandomCoordinateInRadius( self.PickupOuterRadius, self.PickupInnerRadius ) @@ -472,18 +466,34 @@ function AI_CARGO_DISPATCHER:onafterMonitor() AI_Cargo:Pickup( PickupCoordinate, math.random( self.PickupMinSpeed, self.PickupMaxSpeed ) ) end break + else + + env.info("FF HomeZone or HomeBase?") if self.HomeZone then + + env.info("FF HomeZone! Really?") if not self.CarrierHome[Carrier] then + env.info("FF Yes!") self.CarrierHome[Carrier] = true AI_Cargo:__Home( 60, self.HomeZone:GetRandomPointVec2() ) + else + env.info("FF Nope!") end - elseif self.HomeBase then + + elseif self.HomeBase2 then + + env.info("FF HomeBase! Really?") if not self.CarrierHome[Carrier] then + env.info("FF Yes!") self.CarrierHome[Carrier] = true - AI_Cargo:__RTB( 60, self.HomeBase ) - end + AI_Cargo:__RTB( 1, self.HomeBase ) + else + env.info("FF Nope!") + end + end + end end end diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index e40f357a4..46cf934a5 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -827,16 +827,6 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() ) Route[#Route].task = Helicopter:TaskCombo( Tasks ) - -- FF - --[[ - local Tasks2 = {} - Tasks2[#Tasks2+1] = Helicopter:TaskFunction("AI_CARGO_HELICOPTER._BackHome", self) - - Route[#Route+1] = WaypointTo - Route[#Route].task = Helicopter:TaskCombo( Tasks2 ) - -- FF - ]] - Route[#Route+1] = WaypointTo -- Now route the helicopter @@ -930,9 +920,10 @@ end --- Function called when transport is back home and nothing more to do. Triggering the event BackHome. -- @param Wrapper.Group#GROUP Helicopter Cargo helicopter. -- @param #AI_CARGO_HELICOPTER self -function AI_CARGO_HELICOPTER._BackHome(Group, self) - --Trigger BackHome event. +function AI_CARGO_HELICOPTER._BackHome(Group, self) + env.info("FF ai cargo helicopter back home task function") Group:SmokeRed() + --Trigger BackHome event. self:__BackHome(1) end @@ -944,5 +935,6 @@ end -- @param Event -- @param To function AI_CARGO_HELICOPTER:onafterBackHome( Helicopter, From, Event, To ) + env.info("FF ai cargo helicopter back home event") Helicopter:SmokeRed() end \ No newline at end of file diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 7cf4dacc8..410c17c82 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -636,7 +636,7 @@ function SET_BASE:Flush( MasterObject ) for ObjectName, Object in pairs( self.Set ) do ObjectNames = ObjectNames .. ObjectName .. ", " end - self:I( { MasterObject = MasterObject and MasterObject:GetClassNameAndID(), "Objects in Set:", ObjectNames } ) + self:T( { MasterObject = MasterObject and MasterObject:GetClassNameAndID(), "Objects in Set:", ObjectNames } ) return ObjectNames end diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 9feccd938..c51d69f4e 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -48,7 +48,7 @@ -- @field #table defending Table holding all defending requests, i.e. self requests that were if the warehouse is under attack. Table elements are of type @{#WAREHOUSE.Pendingitem}. -- @field Core.Zone#ZONE portzone Zone defining the port of a warehouse. This is where naval assets are spawned. -- @field #table shippinglanes Table holding the user defined shipping between warehouses. --- @field #boolean selfdefence When the warehouse is under attack, automatically spawn assets to defend the warehouse. +-- @field #boolean autodefence When the warehouse is under attack, automatically spawn assets to defend the warehouse. -- @extends Core.Fsm#FSM --- Have your assets at the right place at the right time - or not! @@ -135,7 +135,7 @@ -- Assets of the warehouse can be requested by other MOOSE warehouses. A request will first be scrutinize to check if can be fulfilled at all. If the request is valid, it is -- put into the warehouse queue and processed as soon as possible. -- --- A request can be assed by the @{#WAREHOUSE.AddRequest}(*warehouse*, *AssetDescriptor*, *AssetDescriptorValue*, *nAsset*, *TransportType*, *nTransport*, *Prio*) function. +-- A request can be assed by the @{#WAREHOUSE.AddRequest}(*warehouse*, *AssetDescriptor*, *AssetDescriptorValue*, *nAsset*, *TransportType*, *nTransport*, *Prio*, *Assignment*) function. -- The parameters are -- -- * *warehouse*: The requesting MOOSE @{#WAREHOUSE}. Assets will be delivered there. @@ -144,7 +144,8 @@ -- * *nAsset*: (Optional) Number of asset group requested. Default is one group. -- * *TransportType*: (Optional) The transport method used to deliver the assets to the requestor. Default is that assets go to the requesting warehouse on their own. -- * *nTransport*: (Optional) Number of asset groups used to transport the cargo assets from A to B. Default is one group. --- * *Prio*: A number between 1 (high) and 100 (low) describing the priority of the request. Request with high priority are processed first. Default is 50, i.e. medium priority. +-- * *Prio*: (Optional) A number between 1 (high) and 100 (low) describing the priority of the request. Request with high priority are processed first. Default is 50, i.e. medium priority. +-- * *Assignment*: (Optional) A free to choose string describing the assignment. For self requests, this can be used to assign the spawned groups to specific tasks. -- -- So for example: -- @@ -348,7 +349,7 @@ WAREHOUSE = { defending = {}, portzone = nil, shippinglanes = {}, - selfdefence = false, + autodefence = false, } --- Item of the warehouse stock table. @@ -488,7 +489,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.3.2" +WAREHOUSE.version="0.3.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -498,7 +499,7 @@ WAREHOUSE.version="0.3.2" -- TODO: Add autoselfdefence switch and user function. Default should be off. -- DONE: Warehouse re-capturing not working?! -- DONE: Naval assets dont go back into stock once arrived. --- TODO: Take cargo weight into consideration, when selecting transport assets. +-- DONE: Take cargo weight into consideration, when selecting transport assets. -- TODO: Add transport units from dispatchers back to warehouse stock once they completed their mission. -- DONE: Add ports for spawning naval assets. -- TODO: Added habours as interface for transport to from warehouses? @@ -683,8 +684,8 @@ function WAREHOUSE:New(warehouse, alias) -- @param #number nAsset Number of groups requested that match the asset specification. -- @param #WAREHOUSE.TransportType TransportType Type of transport. -- @param #number nTransport Number of transport units requested. - -- @param #string Assignment A keyword or text that later be used to identify this request and postprocess the assets. -- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. + -- @param #string Assignment A keyword or text that later be used to identify this request and postprocess the assets. --- Triggers the FSM event "AddRequest" with a delay. Add a request to the warehouse queue, which is processed when possible. -- @function [parent=#WAREHOUSE] __AddRequest @@ -696,8 +697,8 @@ function WAREHOUSE:New(warehouse, alias) -- @param #number nAsset Number of groups requested that match the asset specification. -- @param #WAREHOUSE.TransportType TransportType Type of transport. -- @param #number nTransport Number of transport units requested. - -- @param #string Assignment A keyword or text that later be used to identify this request and postprocess the assets. -- @param #number Prio Priority of the request. Number ranging from 1=high to 100=low. + -- @param #string Assignment A keyword or text that later be used to identify this request and postprocess the assets. --- Triggers the FSM event "Request". Executes a request from the queue if possible. @@ -845,7 +846,39 @@ end -- User functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Set interval of status updates +--- Set debug mode on. Error messages will be displayed on screen, units will be smoked at some events. +-- @param #WAREHOUSE self +-- @return #WAREHOUSE self +function WAREHOUSE:SetDebugOn() + self.Debug=true + return self +end + +--- Set debug mode off. This is the default +-- @param #WAREHOUSE self +-- @return #WAREHOUSE self +function WAREHOUSE:SetDebugOff() + self.Debug=false + return self +end + +--- Set report on. Messages at events will be displayed on screen to the coalition owning the warehouse. +-- @param #WAREHOUSE self +-- @return #WAREHOUSE self +function WAREHOUSE:SetReportOn() + self.Report=true + return self +end + +--- Set report off. Warehouse does not report about its status and at certain events. +-- @param #WAREHOUSE self +-- @return #WAREHOUSE self +function WAREHOUSE:SetReportOff() + self.Report=false + return self +end + +--- Set interval of status updates. Note that only one request can be processed per time interval. -- @param #WAREHOUSE self -- @param #number timeinterval Time interval in seconds. -- @return #WAREHOUSE self @@ -872,6 +905,23 @@ function WAREHOUSE:SetWarehouseZone(zone) return self end +--- Set auto defence on. When the warehouse is under attack, all ground assets are spawned automatically and will defend the warehouse zone. +-- @param #WAREHOUSE self +-- @return #WAREHOUSE self +function WAREHOUSE:SetAutoDefenceOn() + self.autodefence=true + return self +end + +--- Set auto defence off. This is the default. +-- @param #WAREHOUSE self +-- @return #WAREHOUSE self +function WAREHOUSE:SetAutoDefenceOff() + self.autodefence=false + return self +end + + --- Set the airbase belonging to this warehouse. -- Note that it has to be of the same coalition as the warehouse. -- Also, be reasonable and do not put it too far from the phyiscal warehouse structure because you troops might have a long way to get to their transports. @@ -956,8 +1006,6 @@ function WAREHOUSE:AddShippingLane(remotewarehouse, group) -- Add the shipping lane. Need to take care of the wrong "direction". local lane={} - --lane.towarehouse=remotewarehouse.warehouse:GetName() - --lane.coordinates={} if distF0 then + self:AddRequest(self, WAREHOUSE.Descriptor.CATEGORY, Group.Category.GROUND, "all", nil, nil , 0) + else + local text=string.format("No ground assets currently available.") + MESSAGE:New(text, 20):ToCoalitionIf(self.coalition, self.Report or self.Debug) + self:I(self.wid..text) + end + else + local text=string.format("Warehouse auto defence inactive.") + self:I(self.wid..text) end end @@ -2595,28 +2681,35 @@ function WAREHOUSE:onafterDefeated(From, Event, To) MESSAGE:New(text, 20):ToCoalitionIf(self.coalition, self.Report or self.Debug) self:I(self.wid..text) - --if self.defenderrequest then - for _,request in pairs(self.defending) do + -- Debug smoke. + if self.Debug then + self.coordinate:SmokeGreen() + end + + -- Auto defence: put assets back into stock. + if self.autodefence then + for _,request in pairs(self.defending) do + + -- Route defenders back to warehoue (for visual reasons only) and put them back into stock. + for _,_group in pairs(request.cargogroupset:GetSetObjects()) do + local group=_group --Wrapper.Group#GROUP + + -- Get max speed of group and route it back slowly to the warehouse. + local speed=group:GetSpeedMax() + if group:IsGround() and speed>1 then + group:RouteGroundTo(self.coordinate, speed*0.3) + end + + -- Add asset group back to stock after 60 seconds. + self:__AddAsset(60, group) + end - -- Route defenders back to warehoue (for visual reasons only) and put them back into stock. - for _,_group in pairs(request.cargogroupset:GetSetObjects()) do - local group=_group --Wrapper.Group#GROUP - - -- Get max speed of group and route it back slowly to the warehouse. - local speed=group:GetSpeedMax() - if group:IsGround() and speed>1 then - group:RouteGroundTo(self.coordinate, speed*0.3) - end - - -- Add asset group back to stock after 60 seconds. - self:__AddAsset(60, group) + --self:_DeleteQueueItem(request, self.defending) end - --self:_DeleteQueueItem(request, self.defending) + self.defending=nil + self.defending={} end - - self.defending=nil - self.defending={} end --- On after "Captured" event. Warehouse has been captured by another coalition. @@ -2643,10 +2736,6 @@ function WAREHOUSE:onafterCaptured(From, Event, To, Coalition, Country) -- Delete all waiting requests because they are not valid any more self.queue=nil self.queue={} - - --TODO: What about pending items? Is there any problem due to the coalition change? - --TODO: Maybe if the receiving warehouse gets captured! Oh, oh :( - -- What to do? send the items back? Impossible. -- Airbase could have been captured before and already belongs to the new coalition. local airbase=AIRBASE:FindByName(self.airbasename) @@ -2661,6 +2750,15 @@ function WAREHOUSE:onafterCaptured(From, Event, To, Coalition, Country) self.airbase=nil self.category=-1 end + + -- Debug smoke. + if self.Debug then + if Coalition==coalition.side.RED then + self.coordinate:SmokeRed() + elseif Coalition==coalition.side.BLUE then + self.coordinate:SmokeBlue() + end + end end @@ -2678,8 +2776,14 @@ function WAREHOUSE:onafterAirbaseCaptured(From, Event, To, Coalition) self:I(self.wid..text) -- Debug smoke. - self.airbase:GetCoordinate():SmokeRed() - + if self.Debug then + if Coalition==coalition.side.RED then + self.airbase:GetCoordinate():SmokeRed() + elseif Coalition==coalition.side.BLUE then + self.airbase:GetCoordinate():SmokeBlue() + end + end + -- Set airbase to nil and category to no airbase. self.airbase=nil self.category=-1 -- -1 indicates no airbase. @@ -2703,7 +2807,14 @@ function WAREHOUSE:onafterAirbaseRecaptured(From, Event, To, Coalition) self.category=self.airbase:GetDesc().category -- Debug smoke. - self.airbase:GetCoordinate():SmokeGreen() + if self.Debug then + if Coalition==coalition.side.RED then + self.airbase:GetCoordinate():SmokeRed() + elseif Coalition==coalition.side.BLUE then + self.airbase:GetCoordinate():SmokeBlue() + end + end + end @@ -2959,7 +3070,6 @@ function WAREHOUSE:_OnEventBirth(EventData) if EventData and EventData.IniGroup then local group=EventData.IniGroup - -- env.info(string.format("FF birth of group %s (alive=%s) unit %s", tostring(EventData.IniGroupName), tostring(EventData.IniGroup:IsAlive()), tostring(EventData.IniUnitName))) -- Note: Remember, group:IsAlive might(?) not return true here. local wid,aid,rid=self:_GetIDsFromGroup(group) if wid==self.uid then @@ -2980,7 +3090,7 @@ function WAREHOUSE:_OnEventEngineStartup(EventData) local group=EventData.IniGroup local wid,aid,rid=self:_GetIDsFromGroup(group) if wid==self.uid then - self:E(self.wid..string.format("Warehouse %s captured event engine startup of its asset unit %s.", self.alias, EventData.IniUnitName)) + self:I(self.wid..string.format("Warehouse %s captured event engine startup of its asset unit %s.", self.alias, EventData.IniUnitName)) end end end @@ -2995,7 +3105,7 @@ function WAREHOUSE:_OnEventTakeOff(EventData) local group=EventData.IniGroup local wid,aid,rid=self:_GetIDsFromGroup(group) if wid==self.uid then - self:E(self.wid..string.format("Warehouse %s captured event takeoff of its asset unit %s.", self.alias, EventData.IniUnitName)) + self:I(self.wid..string.format("Warehouse %s captured event takeoff of its asset unit %s.", self.alias, EventData.IniUnitName)) end end end @@ -3010,7 +3120,7 @@ function WAREHOUSE:_OnEventLanding(EventData) local group=EventData.IniGroup local wid,aid,rid=self:_GetIDsFromGroup(group) if wid==self.uid then - self:E(self.wid..string.format("Warehouse %s captured event landing of its asset unit %s.", self.alias, EventData.IniUnitName)) + self:I(self.wid..string.format("Warehouse %s captured event landing of its asset unit %s.", self.alias, EventData.IniUnitName)) -- Get request of this group local request=self:_GetRequestOfGroup(group,self.pending) @@ -3019,7 +3129,7 @@ function WAREHOUSE:_OnEventLanding(EventData) -- TODO: I might need to add a delivered table, to be better able to get this right. if request==nil then - -- Check if helicopter landed in spawn zone. If so, we call it a day and add it back to stock. + -- Check if helicopter landed in spawn zone. If so, we call it a day and add it back to stock. if group:GetCategory()==Group.Category.HELICOPTER then if self.spawnzone:IsCoordinateInZone(EventData.IniUnit:GetCoordinate()) then group:SmokeWhite() @@ -3542,11 +3652,11 @@ function WAREHOUSE:_CheckRequestNow(request) -- Assume request is okay and check scenarios. local okay=true - -- Check if receiving warehouse is running. - if not request.warehouse:IsRunning() then + -- Check if receiving warehouse is running. We do allow self requests if the warehouse is under attack though! + if (not request.warehouse:IsRunning()) and (not request.toself and self:IsAttacked()) then local text=string.format("Warehouse %s: Request denied! Receiving warehouse %s is not running. Current state %s.", self.alias, request.warehouse.alias, request.warehouse:GetState()) MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:E(self.wid..text) + self:I(self.wid..text) return false end @@ -3558,7 +3668,7 @@ function WAREHOUSE:_CheckRequestNow(request) if not _enough then local text=string.format("Warehouse %s: Request denied! Not enough (cargo) assets currently available.", self.alias) MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:E(self.wid..text) + self:I(self.wid..text) return false end @@ -3577,9 +3687,9 @@ function WAREHOUSE:_CheckRequestNow(request) --if Parking==nil and not (self.category==Airbase.Category.HELIPAD) then if Parking==nil then - local text=string.format("Warehouse %s: Request denied! Not enough free parking spots for all assets at the moment.", self.alias) + local text=string.format("Warehouse %s: Request denied! Not enough free parking spots for all requested assets at the moment.", self.alias) MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:E(self.wid..text) + self:I(self.wid..text) return false end @@ -3612,7 +3722,7 @@ function WAREHOUSE:_CheckRequestNow(request) if Parking==nil then local text=string.format("Warehouse %s: Request denied! Not enough free parking spots for all transports at the moment.", self.alias) MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:E(self.wid..text) + self:I(self.wid..text) return false end @@ -3628,7 +3738,7 @@ function WAREHOUSE:_CheckRequestNow(request) -- Not enough or the right transport carriers. local text=string.format("Warehouse %s: Request denied! Not enough transport carriers available at the moment.", self.alias) MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:E(self.wid..text) + self:I(self.wid..text) return false end @@ -4173,16 +4283,16 @@ function WAREHOUSE:_FilterStock(stock, item, value, nmax) if type(nmax)=="string" then if nmax:lower()=="all" then nmax=ntot + elseif nmax:lower()=="threequarter" then + nmax=ntot*3/4 elseif nmax:lower()=="half" then nmax=ntot/2 elseif nmax:lower()=="third" then - nmax=ntot/3 + nmax=ntot/3 elseif nmax:lower()=="quarter" then nmax=ntot/4 - elseif nmax:lower()=="fivth" then - nmax=ntot/5 else - nmax=math.min(1,ntot) + nmax=math.min(1, ntot) end end @@ -4229,10 +4339,6 @@ function WAREHOUSE:_GetAttribute(groupname) local attribute=WAREHOUSE.Attribute.UNKNOWN --#WAREHOUSE.Attribute if group then - - -- Get generalized attributes. - -- TODO: need to work on ships and trucks and SAMs and ... - -- Also the Yak-52 for example is OTHER since it only has the attribute "Battleplanes". ----------- --- Air --- @@ -4486,15 +4592,18 @@ function WAREHOUSE:_UpdateWarehouseMarkText() -- Get assets in stock. local _data=self:GetStockInfo(self.stock) - - -- Create mark text. - local marktext="Warehouse stock:\n" - for _attribute,_count in pairs(_data) do - marktext=marktext..string.format("%s=%d, ", _attribute,_count) -- Dont use \n because too many make DCS crash! - end + -- Text. + local text="Warehouse Stock:\n" + text=text..string.format("Total assets: %d\n", #_data) + local total=0 + for _attribute,_count in pairs(_data) do + local attribute=tostring(UTILS.Split(_attribute, "_")[2]) + text=text..string.format("%s=%d", attribute,_count) + end + -- Create/update marker at warehouse in F10 map. - self.markerid=self.coordinate:MarkToCoalition(marktext, self.coalition, true) + self.markerid=self.coordinate:MarkToCoalition(text, self.coalition, true) end --- Display stock items of warehouse. From 14bc7922d02c319f97ba9400bf3b1e374b5af68c Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 3 Sep 2018 16:14:50 +0200 Subject: [PATCH 307/420] Warehouse v0.3.3w --- .../Moose/Functional/Warehouse.lua | 422 ++++++++++++------ 1 file changed, 282 insertions(+), 140 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index c51d69f4e..42feeedc6 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -496,7 +496,7 @@ WAREHOUSE.version="0.3.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: How to get a specific request once the cargo is delivered? Make addrequest addasset non FSM function? Callback for requests like in SPAWN? --- TODO: Add autoselfdefence switch and user function. Default should be off. +-- DONE: Add autoselfdefence switch and user function. Default should be off. -- DONE: Warehouse re-capturing not working?! -- DONE: Naval assets dont go back into stock once arrived. -- DONE: Take cargo weight into consideration, when selecting transport assets. @@ -510,7 +510,7 @@ WAREHOUSE.version="0.3.3" -- TODO: Handle the case when units of a group die during the transfer. Adjust template?! See Grouping in SPAWN. -- DONE: Handle cases with immobile units <== should be handled by dispatcher classes. -- TODO: Handle cargo crates. --- TODO: Handle cases for aircraft carriers and other ships. Place warehouse on carrier possible? On others probably not - exclude them? +-- DONE: Handle cases for aircraft carriers and other ships. Place warehouse on carrier possible? On others probably not - exclude them? -- TODO: Add general message function for sending to coaliton or debug. -- TODO: Fine tune event handlers. -- TODO: Add save/load capability of warehouse <==> percistance after mission restart. @@ -599,14 +599,14 @@ function WAREHOUSE:New(warehouse, alias) self:AddTransition("Attacked", "SelfRequest", "*") -- Request to warehouse itself. Also possible when warehouse is under attack! self:AddTransition("Running", "Pause", "Paused") -- TODO Pause the processing of new requests. Still possible to add assets and requests. self:AddTransition("Paused", "Unpause", "Running") -- TODO Unpause the warehouse. Queued requests are processed again. - self:AddTransition("*", "Stop", "Stopped") -- TODO Stop the warehouse. + self:AddTransition("*", "Stop", "Stopped") -- DONE Stop the warehouse. self:AddTransition("*", "Save", "*") -- TODO Save the warehouse state to disk. - self:AddTransition("*", "Attacked", "Attacked") -- TODO Warehouse is under attack by enemy coalition. - self:AddTransition("Attacked", "Defeated", "Running") -- TODO Attack by other coalition was defeated! - self:AddTransition("Attacked", "Captured", "Running") -- TODO Warehouse was captured by another coalition. It must have been attacked first. - self:AddTransition("*", "AirbaseCaptured", "*") -- TODO Airbase was captured by other coalition. - self:AddTransition("*", "AirbaseRecaptured", "*") -- TODO Airbase was re-captured from other coalition. - self:AddTransition("*", "Destroyed", "*") -- TODO Warehouse was destoryed. All assets in stock are gone and warehouse is stopped. + self:AddTransition("*", "Attacked", "Attacked") -- DONE Warehouse is under attack by enemy coalition. + self:AddTransition("Attacked", "Defeated", "Running") -- DONE Attack by other coalition was defeated! + self:AddTransition("Attacked", "Captured", "Running") -- DONE Warehouse was captured by another coalition. It must have been attacked first. + self:AddTransition("*", "AirbaseCaptured", "*") -- DONE Airbase was captured by other coalition. + self:AddTransition("*", "AirbaseRecaptured", "*") -- DONE Airbase was re-captured from other coalition. + self:AddTransition("*", "Destroyed", "*") -- DONE Warehouse was destoryed. All assets in stock are gone and warehouse is stopped. ------------------------ --- Pseudo Functions --- @@ -724,18 +724,34 @@ function WAREHOUSE:New(warehouse, alias) -- @param #number delay Delay in seconds. -- @param Wrapper.Group#GROUP group Group that has arrived. + --- On after "Arrived" event user function. Called when a groups has arrived. + -- @function [parent=#WAREHOUSE] OnAfterArrived + -- @param #WAREHOUSE self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param Wrapper.Group#GROUP group Group that has arrived. - --- Triggers the FSM event "Delivered". A group has been delivered from the warehouse to another airbase or warehouse. + + --- Triggers the FSM event "Delivered". A group has been delivered from the warehouse to another warehouse. -- @function [parent=#WAREHOUSE] Delivered -- @param #WAREHOUSE self -- @param #WAREHOUSE.Pendingitem request Pending request that was now delivered. - --- Triggers the FSM event "Delivered" after a delay. A group has been delivered from the warehouse to another airbase or warehouse. + --- Triggers the FSM event "Delivered" after a delay. A group has been delivered from the warehouse to another warehouse. -- @function [parent=#WAREHOUSE] __Delivered -- @param #WAREHOUSE self -- @param #number delay Delay in seconds. -- @param #WAREHOUSE.Pendingitem request Pending request that was now delivered. + --- On after "Delivered" event user function. Called when a group has been delivered from the warehouse to another warehouse. + -- @function [parent=#WAREHOUSE] OnAfterDelivered + -- @param #WAREHOUSE self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #WAREHOUSE.Pendingitem request Pending request that was now delivered. + --- Triggers the FSM event "SelfRequest". Request was initiated to the warehouse itself. Groups are just spawned at the warehouse or the associated airbase. -- If the warehouse is currently under attack when the self request is made, the self request is added to the defending table. One the attack is defeated, @@ -777,6 +793,15 @@ function WAREHOUSE:New(warehouse, alias) -- @param DCS#coalition.side Coalition which is attacking the warehouse. -- @param DCS#country.id Country which is attacking the warehouse. + --- On after "Attacked" event user function. Called when a warehouse (zone) is under attack by an enemy. + -- @param #WAREHOUSE self + -- @function [parent=#WAREHOUSE] OnAfterAttacked + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param DCS#coalition.side Coalition which is attacking the warehouse. + -- @param DCS#country.id Country which is attacking the warehouse. + --- Triggers the FSM event "Defeated" when an attack from an enemy was defeated. -- @param #WAREHOUSE self @@ -791,6 +816,15 @@ function WAREHOUSE:New(warehouse, alias) -- @param DCS#coalition.side Coalition which is attacking the warehouse. -- @param DCS#country.id Country which is attacking the warehouse. + --- On after "Defeated" event user function. Called when an enemy attack was defeated. + -- @param #WAREHOUSE self + -- @function [parent=#WAREHOUSE] OnAfterDefeated + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param DCS#coalition.side Coalition which is attacking the warehouse. + -- @param DCS#country.id Country which is attacking the warehouse. + --- Triggers the FSM event "Captured" when a warehouse has been captured by another coalition. -- @param #WAREHOUSE self @@ -805,6 +839,15 @@ function WAREHOUSE:New(warehouse, alias) -- @param DCS#coalition.side Coalition which captured the warehouse. -- @param DCS#country.id Country which has captured the warehouse. + --- On after "Captured" event user function. Called when the warehouse has been captured by an enemy coalition. + -- @param #WAREHOUSE self + -- @function [parent=#WAREHOUSE] OnAfterCaptured + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param DCS#coalition.side Coalition which captured the warehouse. + -- @param DCS#country.id Country which has captured the warehouse. + -- --- Triggers the FSM event "AirbaseCaptured" when the airbase of the warehouse has been captured by another coalition. -- @param #WAREHOUSE self @@ -817,6 +860,14 @@ function WAREHOUSE:New(warehouse, alias) -- @param #number delay Delay in seconds. -- @param DCS#coalition.side Coalition which captured the airbase. + --- On after "AirbaseCaptured" even user function. Called when the airbase of the warehouse has been captured by another coalition. + -- @param #WAREHOUSE self + -- @function [parent=#WAREHOUSE] OnAfterAirbaseCaptured + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param DCS#coalition.side Coalition which captured the airbase. + --- Triggers the FSM event "AirbaseRecaptured" when the airbase of the warehouse has been re-captured from the other coalition. -- @param #WAREHOUSE self @@ -829,6 +880,14 @@ function WAREHOUSE:New(warehouse, alias) -- @param #number delay Delay in seconds. -- @param DCS#coalition.side Coalition which re-captured the airbase. + --- On after "AirbaseRecaptured" event user function. Called when the airbase of the warehouse has been re-captured from the other coalition. + -- @param #WAREHOUSE self + -- @function [parent=#WAREHOUSE] OnAfterAirbaseRecaptured + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param DCS#coalition.side Coalition which re-captured the airbase. + --- Triggers the FSM event "Destroyed" when the warehouse was destroyed. All services are stopped. -- @param #WAREHOUSE self @@ -839,6 +898,13 @@ function WAREHOUSE:New(warehouse, alias) -- @function [parent=#WAREHOUSE] Destroyed -- @param #number delay Delay in seconds. + --- On after "Destroyed" event user function. Called when the warehouse was destroyed. All services are stopped. + -- @param #WAREHOUSE self + -- @function [parent=#WAREHOUSE] OnAfterDestroyed + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + return self end @@ -1117,7 +1183,7 @@ function WAREHOUSE:HasConnectionNaval(warehouse, markpath, smokepath) if shippinglane then return true,1 else - env.info("FF no shipping lane!") + self:_ErrorMessage("No shipping lane!") end end @@ -1143,6 +1209,14 @@ function WAREHOUSE:GetNumberOfAssets(Descriptor, DescriptorValue) end +--- Get assignment of a request. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE.Pendingitem request The request from which the assignment is extracted. +-- @return #string The assignment text. +function WAREHOUSE:GetAssignment(request) + return request.assignment +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- FSM states ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1236,7 +1310,7 @@ end -- @param #string Event Event. -- @param #string To To state. function WAREHOUSE:onafterStop(From, Event, To) - self:I(self.wid..string.format("Warehouse %s stopped!", self.alias)) + self:_InfoMessage(string.format("Warehouse %s stopped!", self.alias)) -- Unhandle event. self:UnHandleEvent(EVENTS.Birth) @@ -1341,10 +1415,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu if type(group)=="string" then group=GROUP:FindByName(group) end - - -- Debug info. - self:I(self.wid..string.format("Adding %d assets of group %s to warehouse %s.", n, tostring(group:GetName()), self.alias)) - + if group then -- Get unique ids from group name. @@ -1358,15 +1429,19 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu -- Note the group is only added once, i.e. the ngroups parameter is ignored here. -- This is because usually these request comes from an asset that has been transfered from another warehouse and hence should only be added once. - if asset~=nil then - self:E(string.format("Adding new asset with id = %d, attribute = %s to warehouse stock.", asset.uid, asset.attribute)) + if asset~=nil then + self:_DebugMessage(string.format("Adding known asset uid=%d, attribute = %s to warehouse stock.", asset.uid, asset.attribute), 5) table.insert(self.stock, asset) else - env.error("ERROR known asset could not be found in global warehouse db!") + self:_ErrorMessage(string.format("ERROR known asset could not be found in global warehouse db!"), 0) end else + -- Debug info. + self:_Debugmessage(self.wid..string.format("Adding %d NEW assets of group %s to warehouse %s.", n, tostring(group:GetName()), self.alias), 5) + + -- This is a group that is not in the db yet. Add it n times. local assets=self:_RegisterAsset(group, n, forceattribute) @@ -1380,7 +1455,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu -- TODO: This causes a problem, when a completely new asset is added, i.e. not from a template group. -- Need to create a "zombie" template group maybe? if group:IsAlive()==true then - self:E(self.wid..string.format("Destroying group %s.", group:GetName())) + self:_DebugMessage(string.format("Destroying group %s.", group:GetName()), 5) group:Destroy(true) end @@ -1402,12 +1477,12 @@ function WAREHOUSE:_FindAssetInDB(group) local asset=WAREHOUSE.db.Assets[aid] self:E({asset=asset}) if asset==nil then - self:E(string.format("ERROR: Asset for group %s not found in the data base!", group:GetName())) + self:_ErrorMessage(string.format("ERROR: Asset for group %s not found in the data base!", group:GetName()), 0) end return asset end - self:E(string.format("ERROR: Group %s does not contain an asset ID in its name!", group:GetName())) + self:_ErrorMessage(string.format("ERROR: Group %s does not contain an asset ID in its name!", group:GetName()), 0) return nil end @@ -1660,7 +1735,7 @@ function WAREHOUSE:_SpawnAssetAircraft(asset, request, parking, uncontrolled) if #parking<#template.units then local text=string.format("ERROR: Not enough parking! Free parking = %d < %d aircraft to be spawned.", #parking, #template.units) - self:E(text) + self:_DebugMessage(text) return nil end @@ -1689,7 +1764,9 @@ function WAREHOUSE:_SpawnAssetAircraft(asset, request, parking, uncontrolled) local coord=parking[i].Coordinate --Core.Point#COORDINATE local terminal=parking[i].TerminalID --#number - coord:MarkToAll(string.format("Spawnplace unit %s terminal %d", unit.name, terminal)) + if self.Debug then + coord:MarkToAll(string.format("Spawnplace unit %s terminal %d.", unit.name, terminal)) + end unit.x=coord.x unit.y=coord.z @@ -1805,7 +1882,7 @@ function WAREHOUSE:onbeforeAddRequest(From, Event, To, warehouse, AssetDescripto end end if not gotit then - self:E(self.wid.."ERROR: Invalid request. Asset attribute is unknown!") + self:_ErrorMessage("ERROR: Invalid request. Asset attribute is unknown!", 5) okay=false end @@ -1819,26 +1896,26 @@ function WAREHOUSE:onbeforeAddRequest(From, Event, To, warehouse, AssetDescripto end end if not gotit then - self:E(self.wid.."ERROR: Invalid request. Asset category is unknown!") + self:_ErrorMessage("ERROR: Invalid request. Asset category is unknown!", 5) okay=false end elseif AssetDescriptor==WAREHOUSE.Descriptor.TEMPLATENAME then if type(AssetDescriptorValue)~="string" then - self:E(self.wid.."ERROR: Invalid request. Asset template name must be passed as a string!") + self:_ErrorMessage("ERROR: Invalid request. Asset template name must be passed as a string!", 5) okay=false end elseif AssetDescriptor==WAREHOUSE.Descriptor.UNITTYPE then if type(AssetDescriptorValue)~="string" then - self:E(self.wid.."ERROR: Invalid request. Asset unit type must be passed as a string!") + self:_ErrorMessage("ERROR: Invalid request. Asset unit type must be passed as a string!", 5) okay=false end else - self:E(self.wid.."ERROR: Invalid request. Asset descriptor is not ATTRIBUTE, CATEGORY, TEMPLATENAME or UNITTYPE!") + self:_ErrorMessage("ERROR: Invalid request. Asset descriptor is not ATTRIBUTE, CATEGORY, TEMPLATENAME or UNITTYPE!", 5) okay=false end @@ -1920,7 +1997,7 @@ end -- @param #WAREHOUSE.Queueitem Request Information table of the request. -- @return #boolean If true, request is granted. function WAREHOUSE:onbeforeRequest(From, Event, To, Request) - self:E({warehouse=self.alias, request=Request}) + self:T3({warehouse=self.alias, request=Request}) -- Distance from warehouse to requesting warehouse. local distance=self.coordinate:Get2DDistance(Request.warehouse.coordinate) @@ -1929,9 +2006,8 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request) local _assets=Request.cargoassets if Request.nasset==0 then - local text=string.format("Request denied! Zero assets were requested.") - MESSAGE:New(text, 10):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:E(self.wid..text) + local text=string.format("Warehouse %s: Request denied! Zero assets were requested.", self.alias) + self:_InfoMessage(text, 10) return false end @@ -1940,10 +2016,9 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request) local asset=_asset --#WAREHOUSE.Assetitem -- Check if destination is in range. - if asset.range1 then @@ -2587,10 +2667,9 @@ function WAREHOUSE:onafterDelivered(From, Event, To, request) -- Debug info local text=string.format("Warehouse %s: All assets delivered to warehouse %s!", self.alias, request.warehouse.alias) - MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:I(self.wid..text) + self:_InfoMessage(text, 5) - -- Make some noise :) + -- Make some noise :) self:_Fireworks(request.warehouse.coordinate) -- Remove pending request: @@ -2606,24 +2685,27 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param Core.Set#SET_GROUP groupset The set of cargo groups that was delivered to the warehouse itself. +-- @param Core.Set#SET_GROUP groupset The set of asset groups that was delivered to the warehouse itself. -- @param #WAREHOUSE.Pendingitem request Pending self request. function WAREHOUSE:onafterSelfRequest(From, Event, To, groupset, request) -- Debug info. - self:I(self.wid..string.format("Assets spawned at warehouse %s after self request!", self.alias)) + self:_DebugMessage(string.format("Assets spawned at warehouse %s after self request!", self.alias)) -- Debug info. for _,_group in pairs(groupset:GetSetObjects()) do local group=_group --Wrapper.Group#GROUP - local text=string.format("Group name = %s, IsAlive=%s.", tostring(group:GetName()), tostring(group:IsAlive())) - env.info(text) - --group:SmokeGreen() + + --local text=string.format("Group name = %s, IsAlive=%s.", tostring(group:GetName()), tostring(group:IsAlive())) + --env.info(text) + + if self.Debug then + group:FlareGreen() + end end -- Add a "defender request" to be able to despawn all assets once defeated. - if self:IsAttacked() then - --self.defenderrequest=request + if self:IsAttacked() then table.insert(self.defending, request) end @@ -2642,8 +2724,7 @@ function WAREHOUSE:onafterAttacked(From, Event, To, Coalition, Country) -- Warning. local text=string.format("Warehouse %s: We are under attack!", self.alias) - MESSAGE:New(text, 20):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:I(self.wid..text) + self:_InfoMessage(text) -- Debug smoke. if self.Debug then @@ -2653,16 +2734,17 @@ function WAREHOUSE:onafterAttacked(From, Event, To, Coalition, Country) -- Spawn all ground units in the spawnzone? if self.autodefence then local nground=self:GetNumberOfAssets(WAREHOUSE.Descriptor.CATEGORY, Group.Category.GROUND) - local text=string.format("Warehouse auto defence activated. Deploying all %d ground assets.", nground) - MESSAGE:New(text, 20):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:I(self.wid..text) + local text=string.format("Warehouse auto defence activated.\n") + if nground>0 then + text=text..string.format("Deploying all %d ground assets.", nground) + + -- Add self request. self:AddRequest(self, WAREHOUSE.Descriptor.CATEGORY, Group.Category.GROUND, "all", nil, nil , 0) else - local text=string.format("No ground assets currently available.") - MESSAGE:New(text, 20):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:I(self.wid..text) + text=text..string.format("No ground assets currently available.") end + self:_InfoMessage(text) else local text=string.format("Warehouse auto defence inactive.") self:I(self.wid..text) @@ -2678,8 +2760,7 @@ function WAREHOUSE:onafterDefeated(From, Event, To) -- Message. local text=string.format("Warehouse %s: Enemy attack was defeated!", self.alias) - MESSAGE:New(text, 20):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:I(self.wid..text) + self:_InfoMessage(text) -- Debug smoke. if self.Debug then @@ -2703,8 +2784,7 @@ function WAREHOUSE:onafterDefeated(From, Event, To) -- Add asset group back to stock after 60 seconds. self:__AddAsset(60, group) end - - --self:_DeleteQueueItem(request, self.defending) + end self.defending=nil @@ -2723,8 +2803,7 @@ function WAREHOUSE:onafterCaptured(From, Event, To, Coalition, Country) -- Message. local text=string.format("Warehouse %s: We were captured by enemy coalition (%d)!", self.alias, Coalition) - MESSAGE:New(text, 20):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:I(self.wid..text) + self:_InfoMessage(text) -- Respawn warehouse with new coalition/country. self.warehouse:ReSpawn(Country) @@ -2772,8 +2851,7 @@ function WAREHOUSE:onafterAirbaseCaptured(From, Event, To, Coalition) -- Message. local text=string.format("Warehouse %s: Our airbase %s was captured by the enemy (coalition=%d)!", self.alias, self.airbasename, Coalition) - MESSAGE:New(text, 20):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:I(self.wid..text) + self:_InfoMessage(text) -- Debug smoke. if self.Debug then @@ -2799,8 +2877,7 @@ function WAREHOUSE:onafterAirbaseRecaptured(From, Event, To, Coalition) -- Message. local text=string.format("Warehouse %s: We recaptured our airbase %s from the enemy (coalition=%d)!", self.alias, self.airbasename, Coalition) - MESSAGE:New(text, 20):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:I(self.wid..text) + self:_InfoMessage(text) -- Set airbase and category. self.airbase=AIRBASE:FindByName(self.airbasename) @@ -2827,8 +2904,7 @@ function WAREHOUSE:onafterDestroyed(From, Event, To) -- Message. local text=string.format("Warehouse %s was destroyed!", self.alias) - MESSAGE:New(text, 20):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:I(self.wid..text) + self:_InfoMessage(text) -- Stop warehouse FSM. self:Stop() @@ -2999,7 +3075,7 @@ end -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group The group that arrived. function WAREHOUSE:_ArrivedSimple(group) - env.info(string.format("Group %s arrived (simple)!", tostring(group:GetName()))) + self:_DebugMessage(string.format("Group %s arrived (simple)!", tostring(group:GetName()))) if group then --Trigger "Arrived event. @@ -3042,8 +3118,7 @@ function WAREHOUSE:_OnEventArrived(EventData) -- Debug info. local text=string.format("Air asset group %s arrived at warehouse %s.", group:GetName(), self.alias) - MESSAGE:New(text, 20):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:I(self.wid..text) + self:_InfoMessage(text) -- Trigger arrived event for this group. Note that each unit of a group will trigger this event. So the onafterArrived function needs to take care of that. -- Actually, we only take the first unit of the group that arrives. If it does, we assume the whole group arrived, which might not be the case, since @@ -3073,7 +3148,7 @@ function WAREHOUSE:_OnEventBirth(EventData) -- Note: Remember, group:IsAlive might(?) not return true here. local wid,aid,rid=self:_GetIDsFromGroup(group) if wid==self.uid then - self:E(self.wid..string.format("Warehouse %s captured event birth of its asset unit %s.", self.alias, EventData.IniUnitName)) + self:T(self.wid..string.format("Warehouse %s captured event birth of its asset unit %s.", self.alias, EventData.IniUnitName)) else --self:T3({wid=wid, uid=self.uid, match=(wid==self.uid), tw=type(wid), tu=type(self.uid)}) end @@ -3090,7 +3165,7 @@ function WAREHOUSE:_OnEventEngineStartup(EventData) local group=EventData.IniGroup local wid,aid,rid=self:_GetIDsFromGroup(group) if wid==self.uid then - self:I(self.wid..string.format("Warehouse %s captured event engine startup of its asset unit %s.", self.alias, EventData.IniUnitName)) + self:T(self.wid..string.format("Warehouse %s captured event engine startup of its asset unit %s.", self.alias, EventData.IniUnitName)) end end end @@ -3105,7 +3180,7 @@ function WAREHOUSE:_OnEventTakeOff(EventData) local group=EventData.IniGroup local wid,aid,rid=self:_GetIDsFromGroup(group) if wid==self.uid then - self:I(self.wid..string.format("Warehouse %s captured event takeoff of its asset unit %s.", self.alias, EventData.IniUnitName)) + self:T(self.wid..string.format("Warehouse %s captured event takeoff of its asset unit %s.", self.alias, EventData.IniUnitName)) end end end @@ -3120,7 +3195,7 @@ function WAREHOUSE:_OnEventLanding(EventData) local group=EventData.IniGroup local wid,aid,rid=self:_GetIDsFromGroup(group) if wid==self.uid then - self:I(self.wid..string.format("Warehouse %s captured event landing of its asset unit %s.", self.alias, EventData.IniUnitName)) + self:T(self.wid..string.format("Warehouse %s captured event landing of its asset unit %s.", self.alias, EventData.IniUnitName)) -- Get request of this group local request=self:_GetRequestOfGroup(group,self.pending) @@ -3132,9 +3207,13 @@ function WAREHOUSE:_OnEventLanding(EventData) -- Check if helicopter landed in spawn zone. If so, we call it a day and add it back to stock. if group:GetCategory()==Group.Category.HELICOPTER then if self.spawnzone:IsCoordinateInZone(EventData.IniUnit:GetCoordinate()) then - group:SmokeWhite() - self:__AddAsset(30, group) - end + + self:_DebugMessage("Helicopter landed in spawn zone. No pending request. Putting back into stock.") + if self.Debug then + group:SmokeWhite() + end + self:__AddAsset(30, group) + end end end @@ -3153,7 +3232,7 @@ function WAREHOUSE:_OnEventEngineShutdown(EventData) local group=EventData.IniGroup local wid,aid,rid=self:_GetIDsFromGroup(group) if wid==self.uid then - self:E(self.wid..string.format("Warehouse %s captured event engine shutdown of its asset unit %s.", self.alias, EventData.IniUnitName)) + self:T(self.wid..string.format("Warehouse %s captured event engine shutdown of its asset unit %s.", self.alias, EventData.IniUnitName)) end end end @@ -3169,7 +3248,9 @@ function WAREHOUSE:_OnEventCrashOrDead(EventData) -- Check if warehouse was destroyed. local warehousename=self.warehouse:GetName() if EventData.IniUnitName==warehousename then - env.info(self.wid..string.format("Warehouse %s alias %s was destroyed!", warehousename, self.alias)) + self:_DebugMessage(string.format("Warehouse %s alias %s was destroyed!", warehousename, self.alias)) + + -- Trigger Destroyed event. self:Destroyed() end end @@ -3179,7 +3260,7 @@ function WAREHOUSE:_OnEventCrashOrDead(EventData) local group=EventData.IniGroup local wid,aid,rid=self:_GetIDsFromGroup(group) if wid==self.uid then - self:E(self.wid..string.format("Warehouse %s captured event dead or crash of its asset unit %s.", self.alias, EventData.IniUnitName)) + self:T(self.wid..string.format("Warehouse %s captured event dead or crash of its asset unit %s.", self.alias, EventData.IniUnitName)) end end @@ -3209,7 +3290,7 @@ function WAREHOUSE:_OnEventBaseCaptured(EventData) local NewCoalitionAirbase=airbase:GetCoalition() -- Debug info - self:I(self.wid..string.format("Airbase of warehouse %s (coalition = %d) was captured! New owner coalition = %d.",self.alias, self.coalition, NewCoalitionAirbase)) + self:T(self.wid..string.format("Airbase of warehouse %s (coalition = %d) was captured! New owner coalition = %d.",self.alias, self.coalition, NewCoalitionAirbase)) -- So what can happen? -- Warehouse is blue, airbase is blue and belongs to warehouse and red captures it ==> self.airbase=nil @@ -3655,8 +3736,7 @@ function WAREHOUSE:_CheckRequestNow(request) -- Check if receiving warehouse is running. We do allow self requests if the warehouse is under attack though! if (not request.warehouse:IsRunning()) and (not request.toself and self:IsAttacked()) then local text=string.format("Warehouse %s: Request denied! Receiving warehouse %s is not running. Current state %s.", self.alias, request.warehouse.alias, request.warehouse:GetState()) - MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:I(self.wid..text) + self:_InfoMessage(text, 5) return false end @@ -3667,8 +3747,7 @@ function WAREHOUSE:_CheckRequestNow(request) -- Check if enough assets are in stock. if not _enough then local text=string.format("Warehouse %s: Request denied! Not enough (cargo) assets currently available.", self.alias) - MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:I(self.wid..text) + self:_InfoMessage(text, 5) return false end @@ -3688,8 +3767,7 @@ function WAREHOUSE:_CheckRequestNow(request) --if Parking==nil and not (self.category==Airbase.Category.HELIPAD) then if Parking==nil then local text=string.format("Warehouse %s: Request denied! Not enough free parking spots for all requested assets at the moment.", self.alias) - MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:I(self.wid..text) + self:_InfoMessage(text, 5) return false end @@ -3721,8 +3799,7 @@ function WAREHOUSE:_CheckRequestNow(request) local Parking=self:_FindParkingForAssets(self.airbase,_transports) if Parking==nil then local text=string.format("Warehouse %s: Request denied! Not enough free parking spots for all transports at the moment.", self.alias) - MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:I(self.wid..text) + self:_InfoMessage(text, 5) return false end @@ -3737,8 +3814,7 @@ function WAREHOUSE:_CheckRequestNow(request) -- Not enough or the right transport carriers. local text=string.format("Warehouse %s: Request denied! Not enough transport carriers available at the moment.", self.alias) - MESSAGE:New(text, 5):ToCoalitionIf(self.coalition, self.Report or self.Debug) - self:I(self.wid..text) + self:_InfoMessage(text, 5) return false end @@ -3877,7 +3953,7 @@ function WAREHOUSE:_GetTransportsForAssets(request) text=text..string.format("Total cargo bay capacity = %.1f kg\n", totalcargobay) text=text..string.format("Total cargo weight = %.1f kg\n", totalcargoweight) text=text..string.format("Minimum number of runs = %.1f", totalcargoweight/totalcargobay) - self:I(self.wid..text) + self:_DebugMessage(text) return used_transports end @@ -3920,7 +3996,7 @@ function WAREHOUSE:_CheckQueue() -- Delete invalid requests. for _,_request in pairs(invalid) do - self:E(self.wid..string.format("Deleting invalid request id=%d.",_request.uid)) + self:T(self.wid..string.format("Deleting invalid request id=%d.",_request.uid)) self:_DeleteQueueItem(_request, self.queue) end @@ -4116,7 +4192,7 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) -- Add parkingspot for this asset unit. table.insert(parking[_asset.uid], parkingspot) - self:E(self.wid..string.format("Parking spot #%d is free for asset id=%d!", _termid, _asset.uid)) + self:T(self.wid..string.format("Parking spot #%d is free for asset id=%d!", _termid, _asset.uid)) -- Add the unit as obstacle so that this spot will not be available for the next unit. -- TODO Alternatively, I could remove this parking spot from the table, right? @@ -4125,10 +4201,12 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) gotit=true break else - self:E(self.wid..string.format("Parking spot #%d is occupied or not big enough!", _termid)) - local coord=problem.coord --Core.Point#COORDINATE - local text=string.format("Obstacle blocking spot #%d is %s type %s with size=%.1f m and distance=%.1f m.", _termid, problem.name, problem.type, problem.size, problem.dist) - coord:MarkToAll(string.format(text)) + self:T(self.wid..string.format("Parking spot #%d is occupied or not big enough!", _termid)) + if self.Debug then + local coord=problem.coord --Core.Point#COORDINATE + local text=string.format("Obstacle blocking spot #%d is %s type %s with size=%.1f m and distance=%.1f m.", _termid, problem.name, problem.type, problem.size, problem.dist) + coord:MarkToAll(string.format(text)) + end end end -- check terminal type @@ -4136,7 +4214,7 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) if not gotit then - self:E(self.wid..string.format("WARNING: No free parking spot for asset id=%d",_asset.uid)) + self:T(self.wid..string.format("WARNING: No free parking spot for asset id=%d",_asset.uid)) return nil end end -- loop over asset units @@ -4541,7 +4619,7 @@ function WAREHOUSE:_DisplayStatus() text=text..string.format("Pending requests = %d\n", #self.pending) text=text..string.format("------------------------------------------------------\n") text=text..self:_GetStockAssetsText() - env.info(text) + self:T(text) --TODO: number of ground, air, naval assets. end @@ -4636,7 +4714,45 @@ function WAREHOUSE:_Fireworks(coord) end end ---- Make a flight plan from a departure to a destination airport. +--- Info Message. +-- @param #WAREHOUSE self +-- @param #string text The text of the error message. +-- @param #number duration Message display duration in seconds. Default 20 sec. +function WAREHOUSE:_InfoMessage(text, duration) + duration=duration or 20 + if duration>0 then + MESSAGE:New(text, duration):ToCoalitionIf(self.coalition, self.Debug or self.Report) + end + self:I(self.wid..text) +end + + +--- Debug message. +-- @param #WAREHOUSE self +-- @param #string text The text of the error message. +-- @param #number duration Message display duration in seconds. Default 20 sec. +function WAREHOUSE:_DebugMessage(text, duration) + duration=duration or 20 + if duration>0 then + MESSAGE:New(text, duration):ToAllIf(self.Debug) + end + self:T(self.wid..text) +end + +--- Error message. +-- @param #WAREHOUSE self +-- @param #string text The text of the error message. +-- @param #number duration Message display duration in seconds. Default 20 sec. +function WAREHOUSE:_ErrorMessage(text, duration) + duration=duration or 20 + if duration>0 then + MESSAGE:New(text, duration):ToAllIf(self.Debug) + end + self:E(self.wid..text) +end + + +--- Calculate the maximum height an aircraft can reach for the given parameters. -- @param #WAREHOUSE self -- @param #number D Total distance in meters from Departure to holding point at destination. -- @param #number alphaC Climb angle in rad. @@ -4644,7 +4760,8 @@ end -- @param #number Hdep AGL altitude of departure point. -- @param #number Hdest AGL altitude of destination point. -- @param #number Deltahhold Relative altitude of holding point above destination. -function WAREHOUSE:_MakeFlightplan(D, alphaC, alphaD, Hdep, Hdest, Deltahhold) +-- @return #number Maximum height the aircraft can reach. +function WAREHOUSE:_GetMaxHeight(D, alphaC, alphaD, Hdep, Hdest, Deltahhold) local Hhold=Hdest+Deltahhold local hdest=Hdest-Hdep @@ -4825,7 +4942,7 @@ function WAREHOUSE:_GetFlightplan(asset, departure, destination) h_holding=UTILS.Randomize(h_holding, 0.2) -- Max holding altitude. - local DeltaholdingMax=self:_MakeFlightplan(d_total, AlphaClimb, AlphaDescent, H_departure, H_holding, 0) + local DeltaholdingMax=self:_GetMaxHeight(d_total, AlphaClimb, AlphaDescent, H_departure, H_holding, 0) if h_holding>DeltaholdingMax then h_holding=math.abs(DeltaholdingMax) @@ -4839,7 +4956,7 @@ function WAREHOUSE:_GetFlightplan(asset, departure, destination) --------------------------- -- Get max flight altitude relative to H_departure. - local h_max=self:_MakeFlightplan(d_total, AlphaClimb, AlphaDescent, H_departure, H_holding, h_holding) + local h_max=self:_GetMaxHeight(d_total, AlphaClimb, AlphaDescent, H_departure, H_holding, h_holding) -- Max flight level ASL aircraft can reach for given angles and distance. local FLmax = h_max+H_departure @@ -4921,6 +5038,53 @@ function WAREHOUSE:_GetFlightplan(asset, departure, destination) c[#c+1]=Pdeparture wp[#wp+1]=Pdeparture:WaypointAir("RADIO", COORDINATE.WaypointType.TakeOffParking, COORDINATE.WaypointAction.FromParkingArea, VxClimb, true, departure, nil, "Departure") + --- Begin of Cruise + local Pcruise=Pdeparture:Translate(d_climb, heading) + Pcruise.y=FLcruise + c[#c+1]=Pcruise + wp[#wp+1]=Pcruise:WaypointAir("BARO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, VxCruise, true, nil, nil, "Cruise") + + --- Descent + local Pdescent=Pcruise:Translate(d_cruise, heading) + Pdescent.y=FLcruise + c[#c+1]=Pdescent + wp[#wp+1]=Pdescent:WaypointAir("BARO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, VxDescent, true, nil, nil, "Descent") + + --- Holding point + Pholding.y=H_holding+h_holding + c[#c+1]=Pholding + wp[#wp+1]=Pholding:WaypointAir("BARO", COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, VxHolding, true, nil, nil, "Holding") + + --- Final destination. + c[#c+1]=Pdestination + wp[#wp+1]=Pdestination:WaypointAir("RADIO", COORDINATE.WaypointType.Land, COORDINATE.WaypointAction.Landing, VxFinal, true, destination, nil, "Final Destination") + + + -- Mark points at waypoints for debugging. + if self.Debug then + for i,coord in pairs(c) do + local coord=coord --Core.Point#COORDINATE + local dist=0 + if i>1 then + dist=coord:Get2DDistance(c[i-1]) + end + coord:MarkToAll(string.format("Waypoint %i, distance = %.2f km",i, dist/1000)) + end + end + + return wp,c +end + + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--[[ + --- Departure/Take-off + c[#c+1]=Pdeparture + wp[#wp+1]=Pdeparture:WaypointAir("RADIO", COORDINATE.WaypointType.TakeOffParking, COORDINATE.WaypointAction.FromParkingArea, VxClimb, true, departure, nil, "Departure") + --- Climb local Pclimb=Pdeparture:Translate(d_climb/2, heading) Pclimb.y=H_departure+(FLcruise-H_departure)/2 @@ -4952,27 +5116,5 @@ function WAREHOUSE:_GetFlightplan(asset, departure, destination) --- Final destination. c[#c+1]=Pdestination - wp[#wp+1]=Pcruise2:WaypointAir("RADIO", COORDINATE.WaypointType.Land, COORDINATE.WaypointAction.Landing, VxFinal, true, destination, nil, "Final Destination") - - - -- Mark points at waypoints for debugging. - if self.Debug then - for i,coord in pairs(c) do - local coord=coord --Core.Point#COORDINATE - env.info(i) - local dist=0 - if i>1 then - dist=coord:Get2DDistance(c[i-1]) - end - coord:MarkToAll(string.format("Waypoint %i, dist = %.2f km",i, dist/1000)) - end - end - - return wp,c -end - - -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - + wp[#wp+1]=Pdestination:WaypointAir("RADIO", COORDINATE.WaypointType.Land, COORDINATE.WaypointAction.Landing, VxFinal, true, destination, nil, "Final Destination") +]] From 13451ed6023586d781072cbe4f72aa98afb89cb7 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 3 Sep 2018 21:51:38 +0200 Subject: [PATCH 308/420] Warehosue v0.3.4 Fixed some bugs. Helos on ships are now not spawned in uncontrolled state due to DCS bug. Self requests are not deleted from the pending queue any more in case they return to their warehouse. --- Moose Development/Moose/AI/AI_Formation.lua | 2 +- Moose Development/Moose/Core/Zone.lua | 3 +- .../Moose/Functional/Warehouse.lua | 40 +++++++++++++------ 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Formation.lua b/Moose Development/Moose/AI/AI_Formation.lua index 8be01bd23..489e69cf3 100644 --- a/Moose Development/Moose/AI/AI_Formation.lua +++ b/Moose Development/Moose/AI/AI_Formation.lua @@ -650,7 +650,7 @@ function AI_FORMATION:onafterFormationLine( FollowGroupSet, From , Event , To, X local FollowSet = FollowGroupSet:GetSet() - local i = 0 + local i = 1 --FF i=0 caused first unit to have no XSpace! Probably needs further adjustments. This is just a quick work around. for FollowID, FollowGroup in pairs( FollowSet ) do diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 1ab9d9f78..1adc3f820 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -1118,7 +1118,8 @@ function ZONE_UNIT:GetRandomVec2() self:F( self.ZoneName ) local RandomVec2 = {} - local Vec2 = self.ZoneUNIT:GetVec2() + --local Vec2 = self.ZoneUNIT:GetVec2() -- FF: This does not take care of the new offset feature! + local Vec2 = self:GetVec2() if not Vec2 then Vec2 = self.LastVec2 diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 42feeedc6..f25c16ab8 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -458,7 +458,7 @@ WAREHOUSE.Attribute = { NAVAL_ARMEDSHIP="Naval_ArmedShip", NAVAL_UNARMEDSHIP="Naval_UnarmedShip", NAVAL_OTHER="Naval_OtherNaval", - UNKNOWN="Unknown", + UNKNOWN="Other_Unknown", } --- Cargo transport type. Defines how assets are transported to their destination. @@ -489,7 +489,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.3.3" +WAREHOUSE.version="0.3.4" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -1177,7 +1177,13 @@ end function WAREHOUSE:HasConnectionNaval(warehouse, markpath, smokepath) if warehouse then - + + -- Self request + if warehouse.warehouse:GetName()==self.warehouse:GetName() then + return true,1 + end + + -- Get shipping lane. local shippinglane=self.shippinglanes[warehouse.warehouse:GetName()] if shippinglane then @@ -1354,6 +1360,9 @@ end function WAREHOUSE:onafterStatus(From, Event, To) self:I(self.wid..string.format("Checking status of warehouse %s. Current FSM state %s. Global warehouse assets = %d.", self.alias, self:GetState(), #WAREHOUSE.db.Assets)) + -- Update coordinate in case we have a "moving" warehouse (e.g. on a carrier). + self.coordinate=self.warehouse:GetCoordinate() + -- Print status. self:_DisplayStatus() @@ -1439,7 +1448,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu else -- Debug info. - self:_Debugmessage(self.wid..string.format("Adding %d NEW assets of group %s to warehouse %s.", n, tostring(group:GetName()), self.alias), 5) + self:_DebugMessage(self.wid..string.format("Adding %d NEW assets of group %s to warehouse %s.", n, tostring(group:GetName()), self.alias), 5) -- This is a group that is not in the db yet. Add it n times. @@ -1640,6 +1649,8 @@ function WAREHOUSE:_SpawnAssetGroundNaval(asset, request, spawnzone, aioff) -- Get a random coordinate in the spawn zone. local coord=spawnzone:GetRandomCoordinate() + + --spawnzone:SmokeZone(1, 30) -- Translate the position of the units. for i=1,#template.units do @@ -1690,8 +1701,9 @@ end -- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. -- @param #table parking Parking data for this asset. -- @param #boolean uncontrolled Spawn aircraft in uncontrolled state. +-- @param #boolean hotstart Spawn aircraft with engines already on. Default is a cold start with engines off. -- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned. -function WAREHOUSE:_SpawnAssetAircraft(asset, request, parking, uncontrolled) +function WAREHOUSE:_SpawnAssetAircraft(asset, request, parking, uncontrolled, hotstart) if asset and asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then @@ -1705,8 +1717,6 @@ function WAREHOUSE:_SpawnAssetAircraft(asset, request, parking, uncontrolled) template.route.points=self:_GetFlightplan(asset, self.airbase, request.warehouse.airbase) else - - local hotstart=true -- Cold start (default). local _type=COORDINATE.WaypointType.TakeOffParking @@ -1782,6 +1792,12 @@ function WAREHOUSE:_SpawnAssetAircraft(asset, request, parking, uncontrolled) template.x = template.units[1].x template.y = template.units[1].y + -- DCS bug workaround. Spawning helos in uncontrolled state on carriers causes a big spash! + -- See https://forums.eagle.ru/showthread.php?t=219550 + if AirbaseCategory == Airbase.Category.SHIP and asset.category==Group.Category.HELICOPTER then + uncontrolled=false + end + -- Uncontrolled spawning. template.uncontrolled=uncontrolled @@ -2710,7 +2726,7 @@ function WAREHOUSE:onafterSelfRequest(From, Event, To, groupset, request) end -- Remove pending request. - self:_DeleteQueueItem(request, self.pending) + --self:_DeleteQueueItem(request, self.pending) end --- On after "Attacked" event. Warehouse is under attack by an another coalition. @@ -3468,7 +3484,7 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) local request=_request --#WAREHOUSE.Queueitem -- Debug info. - self:T2(self.wid..string.format("Checking request = %d.", request.uid)) + self:T2(self.wid..string.format("Checking request id=%d.", request.uid)) -- Let's assume everything is fine. local valid=true @@ -4632,12 +4648,10 @@ function WAREHOUSE:_GetStockAssetsText(messagetoall) -- Get assets in stock. local _data=self:GetStockInfo(self.stock) - --[[ local function _sort(a,b) - return a Date: Tue, 4 Sep 2018 16:23:24 +0200 Subject: [PATCH 309/420] Warehouse v0.3.4w --- .../Moose/Functional/Warehouse.lua | 431 ++++++++++++++++-- 1 file changed, 404 insertions(+), 27 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index f25c16ab8..d4db49502 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -15,7 +15,7 @@ -- -- === -- --- ### Authors: **funkyfranky** +-- ### Authors: **funkyfranky**, FlightControl (cargo dispatcher classes) -- -- @module Functional.Warehouse -- @image Warehouse.JPG @@ -45,6 +45,7 @@ -- @field #table stock Table holding all assets in stock. Table entries are of type @{#WAREHOUSE.Assetitem}. -- @field #table queue Table holding all queued requests. Table entries are of type @{#WAREHOUSE.Queueitem}. -- @field #table pending Table holding all pending requests, i.e. those that are currently in progress. Table elements are of type @{#WAREHOUSE.Pendingitem}. +-- @field #table delivered Table holding all delivered requests. -- @field #table defending Table holding all defending requests, i.e. self requests that were if the warehouse is under attack. Table elements are of type @{#WAREHOUSE.Pendingitem}. -- @field Core.Zone#ZONE portzone Zone defining the port of a warehouse. This is where naval assets are spawned. -- @field #table shippinglanes Table holding the user defined shipping between warehouses. @@ -77,7 +78,8 @@ -- by themselfs. Once arrived at the requesting warehouse, the assets go into the stock of the requestor and can be activated/deployed when necessary. -- -- ## What assets can be stored? --- Any kind of ground, airborne or naval asset can be stored. +-- Any kind of ground, airborne or naval asset can be stored and are spawned upon request. +-- The fact that the assets "live" only virtually in the stock has a positive impact on the game performance. -- -- ## What means of transportation are available? -- Firstly, all mobile assets can be send from warehouse to another on their own. @@ -228,9 +230,13 @@ -- By default, the zone were ground assets are spawned is a circular zone around the physical location of the warehouse with a radius of 200 meters. However, the location of the -- spawn zone can be set by the @{#WAREHOUSE.SetSpawnZone}(*zone*) functions. It is advisable to choose a zone which is clear of obstacles. -- +-- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_Batumi.png) +-- -- The parameter *zone* is a MOOSE @{Core.Zone#ZONE} object. So one can, e.g., use trigger zones defined in the mission editor. If a cicular zone is not desired, one -- can use a polygon zone (see @{Core.Zone#ZONE_POLYGON}). -- +-- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_SpawnPolygon.png) +-- -- ## Road Connections -- -- Ground assets will use a road connection to travel from one warehouse to another. Therefore, a proper road connection is necessary. @@ -266,12 +272,16 @@ -- A port in this context is the zone where all naval assets are spawned. This zone can be defined with the function @{#WAREHOUSE.SetPortZone}(*zone*), where the parameter -- *zone* is a MOOSE zone. So again, this can be create from a trigger zone defined in the mission editor or if a general shape is desired by a @{Core.Zone#ZONE_POLYGON}. -- +-- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_PortZone.png) +-- -- ### Defining Shipping Lanes -- -- A shipping lane between to warehouses can be defined by the @{#WAREHOUSE.AddShippingLane}(*remotewarehouse*, *group*) function. The first parameter *remotewarehouse* -- is the warehouse which should be connected to the present warehouse. -- --- The parameter *group* should be a late activated group defined in the mission editor. The waypoints of this group are used as waypoints of the shipping lane. +-- The parameter *group* should be a late activated group defined in the mission editor. The waypoints of this group are used as waypoints of the shipping lane. +-- +-- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_ShippingLane.png) -- -- -- # Strategic Considerations @@ -317,8 +327,347 @@ -- === -- -- # Examples +-- +-- ## Example 1: Self Request +-- +-- Ground troops are taken from the Batumi warehouse stock and spawned in its spawn zone. After a short delay, they are added back to the warehouse stock. +-- Also a new request is made. Hence, the groups will be spawned, added back to the warehouse, spawned again and so on and so forth... +-- +-- -- Start warehouse Batumi. +-- warehouse.Batumi:Start() +-- +-- -- Add five groups of infantry as assets. +-- warehouse.Batumi:AddAsset(GROUP:FindByName("Infantry Platoon Alpha"), 5) +-- +-- -- Add self request for three infantry at Batumi. +-- warehouse.Batumi:AddRequest(warehouse.Batumi, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 3) +-- +-- +-- --- Self request event. Triggered once the assets are spawned in the spawn zone or at the airbase. +-- function warehouse.Batumi:OnAfterSelfRequest(From, Event, To, groupset, request) +-- local mygroupset=groupset --Core.Set#SET_GROUP +-- +-- -- Loop over all groups spawned from that request. +-- for _,group in pairs(mygroupset:GetSetObjects()) do +-- local group=group --Wrapper.Group#GROUP +-- +-- -- Gree smoke on spawned group. +-- group:SmokeGreen() +-- group:FlareRed() +-- +-- -- Put asset back to stock after 10 seconds. +-- warehouse.Batumi:__AddAsset(10, group) +-- end +-- +-- -- Add new self request after 20 seconds. +-- warehouse.Batumi:__AddRequest(20, warehouse.Batumi, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 3) +-- +-- end -- --- **WIP** +-- ## Example 2: Self propelled Ground Troops +-- +-- Warehouse Berlin, which is a FARP near Batumi, requests infantry and troop transports from the warehouse at Batumi. +-- The groups are spawned at Batumi and move by themselfs from Batumi to Berlin using the roads. +-- Once the troops have arrived at Berlin, the troops are automatically added to the warehouse stock of Berlin. +-- While on the road, Batumi has requested back two APCs from Berlin. Since Berlin does not have the assets in stock, +-- the request is queued. After the troops have arrived, Berlin is sending back the APCs to Batumi. +-- +-- -- Start Warehouse at Batumi. +-- warehouse.Batumi:Start() +-- +-- -- Add 20 infantry groups as assets at Batumi. +-- warehouse.Batumi:AddAsset("Infantry Platoon Alpha", 20) +-- warehouse.Batumi:AddAsset("TPz Fuchs", 5) +-- +-- -- Start Warehouse Berlin. +-- warehouse.Berlin:Start() +-- +-- -- Warehouse Berlin requests 10 infantry groups and 3 APCs from warehouse Batumi. +-- warehouse.Batumi:AddRequest(warehouse.Berlin, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 10) +-- warehouse.Batumi:AddRequest(warehouse.Berlin, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_APC, 5) +-- +-- -- Request from Batumi for 2 APCs. Initially these are not in stock. When they become available, the request is executed. +-- warehouse.Berlin:AddRequest(warehouse.Batumi, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_APC, 2) +-- +-- ## Example 3: Self Propelled Airborne Assets +-- +-- Warehouse Senaki receives requests from Kutaisi for one Yak-52s and from FARP London for three Hueys. +-- Assets are spawned in Senaki and make their way to the requesting warehouses. +-- Once the units have arrived they are added to the stock of the receiving warehouses and can be used for further assignments. +-- +-- -- Start sending warehouse. +-- warehouse.Senaki:Start() +-- +-- -- Add assets. +-- warehouse.Senaki:AddAsset("Yak-52", 10) +-- warehouse.Senaki:AddAsset("Huey", 10) +-- +-- -- Start receiving warehouses +-- warehouse.Kutaisi:Start() +-- warehouse.London:Start() +-- +-- -- Kusaisi requests one Yak-52 form Senaki. FARP London requests three UH-1H Huys from Senaki. +-- warehouse.Senaki:AddRequest(warehouse.Kutaisi, WAREHOUSE.Descriptor.TEMPLATENAME, "Yak-52", 1) +-- warehouse.Senaki:AddRequest(warehouse.London, WAREHOUSE.Descriptor.TEMPLATENAME, "Huey", 3) +-- +-- ## Example 4: Transport of Assets by APCs +-- +-- Warehouse at FARP Berlin requests three infantry groups from Batumi. These assets shall be transported using one APC. +-- Infantry and APC are spawned in the spawn zone at Batumi. The APC picks up two of the three infantry groups and +-- drives them to Berlin. There, they unboard and walk to the warehouse where they will be added to the stock. +-- Meanwhile the APC drives back and picks up the last infantry group and also brings it to Batumi. +-- The APC will then return to Batumi and be added back to the stock of the Batumi warehouse. +-- The reason that the APC has to drive twice, it that can only up to ten soldiers. +-- +-- -- Start Warehouse at Batumi. +-- warehouse.Batumi:Start() +-- +-- -- Start Warehouse Berlin. +-- warehouse.Berlin:Start() +-- +-- -- Add 20 infantry groups and five APCs as assets at Batumi. +-- warehouse.Batumi:AddAsset("Infantry Platoon Alpha", 20) +-- warehouse.Batumi:AddAsset("TPz Fuchs", 5) +-- +-- -- Warehouse Berlin requests 3 infantry groups from warehouse Batumi using 1 APC for transport. +-- warehouse.Batumi:AddRequest(warehouse.Berlin, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 3, WAREHOUSE.TransportType.APC, 1) +-- +--## Example 5: Transport of Assets by Helicopters +-- +-- Warehouse at FARP Berlin requests 10 infantry groups from Batumi. They shall be transported by one helicopter. +-- Note that the UH-1H Huey in DCS is an attack and not a transport helo. So the warehouse logic would be default also +-- register it as an @{#WAREHOUSE.Attribute.AIR_ATTACKHELICOPTER}. In order to use it as a transport we need to force +-- it to be added as transport helo. +-- +-- -- Start Warehouses. +-- warehouse.Batumi:Start() +-- warehouse.Berlin:Start() +-- +-- -- Add 20 infantry groups as assets at Batumi. +-- warehouse.Batumi:AddAsset("Infantry Platoon Alpha", 20) +-- +-- -- Add five Hueys for transport. Note that the Huey in DCS is an attack and not a transport helo. So we force the attribute! +-- warehouse.Batumi:AddAsset("Huey", 5, WAREHOUSE.Attribute.AIR_TRANSPORTHELO) +-- +-- -- Warehouse Berlin requests 10 infantry groups from warehouse Batumi using one huey for transport. +-- warehouse.Batumi:AddRequest(warehouse.Berlin, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 10, WAREHOUSE.TransportType.HELICOPTER, 1) +-- +--## Example 6: Transport of Assets by Airplanes +-- +-- Kutaisi requests 20 infantry groups from Senaki. These assets will be loaded into one C-130 cargo plane. +-- +-- -- Start Warehouses. +-- warehouse.Senaki:Start() +-- warehouse.Kutaisi:Start() +-- +-- -- Add 20 infantry groups and 5 C-130 transport planes as assets to Senaki warehouse. +-- warehouse.Senaki:AddAsset("Infantry Platoon Alpha", 20) +-- warehouse.Senaki:AddAsset("C-130", 5) +-- +-- -- Warehouse Berlin requests 10 infantry groups from warehouse Batumi using 3 APCs for transport. +-- warehouse.Senaki:AddRequest(warehouse.Kutaisi, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 20, WAREHOUSE.TransportType.AIRPLANE, 1) +-- +-- ## Example 7: Capturing Airbase and Warehouse +-- +-- A red BMP has made it through our defence lines and drives towards our unprotected airbase at Senaki. +-- Once the BMP captures the airbase (DCS S\_EVENT_\BASE_\CAPTURED is evaluated) the warehouse at Senaki lost its air infrastructure and it is not +-- possible any more to spawn airborne units. All requests for airborne units are rejected and not queued in this case. +-- +-- The red BMP then drives further to the warehouse. Once it enters the warehouse zone (500 m radius around the warehouse building), the warehouse is +-- considered to be under attack. This triggers the event **Attacked**. The @{#WAREHOUSE.OnAfterAttacked} function can be used to react to this situation. +-- Here, we only broadcast a distress call and launch a flare. However, it would also be reasonable to spawn all or selected ground troops in order to defend +-- the warehouse. Note, that the warehouse has a self defence option which can be activated via the @{#WAREHOUSE.SetAutoDefenceOn}() function. If activated, +-- *all* ground assets are automatically spawned and assigned to defend the warehouse. Once/if the attack is defeated, these assets go automatically back +-- into the warehouse stock. +-- +-- If the red coalition manages to capture our warehouse, all assets go into their possession. Here, even our airbase has been captured. Therefore, a (self) request +-- to the warehouse will now spawn the F/A-18 fighters as red units. Note, that the request could also some from another red warehouse. In that case, +-- the planes would take off and (if they make it) be added to the red warehouse. So you can steal valuable assets from your enemy if he is not careful. +-- +-- Here, we simply activate a blue external unit which drives to the warehouse, destroyes the red intruder and re-captures our warehouse. +-- +-- -- Start warehouse. +-- warehouse.Senaki:Start() +-- +-- -- Add some assets. +-- warehouse.Senaki:AddAsset("TPz Fuchs", 5) +-- warehouse.Senaki:AddAsset("Infantry Platoon Alpha", 10) +-- warehouse.Senaki:AddAsset("F/A-18C 2ship", 10) +-- +-- -- Auto defence! When enabled, all ground troops of the warehouse are spawned automatically to defend the warehouse. +-- -- warehouse.Senaki:SetAutoDefenceOn() +-- +-- -- Red BMP trying to capture the airfield and later the warehouse. +-- local red1=GROUP:FindByName("Red BMP-80 Senaki") +-- red1:Activate() +-- +-- -- The red BMP first drives to the airbase which gets captured and changes from blue to red. So the warehouse loses its airbase. +-- function warehouse.Senaki:OnAfterAirbaseCaptured(From,Event,To,Coalition) +-- -- This request should not be processed since the warehouse has lost its airbase. In fact it is deleted from the queue. +-- warehouse.Senaki:AddRequest(warehouse.Senaki,WAREHOUSE.Descriptor.CATEGORY, Group.Category.AIRPLANE, 1) +-- end +-- +-- -- Enemy has entered the warehouse zone. This triggers the "Attacked" event. +-- function warehouse.Senaki:OnAfterAttacked(From,Event,To,Coalition,Country) +-- MESSAGE:New(string.format("Warehouse %s: We are under attack!", self.alias), 30):ToCoalition(self.coalition) +-- self.coordinate:SmokeRed() +-- end +-- +-- -- Now the red BMP also captured the warehouse. So the warehouse and the airbase are both red and planes can be spawned again. +-- function warehouse.Senaki:OnAfterCaptured(From,Event,To,Coalition,Country) +-- -- These units will be spawned as red units because the warehouse has just been captured. +-- warehouse.Senaki:AddRequest(warehouse.Senaki,WAREHOUSE.Descriptor.CATEGORY, Group.Category.AIRPLANE, 1) +-- +-- -- Activate Blue Humvee to recapture the warehouse. +-- local blue1=GROUP:FindByName("blue1") +-- blue1:Activate() +-- end +-- +-- ## Example 8: Destroying a Warehouse +-- +-- After 30 seconds into the mission we create and (artificial) big explosion - or a terrorist attack if you like - which completely destroys the +-- the warehouse at Batumi. All assets are gone and requests cannot be processed anymore. +-- +-- -- Start Batumi and Berlin warehouses. +-- warehouse.Batumi:Start() +-- warehouse.Berlin:Start() +-- +-- -- Add some assets. +-- warehouse.Batumi:AddAsset("Huey", 5, WAREHOUSE.Attribute.AIR_TRANSPORTHELO) +-- warehouse.Berlin:AddAsset("Huey", 5, WAREHOUSE.Attribute.AIR_TRANSPORTHELO) +-- +-- -- Big explosion at the warehose. It has a very nice damage model by the way :) +-- local function DestroyWarehouse() +-- warehouse.Batumi.warehouse:GetCoordinate():Explosion(9999) +-- end +-- +-- -- Create and explosion after 30 sec. +-- SCHEDULER:New(nil, DestroyWarehouse, {}, 30) +-- +-- -- These requests should not be processed any more since the warehouse is destroyed. +-- warehouse.Batumi:__AddRequest(35, warehouse.Berlin, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.AIR_TRANSPORTHELO, 1) +-- warehouse.Berlin:__AddRequest(40, warehouse.Batumi, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.AIR_TRANSPORTHELO, 1) +-- +-- ## Example 9: Self Propelled Naval Assets +-- +-- Kobuleti requests a war ship from Batumi. Both warehouses need to have a port, which we define by two polygon zones at a place +-- in the sea closest to the warehouses. Also a shipping lane between the two warehouses needs to be defined manually. +-- With this infrastructure it is possible to exachange naval assets between warehouses. +-- +-- -- Start warehouses. +-- warehouse.Batumi:Start() +-- warehouse.Kobuleti:Start() +-- +-- -- Define ports and shipping lanes. +-- warehouse.Batumi:SetPortZone(ZONE_POLYGON:NewFromGroupName("Warehouse Batumi Port", "Warehouse Batumi Port")) +-- warehouse.Kobuleti:SetPortZone(ZONE_POLYGON:NewFromGroupName("Warehouse Kobuleti Port", "Warehouse Kobuleti Port")) +-- warehouse.Batumi:AddShippingLane(warehouse.Kobuleti, GROUP:FindByName("Warehouse Batumi-Kobuleti Shipping Lane")) +-- +-- -- Add five USS Normandy naval assets. +-- warehouse.Batumi:AddAsset("Normandy", 5) +-- +-- -- Kobuleti requests a war ship from Batumi. +-- warehouse.Batumi:AddRequest(warehouse.Kobuleti, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.NAVAL_WARSHIP) +-- +-- ## Example 10: Aircraft Carrier - Rescue Helo and Escort +-- +-- This example shows how to spawn assets from a warehouse located on an aircraft carrier. +-- +-- After 10 seconds we make a self request for a rescue helicopter. Note, that the @{#WAREHOUSE.AddRequest} function has a parameter which lets you +-- specify an "Assignment". This can be later used to identify the request and take the right actions. +-- +-- Once the request is processed, the @{#WAREHOUSE.OnafterSelfRequest} function is called. This is where we hook in and postprocess the spawned assets. +-- In particular, we use the @{AI.AI_Formation#AI_FORMATION} class to make some nice escorts for our carrier. +-- +-- When the resue helo is spawned, we can check that this is the correct asset and make the helo go into formation with the carrier. +-- Once the helo runs out of fuel, it will automatically return to the ship and land. For the warehouse, this means that the "cargo", i.e. the helicopter +-- has been delivered - assets can be delivered to other warehouses and to the same warehouse - hence a *self* request. +-- When that happens, the **Delivered** event is triggered and the @{#WAREHOUSE.OnAfterDelivered} function called. This can now be used to spawn +-- a fresh helo. Effectively, there we created an infinite, never ending loop. So a rescue helo will be up at all times. +-- +-- After 30 and 45 seconds requests for five groups of armed speedboats are made. These will be spawned in the port zone right behind the carrier. +-- The first five groups will go port of the carrier an form a left wing formation. The seconds groups will to the analogue on the starboard side. +-- +-- -- Start warehouse on USS Stennis. +-- warehouse.Stennis:Start() +-- +-- -- Add speedboat and helo assets. +-- warehouse.Stennis:AddAsset("Speedboat", 10) +-- warehouse.Stennis:AddAsset("CH-53E", 3) +-- +-- -- Define a "port" at the Stennis to be able to spawn Naval assets. This zone will move behind the Stennis. +-- local stenniszone=ZONE_UNIT:New("Spawnzone Stennis", UNIT:FindByName("Stennis"), 100, {rho=250, theta=180, relative_to_unit=true}) +-- warehouse.Stennis:SetPortZone(stenniszone) +-- +-- -- Self request of rescue helo and speed boats. +-- warehouse.Stennis:__AddRequest(10, warehouse.Stennis, WAREHOUSE.Descriptor.TEMPLATENAME, "CH-53E", 1, nil, nil, nil, "Rescue Helo") +-- warehouse.Stennis:__AddRequest(30, warehouse.Stennis, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.NAVAL_ARMEDSHIP, 5, nil, nil, nil, "Speedboats Left") +-- warehouse.Stennis:__AddRequest(45, warehouse.Stennis, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.NAVAL_ARMEDSHIP, 5, nil, nil, nil, "Speedboats Right") +-- +-- --- Function called after self request +-- function warehouse.Stennis:OnAfterSelfRequest(From,Event,To,groupset,request) +-- +-- local groupset=groupset --Core.Set#SET_GROUP +-- local request=request --Functional.Warehouse#WAREHOUSE.Pendingitem +-- +-- local Mother=UNIT:FindByName("Stennis") +-- +-- if request.assignment=="Speedboats Left" then +-- +-- -- Define AI Formation object. +-- -- Note that this has to be a global variable or the garbage collector will remove it for some reason! +-- CarrierFormationLeft = AI_FORMATION:New(Mother, groupset, "Left Formation with Carrier", "Follow Carrier at given parameters.") +-- +-- -- Formation parameters. +-- CarrierFormationLeft:FormationLeftWing(200 ,50, 0, 0, 500, 50) +-- +-- CarrierFormationLeft:__Start(2) +-- +-- for _,group in pairs(groupset:GetSetObjects()) do +-- local group=group --Wrapper.Group#GROUP +-- group:FlareRed() +-- end +-- +-- elseif request.assignment=="Speedboats Right" then +-- +-- -- Define AI Formation object. +-- -- Note that this has to be a global variable or the garbage collector will remove it for some reason! +-- CarrierFormationRight = AI_FORMATION:New(Mother, groupset, "Right Formation with Carrier", "Follow Carrier at given parameters.") +-- +-- -- Formation parameters. +-- CarrierFormationRight:FormationRightWing(200 ,50, 0, 0, 500, 50) +-- +-- CarrierFormationRight:__Start(2) +-- +-- for _,group in pairs(groupset:GetSetObjects()) do +-- local group=group --Wrapper.Group#GROUP +-- group:FlareGreen() +-- end +-- +-- elseif request.assignment=="Rescue Helo" then +-- +-- -- Define AI Formation object. +-- CarrierFormationHelo = AI_FORMATION:New(Mother, groupset, "Helo Formation with Carrier", "Follow Carrier at given parameters.") +-- +-- -- Formation parameters. +-- CarrierFormationHelo:FormationCenterWing(-150, 50, 20, 50, 100, 50) +-- +-- -- Start formation FSM. +-- CarrierFormationHelo:__Start(2) +-- +-- end +-- +-- --- When the helo is out of fuel, it will return to the carrier and should be delivered. +-- function warehouse.Stennis:OnAfterDelivered(From,Event,To,request) +-- local request=request --Functional.Warehouse#WAREHOUSE.Pendingitem +-- +-- -- So we start another request. +-- if request.assignment=="Rescue Helo" then +-- warehouse.Stennis:__AddRequest(10, warehouse.Stennis, WAREHOUSE.Descriptor.TEMPLATENAME, "CH-53E", 1, nil, nil, nil, "Rescue Helo") +-- end +-- end +-- +-- end -- -- -- @field #WAREHOUSE @@ -346,6 +695,7 @@ WAREHOUSE = { stock = {}, queue = {}, pending = {}, + delivered = {}, defending = {}, portzone = nil, shippinglanes = {}, @@ -489,33 +839,32 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.3.4" +WAREHOUSE.version="0.3.4w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: How to get a specific request once the cargo is delivered? Make addrequest addasset non FSM function? Callback for requests like in SPAWN? --- DONE: Add autoselfdefence switch and user function. Default should be off. --- DONE: Warehouse re-capturing not working?! --- DONE: Naval assets dont go back into stock once arrived. --- DONE: Take cargo weight into consideration, when selecting transport assets. -- TODO: Add transport units from dispatchers back to warehouse stock once they completed their mission. --- DONE: Add ports for spawning naval assets. --- TODO: Added habours as interface for transport to from warehouses? --- DONE: Add shipping lanes between warehouses. +-- TODO: Added habours as interface for transport to from warehouses? -- TODO: Set ROE for spawned groups. -- TODO: Add possibility to add active groups. Need to create a pseudo template before destroy. -- TODO: Write documentation. -- TODO: Handle the case when units of a group die during the transfer. Adjust template?! See Grouping in SPAWN. --- DONE: Handle cases with immobile units <== should be handled by dispatcher classes. --- TODO: Handle cargo crates. --- DONE: Handle cases for aircraft carriers and other ships. Place warehouse on carrier possible? On others probably not - exclude them? --- TODO: Add general message function for sending to coaliton or debug. --- TODO: Fine tune event handlers. -- TODO: Add save/load capability of warehouse <==> percistance after mission restart. --- DONE: Improve generalized attributes. -- TODO: Add a time stamp when an asset is added to the stock and for requests. +-- DONE: How to get a specific request once the cargo is delivered? Make addrequest addasset non FSM function? Callback for requests like in SPAWN? +-- DONE: Add autoselfdefence switch and user function. Default should be off. +-- DONE: Warehouse re-capturing not working?! +-- DONE: Naval assets dont go back into stock once arrived. +-- DONE: Take cargo weight into consideration, when selecting transport assets. +-- DONE: Add ports for spawning naval assets. +-- DONE: Add shipping lanes between warehouses. +-- DONE: Handle cases with immobile units <== should be handled by dispatcher classes. +-- DONE: Handle cases for aircraft carriers and other ships. Place warehouse on carrier possible? On others probably not - exclude them? +-- DONE: Add general message function for sending to coaliton or debug. +-- DONE: Fine tune event handlers. +-- DONE: Improve generalized attributes. -- DONE: If warehouse is destoyed, all asssets are gone. -- DONE: Add event handlers. -- DONE: Add AI_CARGO_AIRPLANE @@ -1087,12 +1436,14 @@ function WAREHOUSE:AddShippingLane(remotewarehouse, group) end -- Debug info. Marks along shipping lane. - for i=1,#lane do - local coord=lane[i] --Core.Point#COORDINATE - local text=string.format("Shipping lane %s to %s. Point %d.", self.alias, remotewarehouse.alias, i) - coord:MarkToCoalition(text, self.coalition) + if self.Debug then + for i=1,#lane do + local coord=lane[i] --Core.Point#COORDINATE + local text=string.format("Shipping lane %s to %s. Point %d.", self.alias, remotewarehouse.alias, i) + coord:MarkToCoalition(text, self.coalition) + end end - + -- Add shipping lane. self.shippinglanes[remotewarehouse.warehouse:GetName()]=lane @@ -1306,7 +1657,7 @@ function WAREHOUSE:onafterStart(From, Event, To) self:HandleEvent(EVENTS.EngineShutdown, self._OnEventArrived) -- Start the status monitoring. - self:__Status(1) + self:__Status(-1) end @@ -1328,6 +1679,15 @@ function WAREHOUSE:onafterStop(From, Event, To) self:UnHandleEvent(EVENTS.Dead) self:UnHandleEvent(EVENTS.BaseCaptured) + self.pending=nil + self.pending={} + + self.queue=nil + self.queue={} + + self.stock=nil + self.stock={} + -- Clear all pending schedules. self.CallScheduler:Clear() end @@ -2687,6 +3047,9 @@ function WAREHOUSE:onafterDelivered(From, Event, To, request) -- Make some noise :) self:_Fireworks(request.warehouse.coordinate) + + -- Add table + self.delivered[request.uid]=true -- Remove pending request: self:_DeleteQueueItem(request, self.pending) @@ -2721,7 +3084,19 @@ function WAREHOUSE:onafterSelfRequest(From, Event, To, groupset, request) end -- Add a "defender request" to be able to despawn all assets once defeated. - if self:IsAttacked() then + if self:IsAttacked() then + + -- Route (mobile) ground troops to warehouse zone if they are not alreay there. + if self.autodefence then + for _,_group in pairs(groupset:GetSetObjects()) do + local group=_group --Wrapper.Group#GROUP + local speedmax=group:GetSpeedMax() + if group:IsGround() and speedmax>1 and group:IsNotInZone(self.zone) then + group:RouteGroundTo(self.zone:GetRandomCoordinate(), 0.8*speedmax, "Off Road") + end + end + end + table.insert(self.defending, request) end @@ -4605,7 +4980,7 @@ function WAREHOUSE:_PrintQueue(queue, name) if qitem.airbase then airbasename=qitem.airbase:GetName() end - text=text..string.format("\nUID=%d, Prio=%d, Requestor=%s, Airbase=%s (category=%d), Descriptor: %s=%s, Nasssets=%s, Transport=%s, Ntransport=%d.", + text=text..string.format("\nUID=%d, Prio=%d, Requestor=%s, Airbase=%s (category=%d), Descriptor: %s=%s, #Assets=%s, Transport=%s, #Transport=%d.", qitem.uid, qitem.prio, qitem.warehouse.alias, airbasename, qitem.category, qitem.assetdesc,tostring(qitem.assetdescval), tostring(qitem.nasset), qitem.transporttype, qitem.ntransport) end if #queue==0 then @@ -4648,10 +5023,12 @@ function WAREHOUSE:_GetStockAssetsText(messagetoall) -- Get assets in stock. local _data=self:GetStockInfo(self.stock) + --[[ local function _sort(a,b) return a[1] Date: Wed, 5 Sep 2018 00:20:05 +0200 Subject: [PATCH 310/420] Warehouse v0.3.5 Improved queue output. --- .../Moose/Functional/Warehouse.lua | 205 +++++++++++++----- 1 file changed, 151 insertions(+), 54 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index d4db49502..9d328706b 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -95,6 +95,8 @@ -- in a realistic way by using the corresponding cargo dispatcher classes, i.e. @{AI.AI_Cargo_Dispatcher_APC#AI_DISPATCHER_APC}, -- @{AI.AI_Cargo_Dispatcher_Helicopter#AI_DISPATCHER_HELICOPTER} and @{AI.AI_Cargo_Dispatcher_Airplane#AI_DISPATCHER_AIRPLANE}. -- +-- === +-- -- # Creating a Warehouse -- -- A MOOSE warehouse must be represented in game by a phyical *static* object. For example, the mission editor already has warehouse as static object available. @@ -131,6 +133,7 @@ -- By default, the generalized attribute of the asset is determined automatically from the DCS descriptor attributes. However, this might not always result in the desired outcome. -- Therefore, it is possible, to force a generalized attribute for the asset with the third optional parameter *forceattribute*, which is of type @{#WAREHOUSE.Attribute}. -- +-- === -- -- # Requesting Assets -- @@ -160,7 +163,7 @@ -- -- Also not that the above request is for five infantry units. So any group in stock that has the generalized attribute "INFANTRY" can be selected. -- --- ### Requesting a Specific Unit Type +-- ## Requesting a Specific Unit Type -- -- A more specific request could look like: -- @@ -169,7 +172,7 @@ -- Here, Kobuleti requests a specific unit type, in particular two groups of A-10Cs. Note that the spelling is important as it must exacly be the same as -- what one get's when using the DCS unit type. -- --- ### Requesting a Specifc Group +-- ## Requesting a Specifc Group -- -- An even more specific request would be: -- @@ -177,7 +180,7 @@ -- -- In this case three groups named "Group Name as in ME" are requested. So this explicitly request the groups named like that in the Mission Editor. -- --- ### Requesting a general category +-- ## Requesting a general category -- -- On the other hand, very general unspecifc requests can be made as -- @@ -208,8 +211,8 @@ -- -- @param #WAREHOUSE.Pendingitem request Pending self request. -- function WAREHOUSE:onafterSelfRequest(From, Event, To, groupset, request) -- --- for _,_group in pairs(groupset:GetSetObjects()) do --- local group=_group --Wrapper.Group#GROUP +-- for _,group in pairs(groupset:GetSetObjects()) do +-- local group=group --Wrapper.Group#GROUP -- group:SmokeGreen() -- end -- @@ -220,6 +223,8 @@ -- -- Note that airborne groups are spawned in uncontrolled state and need to be activated first before they can start their assigned mission. -- +-- === +-- -- # Infrastructure -- -- A good infrastructure is important for a warehouse to be efficient. Therefore, the location of a warehouse should be chosen with care. @@ -283,6 +288,30 @@ -- -- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_ShippingLane.png) -- +-- === +-- +-- # Why is my request not processed? +-- +-- For each request, the warehouse class logic does a lot of consistancy and validation checks under the hood. +-- This means that sometimes a request is deemed to be *invalid* in which case they are deleted from the queue or considered to be valid but cannot be executed at this very moment. +-- +-- ## Invalid Requests +-- +-- Invalid request are requests which cannot be processes **ever** because there is some logical or physical argument for it. Or simply because that feature was not implemented (yet). +-- +-- * One warehuse requests airborne assets from another warehouse but at one (or even both) warehouses do not have an associated airbase. +-- +-- All invalid requests are removed from the warehouse queue! +-- +-- ## Temporarily Unprocessable Requests +-- +-- Temporarily unprocessable requests are possible in priciple, but cannot be processed at the given time the warehouse checks its queue. +-- +-- * No enough parking spaces are available for the requests assets but the airbase has enough parking spots in total so that this request is possible once other aircraft have taken off. +-- +-- Temporarily unprocessable requests are held in the queue. If at some point in time, the situation changes so that these requests can be processed, they are executed. +-- +-- === -- -- # Strategic Considerations -- @@ -328,6 +357,32 @@ -- -- # Examples -- +-- This section shows some examples how the WAREHOUSE class is used in practice. This is one of the best ways to explain things, in my opinion. +-- +-- But first, let me introduce a convenient way to define several warehouses in a table. This is absolutely *not* necessary but quite handy if you have +-- multiple WAREHOUSE objects in your mission. +-- +-- ## Example 0: Setting up a Warehouse Array +-- +-- If you have multiple warehouses, you can put them in a table. This makes it easier to access them or to loop over them. +-- +-- -- Define Warehouses. +-- local warehouse={} +-- warehouse.Senaki = WAREHOUSE:New(STATIC:FindByName("Warehouse Senaki"), "Senaki") --Functional.Warehouse#WAREHOUSE +-- warehouse.Batumi = WAREHOUSE:New(STATIC:FindByName("Warehouse Batumi"), "Batumi") --Functional.Warehouse#WAREHOUSE +-- warehouse.Kobuleti = WAREHOUSE:New(STATIC:FindByName("Warehouse Kobuleti"), "Kobuleti") --Functional.Warehouse#WAREHOUSE +-- warehouse.Kutaisi = WAREHOUSE:New(STATIC:FindByName("Warehouse Kutaisi"), "Kutaisi") --Functional.Warehouse#WAREHOUSE +-- warehouse.Berlin = WAREHOUSE:New(STATIC:FindByName("Warehouse Berlin"), "Berlin") --Functional.Warehouse#WAREHOUSE +-- warehouse.London = WAREHOUSE:New(STATIC:FindByName("Warehouse London"), "London") --Functional.Warehouse#WAREHOUSE +-- warehouse.Stennis = WAREHOUSE:New(STATIC:FindByName("Warehouse Stennis"), "Stennis") --Functional.Warehouse#WAREHOUSE +-- +-- Remarks: +-- +-- * I defined the array as local, i.e. local warehouse={}. This is personal preference and sometimes causes trouble with the lua garbage collection. You can also define it as a global array/table! +-- * The "--Functional.Warehouse#WAREHOUSE" at the end is only to have the LDT intellisense working correctly. If you don't use LDT (which you should!), it can be omitted. +-- +-- **NOTE** that all examples below need this bit or code at the beginning - or at least the warehouses which are used. +-- -- ## Example 1: Self Request -- -- Ground troops are taken from the Batumi warehouse stock and spawned in its spawn zone. After a short delay, they are added back to the warehouse stock. @@ -745,11 +800,12 @@ WAREHOUSE = { --- Item of the warehouse pending queue table. -- @type WAREHOUSE.Pendingitem --- @extends #WAREHOUSE.Queueitem +-- @field #number timestamp Absolute mission time in seconds when the request was processed. -- @field Core.Set#SET_GROUP cargogroupset Set of cargo groups do be delivered. -- @field #number ndelivered Number of groups delivered to destination. -- @field Core.Set#SET_GROUP transportgroupset Set of cargo transport groups. -- @field #number ntransporthome Number of transports back home. +-- @extends #WAREHOUSE.Queueitem --- Descriptors enumerator describing the type of the asset. -- @type WAREHOUSE.Descriptor @@ -786,7 +842,7 @@ WAREHOUSE.Descriptor = { -- @field #string NAVAL_ARMEDSHIP Any armed ship that is not an aircraft carrier, a cruiser, destroyer, firgatte or corvette. -- @field #string NAVAL_UNARMEDSHIP Any unarmed naval vessel. -- @field #string NAVAL_OTHER Any naval unit that does not fall into any other naval category. --- @field #string UNKNOWN Anything that does not fall into any other category. +-- @field #string OTHER_UNKNOWN Anything that does not fall into any other category. WAREHOUSE.Attribute = { AIR_TRANSPORTPLANE="Air_TransportPlane", AIR_AWACS="Air_AWACS", @@ -808,16 +864,16 @@ WAREHOUSE.Attribute = { NAVAL_ARMEDSHIP="Naval_ArmedShip", NAVAL_UNARMEDSHIP="Naval_UnarmedShip", NAVAL_OTHER="Naval_OtherNaval", - UNKNOWN="Other_Unknown", + OTHER_UNKNOWN="Other_Unknown", } --- Cargo transport type. Defines how assets are transported to their destination. -- @type WAREHOUSE.TransportType --- @field #string AIRPLANE Transports are conducted by airplanes. --- @field #string HELICOPTER Transports are conducted by helicopters. +-- @field #string AIRPLANE Transports are carried out by airplanes. +-- @field #string HELICOPTER Transports are carried out by helicopters. -- @field #string APC Transports are conducted by APCs. --- @field #string SHIP Transports are conducted by ships. --- @field #string TRAIN Transports are conducted by trains. Not yet implemented. +-- @field #string SHIP Transports are conducted by ships. Not implemented yet. +-- @field #string TRAIN Transports are conducted by trains. Not implemented yet. Also trains are buggy in DCS. -- @field #string SELFPROPELLED Assets go to their destination by themselves. No transport carrier needed. WAREHOUSE.TransportType = { AIRPLANE = "Air_TransportPlane", @@ -839,7 +895,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.3.4w" +WAREHOUSE.version="0.3.5" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -851,7 +907,7 @@ WAREHOUSE.version="0.3.4w" -- TODO: Add possibility to add active groups. Need to create a pseudo template before destroy. -- TODO: Write documentation. -- TODO: Handle the case when units of a group die during the transfer. Adjust template?! See Grouping in SPAWN. --- TODO: Add save/load capability of warehouse <==> percistance after mission restart. +-- TODO: Add save/load capability of warehouse <==> percistance after mission restart. Difficult! -- TODO: Add a time stamp when an asset is added to the stock and for requests. -- DONE: How to get a specific request once the cargo is delivered? Make addrequest addasset non FSM function? Callback for requests like in SPAWN? -- DONE: Add autoselfdefence switch and user function. Default should be off. @@ -1809,8 +1865,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu -- Debug info. self:_DebugMessage(self.wid..string.format("Adding %d NEW assets of group %s to warehouse %s.", n, tostring(group:GetName()), self.alias), 5) - - + -- This is a group that is not in the db yet. Add it n times. local assets=self:_RegisterAsset(group, n, forceattribute) @@ -1862,6 +1917,7 @@ end -- @param #string forceattribute Forced generalized attribute. -- @return #table A table containing all registered assets. function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute) + self:F({groupname=group:GetName(), ngroups=ngroups, forceattribute=forceattribute}) -- Set default. local n=ngroups or 1 @@ -1877,18 +1933,15 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute) return 0,0,0,0 end + -- Get name of template group. local templategroupname=group:GetName() - - local DCSgroup=group:GetDCSObject() - - local DCSunit=DCSgroup:getUnit(1) - local DCSdesc=DCSunit:getDesc() - local DCSdisplay=DCSdesc.displayName - local DCScategory=DCSgroup:getCategory() - local DCStype=DCSunit:getTypeName() + + local Descriptors=group:GetUnit(1):GetDesc() + local Category=group:GetCategory() + local TypeName=group:GetTypeName() local SpeedMax=group:GetSpeedMax() local RangeMin=group:GetRange() - local smax,sx,sy,sz=_GetObjectSize(DCSdesc) + local smax,sx,sy,sz=_GetObjectSize(Descriptors) -- Get weight and cargo bay size in kg. local weight=0 @@ -1915,7 +1968,7 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute) end -- Set/get the generalized attribute. - local attribute=forceattribute or self:_GetAttribute(templategroupname) + local attribute=forceattribute or self:_GetAttribute(group) -- Table for returned assets. local assets={} @@ -1931,14 +1984,14 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute) asset.uid=WAREHOUSE.db.AssetID asset.templatename=templategroupname asset.template=UTILS.DeepCopy(_DATABASE.Templates.Groups[templategroupname].Template) - asset.category=DCScategory - asset.unittype=DCStype + asset.category=Category + asset.unittype=TypeName asset.nunits=#asset.template.units asset.range=RangeMin asset.speedmax=SpeedMax asset.size=smax asset.weight=weight - asset.DCSdesc=DCSdesc + asset.DCSdesc=Descriptors asset.attribute=attribute asset.transporter=false -- not used yet asset.cargobay=cargobay @@ -2424,6 +2477,10 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Pending request. Add cargo groups to request. local Pending=Request --#WAREHOUSE.Pendingitem + -- Set time stamp. + Pending.timestamp=timer.getAbsTime() + env.info("Timestamp="..Pending.timestamp) + -- Spawn assets of this request. local _spawngroups=self:_SpawnAssetRequest(Pending) --Core.Set#SET_GROUP @@ -2963,7 +3020,7 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) local ncargo=request.cargogroupset:Count() -- Debug message. - local text=string.format("Cargo %d of %s arrived at warehouse %s. Assets still to deliver %d.",request.ndelivered, tostring(request.nasset), request.warehouse.alias, ncargo) + local text=string.format("Cargo %d of %s arrived at warehouse %s. Assets still to deliver %d.", request.ndelivered, tostring(request.nasset), request.warehouse.alias, ncargo) self:_DebugMessage(text, 5) -- Route mobile ground group to the warehouse. Group has 60 seconds to get there or it is despawned and added as asset to the new warehouse regardless. @@ -4117,7 +4174,7 @@ end --- Checks if the request can be fulfilled right now. -- Check for current parking situation, number of assets and transports currently in stock. -- @param #WAREHOUSE self --- @param #WAREHOUSE.Pendingitem request The request to be checked. +-- @param #WAREHOUSE.Queueitem request The request to be checked. -- @return #boolean If true, request can be executed. If false, something is not right. function WAREHOUSE:_CheckRequestNow(request) @@ -4714,8 +4771,7 @@ function WAREHOUSE:_GetIDsFromGroup(group) else self:E("WARNING: Group not found in GetIDsFromGroup() function!") end - - + end --- Filter stock assets by table entry. @@ -4781,15 +4837,13 @@ end --- Check if a group has a generalized attribute. -- @param #WAREHOUSE self --- @param #string groupname Name of the group. +-- @param Wrapper.Group#GROUP group MOOSE group object. -- @param #WAREHOUSE.Attribute attribute Attribute to check. -- @return #boolean True if group has the specified attribute. -function WAREHOUSE:_HasAttribute(groupname, attribute) - - local group=GROUP:FindByName(groupname) +function WAREHOUSE:_HasAttribute(group, attribute) if group then - local groupattribute=self:_GetAttribute(groupname) + local groupattribute=self:_GetAttribute(group) return groupattribute==attribute end @@ -4799,13 +4853,12 @@ end --- Get the generalized attribute of a group. -- Note that for a heterogenious group, the attribute is determined from the attribute of the first unit! -- @param #WAREHOUSE self --- @param #string groupname Name of the group. +-- @param Wrapper.Group#GROUP group MOOSE group object. -- @return #WAREHOUSE.Attribute Generalized attribute of the group. -function WAREHOUSE:_GetAttribute(groupname) +function WAREHOUSE:_GetAttribute(group) - local group=GROUP:FindByName(groupname) - - local attribute=WAREHOUSE.Attribute.UNKNOWN --#WAREHOUSE.Attribute + -- Default + local attribute=WAREHOUSE.Attribute.OTHER_UNKNOWN --#WAREHOUSE.Attribute if group then @@ -4880,7 +4933,7 @@ function WAREHOUSE:_GetAttribute(groupname) elseif unarmedship then attribute=WAREHOUSE.Attribute.NAVAL_UNARMEDSHIP else - attribute=WAREHOUSE.Attribute.UNKNOWN + attribute=WAREHOUSE.Attribute.OTHER_UNKNOWN end end @@ -4972,21 +5025,65 @@ end -- @param #table queue Queue to print. -- @param #string name Name of the queue for info reasons. function WAREHOUSE:_PrintQueue(queue, name) - local text=string.format("%s at %s: ",name, self.alias) - for _,_qitem in ipairs(queue) do - local qitem=_qitem --#WAREHOUSE.Queueitem + + local total="Empty" + if #queue>0 then + total=string.format("Total = %d", #queue) + end + + -- Init string. + local text=string.format("%s at %s: %s",name, self.alias, total) + + for i,qitem in ipairs(queue) do + local qitem=qitem --#WAREHOUSE.Pendingitem + -- Set airbase: local airbasename="none" if qitem.airbase then airbasename=qitem.airbase:GetName() - end - text=text..string.format("\nUID=%d, Prio=%d, Requestor=%s, Airbase=%s (category=%d), Descriptor: %s=%s, #Assets=%s, Transport=%s, #Transport=%d.", - qitem.uid, qitem.prio, qitem.warehouse.alias, airbasename, qitem.category, qitem.assetdesc,tostring(qitem.assetdescval), tostring(qitem.nasset), qitem.transporttype, qitem.ntransport) + end + + local uid=qitem.uid + local prio=qitem.prio + local clock="N/A" + if qitem.timestamp then + clock=tostring(UTILS.SecondsToClock(qitem.timestamp)) + end + local requestor=qitem.warehouse.alias + local requestorAirbaseCat=qitem.category + local assetdesc=qitem.assetdesc + local assetdescval=qitem.assetdescval + local nasset=tostring(qitem.nasset) + local ndelivered=tostring(qitem.ndelivered) + local ncargogroupset="N/A" + if qitem.cargogroupset then + ncargogroupset=tostring(qitem.cargogroupset:Count()) + end + local transporttype="N/A" + if qitem.transporttype then + transporttype=qitem.transporttype + end + local ntransport="N/A" + if qitem.ntransport then + ntransport=tostring(qitem.ntransport) + end + local ntransportalive="N/A" + if qitem.transportgroupset then + ntransportalive=tostring(qitem.transportgroupset:Count()) + end + local ntransporthome="N/A" + if qitem.ntransporthome then + ntransporthome=tostring(qitem.ntransporthome) + end + + -- Output text: + text=text..string.format( + "\n%d) UID=%d, Prio=%d, Clock=%s | Requestor=%s [Airbase=%s, category=%d] | Assets(%s)=%s: #requested=%s / #alive=%s / #delivered=%s | Transport=%s: #requested=%d / #alive=%s / #home=%s", + i, uid, prio, clock, requestor, airbasename, requestorAirbaseCat, assetdesc, assetdescval, nasset, ncargogroupset, ndelivered, transporttype, ntransport, ntransportalive, ntransporthome) + end - if #queue==0 then - text=text.."Empty." - end - self:E(self.wid..text) + + self:I(self.wid..text) end --- Display status of warehouse. From 81b0c3a0509ae2aba1c052d363578eaa60ffdd02 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Wed, 5 Sep 2018 06:03:56 +0200 Subject: [PATCH 311/420] Reworking cargo --- Moose Development/Moose/AI/AI_Cargo.lua | 823 ++++++++++++++++++ Moose Development/Moose/AI/AI_Cargo_APC.lua | 227 ++--- .../Moose/AI/AI_Cargo_Airplane.lua | 246 +++--- .../Moose/AI/AI_Cargo_Dispatcher.lua | 540 ++++++++++-- .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 38 +- .../Moose/AI/AI_Cargo_Dispatcher_Airplane.lua | 47 +- .../AI/AI_Cargo_Dispatcher_Helicopter.lua | 47 +- .../Moose/AI/AI_Cargo_Helicopter.lua | 175 ++-- Moose Development/Moose/Cargo/CargoGroup.lua | 4 +- Moose Development/Moose/Cargo/CargoUnit.lua | 7 +- Moose Development/Moose/Core/Set.lua | 33 +- Moose Development/Moose/Core/Zone.lua | 99 +++ 12 files changed, 1874 insertions(+), 412 deletions(-) create mode 100644 Moose Development/Moose/AI/AI_Cargo.lua diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua new file mode 100644 index 000000000..3359a425e --- /dev/null +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -0,0 +1,823 @@ +--- **AI** -- (R2.3) - Models the intelligent transportation of infantry and other cargo. +-- +-- === +-- +-- ### Author: **FlightControl** +-- +-- === +-- +-- @module AI.AI_Cargo +-- @image AI_Cargo_Dispatching.JPG + +--- @type AI_CARGO +-- @extends Core.Fsm#FSM_CONTROLLABLE + + +--- Brings a dynamic cargo handling capability for AI groups. +-- +-- Armoured Personnel Carriers (Carrier), Trucks, Jeeps and other ground based carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. +-- The AI\_CARGO\Carrier module uses the @{Cargo} capabilities within the MOOSE framework. +-- CARGO derived objects must be declared within the mission to make the AI\_CARGO\Carrier object recognize the cargo. +-- Please consult the @{Cargo} module for more information. +-- +-- ## Cargo loading. +-- +-- The module will load automatically cargo when the Carriers are within boarding or loading range. +-- The boarding or loading range is specified when the cargo is created in the simulation, and therefore, this range depends on the type of cargo +-- and the specified boarding range. +-- +-- ## Enemies nearby. +-- +-- When the Carriers are approaching enemy units, something special is happening. +-- The Carriers will stop moving, and the loaded infantry will unboard and follow the Carriers and will help to defend the group. +-- The carrier will hold the route once the unboarded infantry is further than 50 meters from the Carriers, +-- to ensure that the Carriers are not too far away from the following running infantry. +-- Once all enemies are cleared, the infantry will board again automatically into the Carriers. Once boarded, the Carriers will follow its pre-defined route. +-- +-- A combat range needs to be specified in meters at the @{#AI_CARGO.New}() method. +-- This combat range will trigger the unboarding of troops when enemies are within the combat range around the Carriers. +-- During my tests, I've noticed that there is a balance between ensuring that the infantry is within sufficient hit range (effectiveness) versus +-- vulnerability of the infantry. It all depends on the kind of enemies that are expected to be encountered. +-- A combat range of 350 meters to 500 meters has been proven to be the most effective and efficient. +-- +-- ## Infantry health. +-- +-- When infantry is unboarded from the Carriers, the infantry is actually respawned into the battlefield. +-- As a result, the unboarding infantry is very _healthy_ every time it unboards. +-- This is due to the limitation of the DCS simulator, which is not able to specify the health of new spawned units as a parameter. +-- However, infantry that was destroyed when unboarded and following the Carriers, won't be respawned again. Destroyed is destroyed. +-- As a result, there is some additional strength that is gained when an unboarding action happens, but in terms of simulation balance this has +-- marginal impact on the overall battlefield simulation. Fortunately, the firing strength of infantry is limited, and thus, respacing healthy infantry every +-- time is not so much of an issue ... +-- +-- ## Control the Carriers on the map. +-- +-- It is possible also as a human ground commander to influence the path of the Carriers, by pointing a new path using the DCS user interface on the map. +-- In this case, the Carriers will change the direction towards its new indicated route. However, there is a catch! +-- Once the Carriers are near the enemy, and infantry is unboarded, the Carriers won't be able to hold the route until the infantry could catch up. +-- The Carriers will simply drive on and won't stop! This is a limitation in ED that prevents user actions being controlled by the scripting engine. +-- No workaround is possible on this. +-- +-- ## Cargo deployment. +-- +-- Using the @{#AI_CARGO.Deploy}() method, you are able to direct the Carriers towards a point on the battlefield to unboard/unload the cargo at the specific coordinate. +-- The Carriers will follow nearby roads as much as possible, to ensure fast and clean cargo transportation between the objects and villages in the simulation environment. +-- +-- ## Cargo pickup. +-- +-- Using the @{#AI_CARGO.Pickup}() method, you are able to direct the Carriers towards a point on the battlefield to board/load the cargo at the specific coordinate. +-- The Carriers will follow nearby roads as much as possible, to ensure fast and clean cargo transportation between the objects and villages in the simulation environment. +-- +-- +-- +-- @field #AI_CARGO +AI_CARGO = { + ClassName = "AI_CARGO", + Coordinate = nil, -- Core.Point#COORDINATE, + Carrier_Cargo = {}, +} + +--- Creates a new AI_CARGO object. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param Core.Set#SET_CARGO CargoSet +-- @param #number CombatRadius +-- @return #AI_CARGO +function AI_CARGO:New( Carrier, CargoSet, CombatRadius ) + + local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_CARGO + + self.CargoSet = CargoSet -- Core.Set#SET_CARGO + self.CombatRadius = CombatRadius + + self:SetStartState( "Unloaded" ) + + self:AddTransition( "Unloaded", "Pickup", "*" ) + self:AddTransition( "Loaded", "Deploy", "*" ) + + self:AddTransition( "*", "Load", "Boarding" ) + self:AddTransition( { "Boarding", "Loaded" }, "Board", "Boarding" ) + self:AddTransition( "Boarding", "Loaded", "Boarding" ) + self:AddTransition( "Boarding", "PickedUp", "Loaded" ) + + self:AddTransition( "Loaded", "Unload", "Unboarding" ) + self:AddTransition( "Unboarding", "Unboard", "Unboarding" ) + self:AddTransition( "Unboarding", "Unloaded", "Unboarding" ) + self:AddTransition( "Unboarding", "Deployed", "Unloaded" ) + + self:AddTransition( "*", "Monitor", "*" ) + self:AddTransition( "*", "Follow", "Following" ) + self:AddTransition( "*", "Guard", "Unloaded" ) + self:AddTransition( "*", "Home", "*" ) + + self:AddTransition( "*", "Destroyed", "Destroyed" ) + + + --- Pickup Handler OnBefore for AI_CARGO + -- @function [parent=#AI_CARGO] OnBeforePickup + -- @param #AI_CARGO self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. + -- @return #boolean + + --- Pickup Handler OnAfter for AI_CARGO + -- @function [parent=#AI_CARGO] OnAfterPickup + -- @param #AI_CARGO self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. + + --- Pickup Trigger for AI_CARGO + -- @function [parent=#AI_CARGO] Pickup + -- @param #AI_CARGO self + -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. + + --- Pickup Asynchronous Trigger for AI_CARGO + -- @function [parent=#AI_CARGO] __Pickup + -- @param #AI_CARGO self + -- @param #number Delay + -- @param Core.Point#COORDINATE Coordinate Pickup place. If not given, loading starts at the current location. + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. + + --- Deploy Handler OnBefore for AI_CARGO + -- @function [parent=#AI_CARGO] OnBeforeDeploy + -- @param #AI_CARGO self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. + -- @return #boolean + + --- Deploy Handler OnAfter for AI_CARGO + -- @function [parent=#AI_CARGO] OnAfterDeploy + -- @param #AI_CARGO self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. + + --- Deploy Trigger for AI_CARGO + -- @function [parent=#AI_CARGO] Deploy + -- @param #AI_CARGO self + -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. + + --- Deploy Asynchronous Trigger for AI_CARGO + -- @function [parent=#AI_CARGO] __Deploy + -- @param #AI_CARGO self + -- @param #number Delay + -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. + + + --- Loaded Handler OnAfter for AI_CARGO + -- @function [parent=#AI_CARGO] OnAfterLoaded + -- @param #AI_CARGO self + -- @param Wrapper.Group#GROUP Carrier + -- @param #string From + -- @param #string Event + -- @param #string To + + --- Unloaded Handler OnAfter for AI_CARGO + -- @function [parent=#AI_CARGO] OnAfterUnloaded + -- @param #AI_CARGO self + -- @param Wrapper.Group#GROUP Carrier + -- @param #string From + -- @param #string Event + -- @param #string To + + + self:__Monitor( 1 ) + + self:SetCarrier( Carrier ) + + for _, CarrierUnit in pairs( Carrier:GetUnits() ) do + CarrierUnit:SetCargoBayWeightLimit() + end + + self.Transporting = false + self.Relocating = false + + return self +end + + +--- Set the Carrier. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP CargoCarrier +-- @return #AI_CARGO +function AI_CARGO:SetCarrier( CargoCarrier ) + + self.CargoCarrier = CargoCarrier -- Wrapper.Group#GROUP + self.CargoCarrier:SetState( self.CargoCarrier, "AI_CARGO", self ) + + CargoCarrier:HandleEvent( EVENTS.Dead ) + CargoCarrier:HandleEvent( EVENTS.Hit ) + + function CargoCarrier:OnEventDead( EventData ) + self:F({"dead"}) + local AICargoTroops = self:GetState( self, "AI_CARGO" ) + self:F({AICargoTroops=AICargoTroops}) + if AICargoTroops then + self:F({}) + if not AICargoTroops:Is( "Loaded" ) then + -- There are enemies within combat range. Unload the CargoCarrier. + AICargoTroops:Destroyed() + end + end + end + + function CargoCarrier:OnEventHit( EventData ) + self:F({"hit"}) + local AICargoTroops = self:GetState( self, "AI_CARGO" ) + if AICargoTroops then + self:F( { OnHitLoaded = AICargoTroops:Is( "Loaded" ) } ) + if AICargoTroops:Is( "Loaded" ) or AICargoTroops:Is( "Boarding" ) then + -- There are enemies within combat range. Unload the CargoCarrier. + AICargoTroops:Unload( false ) + end + end + end + + self.Zone = ZONE_UNIT:New( self.CargoCarrier:GetName() .. "-Zone", self.CargoCarrier, self.CombatRadius ) + self.Coalition = self.CargoCarrier:GetCoalition() + + self:SetControllable( CargoCarrier ) + + self:Guard() + + return self +end + + +function AI_CARGO:IsTransporting() + + return self.Transporting == true +end + +function AI_CARGO:IsRelocating() + + return self.Relocating == true +end + +--- Find a free Carrier within a range. +-- @param #AI_CARGO self +-- @param Core.Point#COORDINATE Coordinate +-- @param #number Radius +-- @return Wrapper.Group#GROUP NewCarrier +function AI_CARGO:FindCarrier( Coordinate, Radius ) + + local CoordinateZone = ZONE_RADIUS:New( "Zone" , Coordinate:GetVec2(), Radius ) + CoordinateZone:Scan( { Object.Category.UNIT } ) + for _, DCSUnit in pairs( CoordinateZone:GetScannedUnits() ) do + local NearUnit = UNIT:Find( DCSUnit ) + self:F({NearUnit=NearUnit}) + if not NearUnit:GetState( NearUnit, "AI_CARGO" ) then + local Attributes = NearUnit:GetDesc() + self:F({Desc=Attributes}) + if NearUnit:HasAttribute( "Trucks" ) then + return NearUnit:GetGroup() + end + end + end + + return nil + +end + + + +--- Follow Infantry to the Carrier. +-- @param #AI_CARGO self +-- @param #AI_CARGO Me +-- @param Wrapper.Unit#UNIT CarrierUnit +-- @param Cargo.CargoGroup#CARGO_GROUP Cargo +-- @return #AI_CARGO +function AI_CARGO:FollowToCarrier( Me, CarrierUnit, CargoGroup ) + + local InfantryGroup = CargoGroup:GetGroup() + + self:F( { self = self:GetClassNameAndID(), InfantryGroup = InfantryGroup:GetName() } ) + + --if self:Is( "Following" ) then + + if CarrierUnit:IsAlive() then + -- We check if the Cargo is near to the CargoCarrier. + if InfantryGroup:IsPartlyInZone( ZONE_UNIT:New( "Radius", CarrierUnit, 25 ) ) then + + -- The Cargo does not need to follow the Carrier. + Me:Guard() + + else + + self:F( { InfantryGroup = InfantryGroup:GetName() } ) + + if InfantryGroup:IsAlive() then + + self:F( { InfantryGroup = InfantryGroup:GetName() } ) + + local Waypoints = {} + + -- Calculate the new Route. + local FromCoord = InfantryGroup:GetCoordinate() + local FromGround = FromCoord:WaypointGround( 10, "Diamond" ) + self:F({FromGround=FromGround}) + table.insert( Waypoints, FromGround ) + + local ToCoord = CarrierUnit:GetCoordinate():GetRandomCoordinateInRadius( 10, 5 ) + local ToGround = ToCoord:WaypointGround( 10, "Diamond" ) + self:F({ToGround=ToGround}) + table.insert( Waypoints, ToGround ) + + local TaskRoute = InfantryGroup:TaskFunction( "AI_CARGO.FollowToCarrier", Me, CarrierUnit, CargoGroup ) + + self:F({Waypoints = Waypoints}) + local Waypoint = Waypoints[#Waypoints] + InfantryGroup:SetTaskWaypoint( Waypoint, TaskRoute ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone. + + InfantryGroup:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. + end + end + end +end + + +--- On after Monitor event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function AI_CARGO:onafterMonitor( Carrier, From, Event, To ) + self:F( { Carrier, From, Event, To } ) + + if Carrier and Carrier:IsAlive() then + if self.CarrierCoordinate then + if self:IsRelocating() == true then + local Coordinate = Carrier:GetCoordinate() + self.Zone:Scan( { Object.Category.UNIT } ) + if self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then + if self:Is( "Unloaded" ) or self:Is( "Following" ) then + -- There are no enemies within combat range. Load the CargoCarrier. + self:Load() + end + else + if self:Is( "Loaded" ) then + -- There are enemies within combat range. Unload the CargoCarrier. + self:__Unload( 1 ) + else + if self:Is( "Unloaded" ) then + self:Follow() + end + if self:Is( "Following" ) then + for Cargo, CarrierUnit in pairs( self.Carrier_Cargo ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + if Cargo:IsAlive() then + if not Cargo:IsNear( CarrierUnit, 40 ) then + CarrierUnit:RouteStop() + self.CarrierStopped = true + else + if self.CarrierStopped then + if Cargo:IsNear( CarrierUnit, 25 ) then + CarrierUnit:RouteResume() + self.CarrierStopped = nil + end + end + end + end + end + end + end + end + end + + end + self.CarrierCoordinate = Carrier:GetCoordinate() + end + + self:__Monitor( -5 ) + +end + + +--- On before Load event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO:onbeforeLoad( Carrier, From, Event, To, PickupZone ) + self:F( { Carrier, From, Event, To } ) + + local Boarding = false + + local LoadInterval = 10 + local LoadDelay = 10 + local Carrier_List = {} + local Carrier_Weight = {} + + if Carrier and Carrier:IsAlive() then + self.Carrier_Cargo = {} + for _, CarrierUnit in pairs( Carrier:GetUnits() ) do + local CarrierUnit = CarrierUnit -- Wrapper.Unit#UNIT + + local CargoBayFreeWeight = CarrierUnit:GetCargoBayFreeWeight() + self:F({CargoBayFreeWeight=CargoBayFreeWeight}) + + Carrier_List[#Carrier_List+1] = CarrierUnit + Carrier_Weight[CarrierUnit] = CargoBayFreeWeight + end + + local Carrier_Count = #Carrier_List + local Carrier_Index = 1 + + for _, Cargo in UTILS.spairs( self.CargoSet:GetSet(), function( t, a, b ) return t[a]:GetWeight() > t[b]:GetWeight() end ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + + self:F( { IsUnLoaded = Cargo:IsUnLoaded(), IsDeployed = Cargo:IsDeployed(), Cargo:GetName(), Carrier:GetName() } ) + + local Loaded = false + + -- Try all Carriers, but start from the one according the Carrier_Index + for Carrier_Loop = 1, #Carrier_List do + + local CarrierUnit = Carrier_List[Carrier_Index] -- Wrapper.Unit#UNIT + + -- This counters loop through the available Carriers. + Carrier_Index = Carrier_Index + 1 + if Carrier_Index > Carrier_Count then + Carrier_Index = 1 + end + + if Cargo:IsUnLoaded() then -- and not Cargo:IsDeployed() then + if Cargo:IsInLoadRadius( CarrierUnit:GetCoordinate() ) then + self:F( { "In radius", CarrierUnit:GetName() } ) + + local CargoWeight = Cargo:GetWeight() + + -- Only when there is space within the bay to load the next cargo item! + if Carrier_Weight[CarrierUnit] > CargoWeight then --and CargoBayFreeVolume > CargoVolume then + Carrier:RouteStop() + --Cargo:Ungroup() + Cargo:__Board( LoadDelay, CarrierUnit, 25 ) + LoadDelay = LoadDelay + LoadInterval + self:__Board( LoadDelay, Cargo, CarrierUnit, PickupZone ) + + -- So now this CarrierUnit has Cargo that is being loaded. + -- This will be used further in the logic to follow and to check cargo status. + self.Carrier_Cargo[Cargo] = CarrierUnit + Boarding = true + Carrier_Weight[CarrierUnit] = Carrier_Weight[CarrierUnit] - CargoWeight + Loaded = true + + -- Ok, we loaded a cargo, now we can stop the loop. + break + end + end + end + + end + + if not Loaded then + -- If the cargo wasn't loaded in one of the carriers, then we need to stop the loading. + break + end + + end + end + + return Boarding + +end + +--- On after Board event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Cargo.Cargo#CARGO Cargo Cargo object. +-- @param Wrapper.Unit#UNIT CarrierUnit +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO:onafterBoard( Carrier, From, Event, To, Cargo, CarrierUnit, PickupZone ) + self:F( { Carrier, From, Event, To, Cargo, CarrierUnit:GetName() } ) + + if Carrier and Carrier:IsAlive() then + self:F({ IsLoaded = Cargo:IsLoaded(), Cargo:GetName(), Carrier:GetName() } ) + if not Cargo:IsLoaded() then + self:__Board( 10, Cargo, CarrierUnit, PickupZone ) + return + end + end + + self:__Loaded( 10, Cargo, CarrierUnit, PickupZone ) + +end + +--- On after Loaded event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @return #boolean Cargo loaded. +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO:onafterLoaded( Carrier, From, Event, To, Cargo, PickupZone ) + self:F( { Carrier, From, Event, To } ) + + local Loaded = true + + if Carrier and Carrier:IsAlive() then + for Cargo, CarrierUnit in pairs( self.Carrier_Cargo ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + self:F( { IsLoaded = Cargo:IsLoaded(), IsDestroyed = Cargo:IsDestroyed(), Cargo:GetName(), Carrier:GetName() } ) + if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then + Loaded = false + end + end + end + + if Loaded then + self:PickedUp( PickupZone ) + end + +end + +--- On after PickedUp event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO:onafterPickedUp( Carrier, From, Event, To, PickupZone ) + self:F( { Carrier, From, Event, To } ) + + self.Transporting = true + Carrier:RouteResume() + +end + + + + +--- On after Unload event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +function AI_CARGO:onafterUnload( Carrier, From, Event, To, DeployZone ) + self:F( { Carrier, From, Event, To, DeployZone } ) + + local UnboardInterval = 10 + local UnboardDelay = 10 + + if Carrier and Carrier:IsAlive() then + for _, CarrierUnit in pairs( Carrier:GetUnits() ) do + local CarrierUnit = CarrierUnit -- Wrapper.Unit#UNIT + Carrier:RouteStop() + for _, Cargo in pairs( CarrierUnit:GetCargo() ) do + if Cargo:IsLoaded() then + Cargo:__UnBoard( UnboardDelay ) + UnboardDelay = UnboardDelay + UnboardInterval + Cargo:SetDeployed( true ) + self:__Unboard( UnboardDelay, Cargo, CarrierUnit, DeployZone ) + end + end + end + end + +end + +--- On after Unboard event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #string Cargo.Cargo#CARGO Cargo Cargo object. +-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +function AI_CARGO:onafterUnboard( Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone ) + self:F( { Carrier, From, Event, To, Cargo:GetName() } ) + + if Carrier and Carrier:IsAlive() then + if not Cargo:IsUnLoaded() then + self:__Unboard( 10, Cargo, CarrierUnit, DeployZone ) + return + end + end + + self:Unloaded( Cargo, CarrierUnit, DeployZone ) + +end + +--- On after Unloaded event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #string Cargo.Cargo#CARGO Cargo Cargo object. +-- @param #boolean Deployed Cargo is deployed. +-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +function AI_CARGO:onafterUnloaded( Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone ) + self:F( { Carrier, From, Event, To, Cargo:GetName(), DeployZone = DeployZone } ) + + local AllUnloaded = true + + --Cargo:Regroup() + + if Carrier and Carrier:IsAlive() then + for _, CarrierUnit in pairs( Carrier:GetUnits() ) do + local CarrierUnit = CarrierUnit -- Wrapper.Unit#UNIT + local IsEmpty = CarrierUnit:IsCargoEmpty() + self:I({ IsEmpty = IsEmpty }) + if not IsEmpty then + AllUnloaded = false + break + end + end + + if AllUnloaded == true then + if DeployZone == true then + self.Carrier_Cargo = {} + end + self.CargoCarrier = Carrier + end + end + + if AllUnloaded == true then + self:Deployed( DeployZone ) + end + +end + +--- On after Deployed event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +function AI_CARGO:onafterDeployed( Carrier, From, Event, To, DeployZone ) + self:F( { Carrier, From, Event, To, DeployZone = DeployZone } ) + + self.Transporting = false + self:__Guard( 0.1 ) + +end + +--- On after Follow event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +function AI_CARGO:onafterFollow( Carrier, From, Event, To ) + self:F( { Carrier, From, Event, To } ) + + self:F( "Follow" ) + if Carrier and Carrier:IsAlive() then + for Cargo, CarrierUnit in pairs( self.Carrier_Cargo ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + if Cargo:IsUnLoaded() then + self:FollowToCarrier( self, CarrierUnit, Cargo ) + CarrierUnit:RouteResume() + end + end + end + +end + + +--- @param #AI_CARGO +-- @param Wrapper.Group#GROUP Carrier +function AI_CARGO._Pickup( Carrier, self, PickupZone ) + + Carrier:F( { "AI_CARGO._Pickup:", Carrier:GetName() } ) + + if Carrier:IsAlive() then + self:Load( PickupZone) + end +end + + +function AI_CARGO._Deploy( Carrier, self, Coordinate, DeployZone ) + + Carrier:F( { "AI_CARGO._Deploy:", Carrier } ) + + if Carrier:IsAlive() then + self:Unload( DeployZone ) + end +end + + + +--- On after Pickup event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param From +-- @param Event +-- @param To +-- @param Core.Point#COORDINATE Coordinate of the pickup point. +-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO:onafterPickup( Carrier, From, Event, To, Coordinate, Speed, PickupZone ) + + if Carrier and Carrier:IsAlive() then + + if Coordinate then + self.RoutePickup = true + + local _speed=Speed or Carrier:GetSpeedMax()*0.5 + + local Waypoints = Carrier:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true ) + + local TaskFunction = Carrier:TaskFunction( "AI_CARGO._Pickup", self, PickupZone ) + + self:F({Waypoints = Waypoints}) + local Waypoint = Waypoints[#Waypoints] + Carrier:SetTaskWaypoint( Waypoint, TaskFunction ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone. + + Carrier:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. + else + AI_CARGO._Pickup( Carrier, self, PickupZone ) + end + + self.Relocating = true + self.Transporting = false + end + +end + + +--- On after Deploy event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param From +-- @param Event +-- @param To +-- @param Core.Point#COORDINATE Coordinate Deploy place. +-- @param #number Speed Speed in km/h to drive to the depoly coordinate. Default is 50% of max possible speed the unit can go. +function AI_CARGO:onafterDeploy( Carrier, From, Event, To, Coordinate, Speed, DeployZone ) + + if Carrier and Carrier:IsAlive() then + + self.RouteDeploy = true + + local _speed=Speed or Carrier:GetSpeedMax()*0.5 + + local Waypoints = Carrier:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true ) + + local TaskFunction = Carrier:TaskFunction( "AI_CARGO._Deploy", self, Coordinate, DeployZone ) + + self:F({Waypoints = Waypoints}) + local Waypoint = Waypoints[#Waypoints] + Carrier:SetTaskWaypoint( Waypoint, TaskFunction ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone. + + Carrier:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. + + self.Relocating = false + self.Transporting = true + end + +end + + +--- On after Home event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param From +-- @param Event +-- @param To +-- @param Core.Point#COORDINATE Coordinate Home place. +-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. +function AI_CARGO:onafterHome( Carrier, From, Event, To, Coordinate, Speed ) + + if Carrier and Carrier:IsAlive() ~= nil then + + self.RouteHome = true + + local _speed=Speed or Carrier:GetSpeedMax()*0.5 + + local Waypoints = Carrier:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true ) + + self:F({Waypoints = Waypoints}) + local Waypoint = Waypoints[#Waypoints] + + Carrier:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. + + end + +end diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index c56ee235d..ae3de6f15 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -90,17 +90,20 @@ function AI_CARGO_APC:New( APC, CargoSet, CombatRadius ) self.CargoSet = CargoSet -- Core.Set#SET_CARGO self.CombatRadius = CombatRadius - self:SetStartState( "Unloaded" ) + self:SetStartState( "Unloaded" ) self:AddTransition( "Unloaded", "Pickup", "*" ) self:AddTransition( "Loaded", "Deploy", "*" ) self:AddTransition( "*", "Load", "Boarding" ) self:AddTransition( { "Boarding", "Loaded" }, "Board", "Boarding" ) - self:AddTransition( "Boarding", "Loaded", "Loaded" ) + self:AddTransition( "Boarding", "Loaded", "Boarding" ) + self:AddTransition( "Boarding", "PickedUp", "Loaded" ) + self:AddTransition( "Loaded", "Unload", "Unboarding" ) self:AddTransition( "Unboarding", "Unboard", "Unboarding" ) - self:AddTransition( { "Unboarding", "Unloaded" }, "Unloaded", "Unloaded" ) + self:AddTransition( "Unboarding", "Unloaded", "Unboarding" ) + self:AddTransition( "Unboarding", "Deployed", "Unloaded" ) self:AddTransition( "*", "Monitor", "*" ) self:AddTransition( "*", "Follow", "Following" ) @@ -369,13 +372,13 @@ function AI_CARGO_APC:onafterMonitor( APC, From, Event, To ) else if self:Is( "Loaded" ) then -- There are enemies within combat range. Unload the CargoCarrier. - self:__Unload( 1, false ) + self:__Unload( 1 ) else if self:Is( "Unloaded" ) then self:Follow() end if self:Is( "Following" ) then - for APCUnit, Cargo in pairs( self.APC_Cargo ) do + for Cargo, APCUnit in pairs( self.APC_Cargo ) do local Cargo = Cargo -- Cargo.Cargo#CARGO if Cargo:IsAlive() then if not Cargo:IsNear( APCUnit, 40 ) then @@ -411,11 +414,17 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To ) +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To, PickupZone ) self:F( { APC, From, Event, To } ) local Boarding = false + local LoadInterval = 10 + local LoadDelay = 10 + local APC_List = {} + local APC_Weight = {} + if APC and APC:IsAlive() then self.APC_Cargo = {} for _, APCUnit in pairs( APC:GetUnits() ) do @@ -424,32 +433,65 @@ function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To ) local CargoBayFreeWeight = APCUnit:GetCargoBayFreeWeight() self:F({CargoBayFreeWeight=CargoBayFreeWeight}) - --for _, Cargo in pairs( self.CargoSet:GetSet() ) do - for _, Cargo in UTILS.spairs( self.CargoSet:GetSet(), function( t, a, b ) return t[a]:GetWeight() > t[b]:GetWeight() end ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - self:F( { IsUnLoaded = Cargo:IsUnLoaded(), IsDeployed = Cargo:IsDeployed(), Cargo:GetName(), APC:GetName() } ) + APC_List[#APC_List+1] = APCUnit + APC_Weight[APCUnit] = CargoBayFreeWeight + end + + local APC_Count = #APC_List + local APC_Index = 1 + + for _, Cargo in UTILS.spairs( self.CargoSet:GetSet(), function( t, a, b ) return t[a]:GetWeight() > t[b]:GetWeight() end ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + + self:F( { IsUnLoaded = Cargo:IsUnLoaded(), IsDeployed = Cargo:IsDeployed(), Cargo:GetName(), APC:GetName() } ) + + local Loaded = false + + -- Try all APCs, but start from the one according the APC_Index + for APC_Loop = 1, #APC_List do + + local APCUnit = APC_List[APC_Index] -- Wrapper.Unit#UNIT + + -- This counters loop through the available APCs. + APC_Index = APC_Index + 1 + if APC_Index > APC_Count then + APC_Index = 1 + end + if Cargo:IsUnLoaded() then -- and not Cargo:IsDeployed() then if Cargo:IsInLoadRadius( APCUnit:GetCoordinate() ) then self:F( { "In radius", APCUnit:GetName() } ) local CargoWeight = Cargo:GetWeight() - + -- Only when there is space within the bay to load the next cargo item! - if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then + if APC_Weight[APCUnit] > CargoWeight then --and CargoBayFreeVolume > CargoVolume then APC:RouteStop() --Cargo:Ungroup() - Cargo:Board( APCUnit, 25 ) - self:__Board( 1, Cargo, APCUnit ) + Cargo:__Board( LoadDelay, APCUnit, 25 ) + LoadDelay = LoadDelay + LoadInterval + self:__Board( LoadDelay, Cargo, APCUnit, PickupZone ) -- So now this APCUnit has Cargo that is being loaded. -- This will be used further in the logic to follow and to check cargo status. - self.APC_Cargo[APCUnit] = Cargo + self.APC_Cargo[Cargo] = APCUnit Boarding = true + APC_Weight[APCUnit] = APC_Weight[APCUnit] - CargoWeight + Loaded = true + + -- Ok, we loaded a cargo, now we can stop the loop. break end end end + end + + if not Loaded then + -- If the cargo wasn't loaded in one of the carriers, then we need to stop the loading. + break + end + end end @@ -465,54 +507,37 @@ end -- @param #string To To state. -- @param Cargo.Cargo#CARGO Cargo Cargo object. -- @param Wrapper.Unit#UNIT APCUnit -function AI_CARGO_APC:onafterBoard( APC, From, Event, To, Cargo, APCUnit ) +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO_APC:onafterBoard( APC, From, Event, To, Cargo, APCUnit, PickupZone ) self:F( { APC, From, Event, To, Cargo, APCUnit:GetName() } ) if APC and APC:IsAlive() then self:F({ IsLoaded = Cargo:IsLoaded(), Cargo:GetName(), APC:GetName() } ) if not Cargo:IsLoaded() then - self:__Board( 10, Cargo, APCUnit ) - else - local CargoBayFreeWeight = APCUnit:GetCargoBayFreeWeight() - self:F({CargoBayFreeWeight=CargoBayFreeWeight}) - for _, Cargo in UTILS.spairs( self.CargoSet:GetSet(), function( t, a, b ) return t[a]:GetWeight() > t[b]:GetWeight() end ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - if Cargo:IsUnLoaded() then - if Cargo:IsInLoadRadius( APCUnit:GetCoordinate() ) then - local CargoWeight = Cargo:GetWeight() - - -- Only when there is space within the bay to load the next cargo item! - if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then - Cargo:Board( APCUnit, 25 ) - self:__Board( 10, Cargo, APCUnit ) - -- So now this APCUnit has Cargo that is being loaded. - -- This will be used further in the logic to follow and to check cargo status. - self.APC_Cargo[APCUnit] = Cargo - return - end - end - end - end - self:__Loaded( 5, Cargo ) + self:__Board( 10, Cargo, APCUnit, PickupZone ) + return end end + + self:__Loaded( 10, Cargo, APCUnit, PickupZone ) end ---- On before Loaded event. +--- On after Loaded event. -- @param #AI_CARGO_APC self -- @param Wrapper.Group#GROUP APC -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @return #boolean Cargo loaded. -function AI_CARGO_APC:onbeforeLoaded( APC, From, Event, To, Cargo ) +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO_APC:onafterLoaded( APC, From, Event, To, Cargo, PickupZone ) self:F( { APC, From, Event, To } ) local Loaded = true if APC and APC:IsAlive() then - for APCUnit, Cargo in pairs( self.APC_Cargo ) do + for Cargo, APCUnit in pairs( self.APC_Cargo ) do local Cargo = Cargo -- Cargo.Cargo#CARGO self:F( { IsLoaded = Cargo:IsLoaded(), IsDestroyed = Cargo:IsDestroyed(), Cargo:GetName(), APC:GetName() } ) if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then @@ -521,14 +546,26 @@ function AI_CARGO_APC:onbeforeLoaded( APC, From, Event, To, Cargo ) end end - if Loaded == true then - APC:RouteResume() + if Loaded then + self:PickedUp( PickupZone ) end - return Loaded - end +--- On after PickedUp event. +-- @param #AI_CARGO_APC self +-- @param Wrapper.Group#GROUP APC +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO_APC:onafterPickedUp( APC, From, Event, To, PickupZone ) + self:F( { APC, From, Event, To } ) + + self.Transporting = true + APC:RouteResume() + +end @@ -539,9 +576,12 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param #boolean Deployed Cargo is deployed. -function AI_CARGO_APC:onafterUnload( APC, From, Event, To, Deployed ) - self:F( { APC, From, Event, To, Deployed } ) +-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +function AI_CARGO_APC:onafterUnload( APC, From, Event, To, DeployZone ) + self:F( { APC, From, Event, To, DeployZone } ) + + local UnboardInterval = 10 + local UnboardDelay = 10 if APC and APC:IsAlive() then for _, APCUnit in pairs( APC:GetUnits() ) do @@ -549,9 +589,10 @@ function AI_CARGO_APC:onafterUnload( APC, From, Event, To, Deployed ) APC:RouteStop() for _, Cargo in pairs( APCUnit:GetCargo() ) do if Cargo:IsLoaded() then - Cargo:UnBoard() + Cargo:__UnBoard( UnboardDelay ) + UnboardDelay = UnboardDelay + UnboardInterval Cargo:SetDeployed( true ) - self:__Unboard( 10, Cargo, Deployed ) + self:__Unboard( UnboardDelay, Cargo, APCUnit, DeployZone ) end end end @@ -566,32 +607,22 @@ end -- @param #string Event Event. -- @param #string To To state. -- @param #string Cargo.Cargo#CARGO Cargo Cargo object. --- @param #boolean Deployed Cargo is deployed. -function AI_CARGO_APC:onafterUnboard( APC, From, Event, To, Cargo, Deployed ) +-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +function AI_CARGO_APC:onafterUnboard( APC, From, Event, To, Cargo, APCUnit, DeployZone ) self:F( { APC, From, Event, To, Cargo:GetName() } ) if APC and APC:IsAlive() then if not Cargo:IsUnLoaded() then - self:__Unboard( 10, Cargo, Deployed ) - else - for _, APCUnit in pairs( APC:GetUnits() ) do - local APCUnit = APCUnit -- Wrapper.Unit#UNIT - for _, Cargo in pairs( APCUnit:GetCargo() ) do - if Cargo:IsLoaded() then - Cargo:UnBoard() - Cargo:SetDeployed( true ) - self:__Unboard( 10, Cargo, Deployed ) - return - end - end - end - self:__Unloaded( 1, Cargo, Deployed ) + self:__Unboard( 10, Cargo, APCUnit, DeployZone ) + return end end + + self:Unloaded( Cargo, APCUnit, DeployZone ) end ---- On before Unloaded event. +--- On after Unloaded event. -- @param #AI_CARGO_APC self -- @param Wrapper.Group#GROUP APC -- @param #string From From state. @@ -599,9 +630,9 @@ end -- @param #string To To state. -- @param #string Cargo.Cargo#CARGO Cargo Cargo object. -- @param #boolean Deployed Cargo is deployed. --- @return #boolean All cargo unloaded. -function AI_CARGO_APC:onbeforeUnloaded( APC, From, Event, To, Cargo, Deployed ) - self:F( { APC, From, Event, To, Cargo:GetName(), Deployed = Deployed } ) +-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +function AI_CARGO_APC:onafterUnloaded( APC, From, Event, To, Cargo, APCUnit, DeployZone ) + self:F( { APC, From, Event, To, Cargo:GetName(), DeployZone = DeployZone } ) local AllUnloaded = true @@ -619,32 +650,31 @@ function AI_CARGO_APC:onbeforeUnloaded( APC, From, Event, To, Cargo, Deployed ) end if AllUnloaded == true then - if Deployed == true then + if DeployZone == true then self.APC_Cargo = {} end - self:Guard() self.CargoCarrier = APC end end - - self:F( { AllUnloaded = AllUnloaded } ) - return AllUnloaded + + if AllUnloaded == true then + self:Deployed( DeployZone ) + end end ---- On after Unloaded event. +--- On after Deployed event. -- @param #AI_CARGO_APC self -- @param Wrapper.Group#GROUP APC -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param #string Cargo.Cargo#CARGO Cargo Cargo object. --- @param #boolean Deployed Cargo is deployed. --- @return #boolean All cargo unloaded. -function AI_CARGO_APC:onafterUnloaded( APC, From, Event, To, Cargo, Deployed ) - self:F( { APC, From, Event, To, Cargo:GetName(), Deployed = Deployed } ) +-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +function AI_CARGO_APC:onafterDeployed( APC, From, Event, To, DeployZone ) + self:F( { APC, From, Event, To, DeployZone = DeployZone } ) self.Transporting = false + self:__Guard( 0.1 ) end @@ -659,7 +689,7 @@ function AI_CARGO_APC:onafterFollow( APC, From, Event, To ) self:F( "Follow" ) if APC and APC:IsAlive() then - for APCUnit, Cargo in pairs( self.APC_Cargo ) do + for Cargo, APCUnit in pairs( self.APC_Cargo ) do local Cargo = Cargo -- Cargo.Cargo#CARGO if Cargo:IsUnLoaded() then self:FollowToCarrier( self, APCUnit, Cargo ) @@ -673,26 +703,22 @@ end --- @param #AI_CARGO_APC -- @param Wrapper.Group#GROUP APC -function AI_CARGO_APC._Pickup( APC, self ) +function AI_CARGO_APC._Pickup( APC, self, PickupZone ) APC:F( { "AI_CARGO_APC._Pickup:", APC:GetName() } ) if APC:IsAlive() then - self:Load() - self.Relocating = true + self:Load( PickupZone) end end ---- @param #AI_CARGO_APC --- @param Wrapper.Group#GROUP APC -function AI_CARGO_APC._Deploy( APC, self ) +function AI_CARGO_APC._Deploy( APC, self, Coordinate, DeployZone ) APC:F( { "AI_CARGO_APC._Deploy:", APC } ) if APC:IsAlive() then - self:Unload( true ) - self.Relocating = false + self:Unload( DeployZone ) end end @@ -706,7 +732,8 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate of the pickup point. -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. -function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed ) +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed, PickupZone ) if APC and APC:IsAlive() then @@ -717,7 +744,7 @@ function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed ) local Waypoints = APC:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true ) - local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Pickup", self ) + local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Pickup", self, PickupZone ) self:F({Waypoints = Waypoints}) local Waypoint = Waypoints[#Waypoints] @@ -725,10 +752,11 @@ function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed ) APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. else - AI_CARGO_APC._Pickup( APC, self ) + AI_CARGO_APC._Pickup( APC, self, PickupZone ) end - - self.Transporting = true + + self.Relocating = true + self.Transporting = false end end @@ -742,7 +770,7 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate Deploy place. -- @param #number Speed Speed in km/h to drive to the depoly coordinate. Default is 50% of max possible speed the unit can go. -function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed ) +function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed, DeployZone ) if APC and APC:IsAlive() then @@ -752,13 +780,16 @@ function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed ) local Waypoints = APC:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true ) - local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Deploy", self ) + local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Deploy", self, Coordinate, DeployZone ) self:F({Waypoints = Waypoints}) local Waypoint = Waypoints[#Waypoints] APC:SetTaskWaypoint( Waypoint, TaskFunction ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone. APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. + + self.Relocating = false + self.Transporting = true end end diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 8ce9bcb42..55e5a79fd 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -18,7 +18,8 @@ -- @field #AI_CARGO_AIRPLANE AI_CARGO_AIRPLANE = { ClassName = "AI_CARGO_AIRPLANE", - Coordinate = nil -- Core.Point#COORDINATE, + Coordinate = nil, -- Core.Point#COORDINATE + Airplane_Cargo = {}, } --- Creates a new AI_CARGO_AIRPLANE object. @@ -39,10 +40,13 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) self:AddTransition( { "Unloaded", "Boarding" }, "Load", "Boarding" ) self:AddTransition( "Boarding", "Board", "Boarding" ) - self:AddTransition( "Boarding", "Loaded", "Loaded" ) + self:AddTransition( "Boarding", "Loaded", "Boarding" ) + self:AddTransition( "Boarding", "PickedUp", "Loaded" ) + self:AddTransition( "Loaded", "Unload", "Unboarding" ) self:AddTransition( "Unboarding", "Unboard", "Unboarding" ) - self:AddTransition( "Unboarding" , "Unloaded", "Unloaded" ) + self:AddTransition( "Unboarding" , "Unloaded", "Unboarding" ) + self:AddTransition( "Unboarding" , "Deployed", "Unloaded" ) self:AddTransition( "*", "Landed", "*" ) self:AddTransition( "*", "Home" , "*" ) @@ -245,7 +249,7 @@ function AI_CARGO_AIRPLANE:onafterLanded( Airplane, From, Event, To ) -- Aircraft was sent to this airbase to pickup troops. Initiate loadling. if self.RoutePickup == true then env.info("FF load airplane "..Airplane:GetName()) - self:Load( Airplane:GetCoordinate() ) + self:Load( self.PickupZone ) self.RoutePickup = false self.Relocating = true end @@ -269,12 +273,15 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param Wrapper.Airbase#AIRBASE Airbase Airbase where the troops as picked up. +-- @param Core.Point#COORDINATE Coordinate -- @param #number Speed in km/h for travelling to pickup base. -function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Airbase, Speed ) +-- @param Core.Zone#ZONE_AIRBASE PickupZone +function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate, Speed, PickupZone ) if Airplane and Airplane:IsAlive()~=nil then env.info("FF onafterpick aircraft alive") + + self.PickupZone = PickupZone -- Get closest airbase of current position. local ClosestAirbase, DistToAirbase=Airplane:GetCoordinate():GetClosestAirbase() @@ -288,8 +295,10 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Airbase, Sp self.Airbase=ClosestAirbase end + local Airbase = PickupZone:GetAirbase() + -- Distance from closest to pickup airbase ==> we need to know if we are already at the pickup airbase. - local Dist=Airbase:GetCoordinate():Get2DDistance(ClosestAirbase:GetCoordinate()) + local Dist = Airbase:GetCoordinate():Get2DDistance(ClosestAirbase:GetCoordinate()) env.info("Distance closest to pickup airbase = "..Dist) if Airplane:InAir() or Dist>500 then @@ -305,9 +314,6 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Airbase, Sp -- Aircraft is on a pickup mission. self.RoutePickup = true - -- Unclear!? - self.Transporting = true - self.Relocating = false else env.info("FF onafterpick calling landed") @@ -316,9 +322,13 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Airbase, Sp self:Landed() end + + self.Transporting = false + self.Relocating = true else env.info("FF onafterpick aircraft not alive") end + end @@ -328,12 +338,15 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troups should be deployed. --- @param #number Speed Speed in km/h for travelling to deploy base. -function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Airbase, Speed ) +-- @param Core.Point#COORDINATE Coordinate +-- @param #number Speed in km/h for travelling to pickup base. +-- @param Core.Zone#ZONE_AIRBASE DeployZone +function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Coordinate, Speed, DeployZone ) if Airplane and Airplane:IsAlive()~=nil then + local Airbase = DeployZone:GetAirbase() + -- Activate uncontrolled airplane. if Airplane:IsAlive()==false then Airplane:SetCommand({id = 'Start', params = {}}) @@ -361,32 +374,42 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param Wrapper.Point#COORDINATE Coordinate Place where the cargo is guided to if it is inside the load radius. -function AI_CARGO_AIRPLANE:onbeforeLoad( Airplane, From, Event, To, Coordinate ) +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO_AIRPLANE:onbeforeLoad( Airplane, From, Event, To, PickupZone ) local Boarding = false + local LoadInterval = 10 + local LoadDelay = 10 + if Airplane and Airplane:IsAlive() ~= nil then for _, AirplaneUnit in pairs( Airplane:GetUnits() ) do local AirplaneUnit = AirplaneUnit -- Wrapper.Unit#UNIT - for _,_Cargo in pairs( self.CargoSet:GetSet() ) do - self:F({_Cargo:GetName()}) - local Cargo=_Cargo --Cargo.Cargo#CARGO - local InRadius = Cargo:IsInLoadRadius( Coordinate ) - if InRadius then - - -- Is there a cargo still unloaded? - if Cargo:IsUnLoaded() == true then - - Cargo:Board( AirplaneUnit, 25 ) - self:__Board( 5, Cargo, AirplaneUnit ) - Boarding = true - break + local CargoBayFreeWeight = AirplaneUnit:GetCargoBayFreeWeight() + self:F({CargoBayFreeWeight=CargoBayFreeWeight}) + + for _, Cargo in UTILS.spairs( self.CargoSet:GetSet(), function( t, a, b ) return t[a]:GetWeight() > t[b]:GetWeight() end ) do + self:F({Cargo:GetName()}) + local Cargo=Cargo --Cargo.Cargo#CARGO + if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then + + if Cargo:IsInLoadRadius( AirplaneUnit:GetCoordinate() ) then + + local CargoWeight = Cargo:GetWeight() + + -- Only when there is space within the bay to load the next cargo item! + if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then + Cargo:__Board( LoadDelay, AirplaneUnit, 25 ) + LoadDelay = LoadDelay + LoadInterval + self:__Board( LoadDelay, Cargo, AirplaneUnit, PickupZone ) + self.Airplane_Cargo[AirplaneUnit] = Cargo + Boarding = true + CargoBayFreeWeight = CargoBayFreeWeight - CargoWeight + end end - end - + end end end end @@ -403,68 +426,64 @@ end -- @param #string To To state. -- @param Cargo.Cargo#CARGO Cargo Cargo object. -- @param Wrapper.Unit#UNIT AirplaneUnit -function AI_CARGO_AIRPLANE:onafterBoard( Airplane, From, Event, To, Cargo, AirplaneUnit ) +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO_AIRPLANE:onafterBoard( Airplane, From, Event, To, Cargo, AirplaneUnit, PickupZone ) if Airplane and Airplane:IsAlive() then self:F({ IsLoaded = Cargo:IsLoaded() } ) if not Cargo:IsLoaded() then - self:__Board( 10, Cargo, AirplaneUnit ) - else - local CargoBayFreeWeight = AirplaneUnit:GetCargoBayFreeWeight() - self:F({CargoBayFreeWeight=CargoBayFreeWeight}) - - -- Check if another cargo can be loaded into the airplane. - for _,_Cargo in UTILS.spairs( self.CargoSet:GetSet(), function( t, a, b ) return t[a]:GetWeight() > t[b]:GetWeight() end ) do - - self:F({_Cargo:GetName()}) - local Cargo =_Cargo --Cargo.Cargo#CARGO - - -- Is there a cargo still unloaded? - if Cargo:IsUnLoaded() == true then - - -- Only when the cargo is within load radius. - local InRadius = Cargo:IsInLoadRadius( Airplane:GetCoordinate() ) - if InRadius then - - local CargoBayFreeWeight = AirplaneUnit:GetCargoBayFreeWeight() - --local CargoBayFreeVolume = Airplane:GetCargoBayFreeVolume() - - local CargoWeight = Cargo:GetWeight() - --local CargoVolume = Cargo:GetVolume() - - -- Only when there is space within the bay to load the next cargo item! - if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then - - -- ok, board. - self:__Load( 5, Airplane:GetCoordinate() ) - - -- And start the boarding loop for the AI_CARGO_AIRPLANE object until the cargo is boarded. - --Cargo:Board( Airplane, 25 ) - return - end - end - end - end - self:__Loaded( 1, Cargo ) + self:__Board( 10, Cargo, AirplaneUnit, PickupZone ) + return end end + + self:__Loaded( 10, Cargo, AirplaneUnit, PickupZone ) + +end + + +--- On After Loaded event. +-- @param #AI_CARGO_HELICOPTER self +-- @param Wrapper.Group#GROUP Helicopter +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @return #boolean Cargo loaded. +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO_AIRPLANE:onafterLoaded( AirplaneGroup, From, Event, To, Cargo, AirplaneUnit, PickupZone ) + self:F( { AirplaneGroup, From, Event, To } ) + + local Loaded = true + + if AirplaneGroup and AirplaneGroup:IsAlive() then + for AirplaneUnit, Cargo in pairs( self.Airplane_Cargo ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + self:F( { IsLoaded = Cargo:IsLoaded(), IsDestroyed = Cargo:IsDestroyed(), Cargo:GetName(), AirplaneGroup:GetName() } ) + if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then + Loaded = false + end + end + end + + if Loaded then + self:PickedUp( PickupZone ) + end end ---- On after Loaded event. Cargo is inside the carrier and ready to be transported. +--- On after PickedUp event. All cargo is inside the carrier and ready to be transported. -- @param #AI_CARGO_AIRPLANE self -- @param Wrapper.Group#GROUP Airplane Cargo transport plane. -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -function AI_CARGO_AIRPLANE:onafterLoaded( Airplane, From, Event, To, Cargo ) +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO_AIRPLANE:onafterPickedUp( Airplane, From, Event, To, PickupZone ) + self:F( { AirplaneGroup, From, Event, To } ) - env.info("FF troops loaded into cargo plane") - if Airplane and Airplane:IsAlive() then - self:F( { "Transporting" } ) self.Transporting = true -- This will only be executed when there is no cargo boarded anymore. The dispatcher will then kick-off the deploy cycle! end end @@ -476,7 +495,10 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To ) +function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To, DeployZone ) + + local UnboardInterval = 10 + local UnboardDelay = 10 if Airplane and Airplane:IsAlive() then for _, AirplaneUnit in pairs( Airplane:GetUnits() ) do @@ -489,9 +511,10 @@ function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To ) self:T( { CargoCarrierHeading, CargoDeployHeading } ) local CargoDeployCoordinate = Airplane:GetPointVec2():Translate( 150, CargoDeployHeading ) - Cargo:UnBoard( CargoDeployCoordinate ) + Cargo:__UnBoard( UnboardDelay, CargoDeployCoordinate ) + UnboardDelay = UnboardDelay + UnboardInterval Cargo:SetDeployed( true ) - self:__Unboard( 10, Cargo ) + self:__Unboard( UnboardDelay, Cargo, AirplaneUnit, DeployZone ) end end end @@ -504,34 +527,19 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -function AI_CARGO_AIRPLANE:onafterUnboard( Airplane, From, Event, To, Cargo ) +function AI_CARGO_AIRPLANE:onafterUnboard( Airplane, From, Event, To, Cargo, AirplaneUnit, DeployZone ) self:E( { "Unboard", Cargo } ) if Airplane and Airplane:IsAlive() then if not Cargo:IsUnLoaded() then - self:__Unboard( 10, Cargo ) - else - for _, AirplaneUnit in pairs( Airplane:GetUnits() ) do - local Cargos = AirplaneUnit:GetCargo() - for CargoID, Cargo in pairs( Cargos ) do - if Cargo:IsLoaded() then - local Angle = 180 - local CargoCarrierHeading = Airplane:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - self:T( { CargoCarrierHeading, CargoDeployHeading } ) - local CargoDeployCoordinate = Airplane:GetPointVec2():Translate( 150, CargoDeployHeading ) - Cargo:UnBoard( CargoDeployCoordinate ) - Cargo:SetDeployed( true ) - - self:__Unboard( 10, Cargo ) - return - end - end - self:__Unloaded( 1, Cargo ) - end + self:__Unboard( 10, Cargo, AirplaneUnit, DeployZone ) + return end end + + self:Unloaded( Cargo, AirplaneUnit, DeployZone ) + end --- On after Unloaded event. Cargo has been unloaded, i.e. the unboarding process is finished. @@ -541,16 +549,52 @@ end -- @param #string Event Event. -- @param #string To To state. -- @param Cargo.Cargo#CARGO Cargo -function AI_CARGO_AIRPLANE:onafterUnloaded( Airplane, From, Event, To, Cargo ) +function AI_CARGO_AIRPLANE:onafterUnloaded( Airplane, From, Event, To, Cargo, AirplaneUnit, DeployZone ) - self:E( { "Unloaded", Cargo } ) + local AllUnloaded = true + + if AirplaneUnit and AirplaneUnit:IsAlive() then + for _, AirplaneUnit in pairs( AirplaneUnit:GetUnits() ) do + local IsEmpty = AirplaneUnit:IsCargoEmpty() + self:I({ IsEmpty = IsEmpty }) + if not IsEmpty then + AllUnloaded = false + break + end + end + + if AllUnloaded == true then + if DeployZone then + self.Airplane_Cargo = {} + end + self.Airplane = AirplaneUnit + end + end + + if AllUnloaded == true then + self:Deployed( DeployZone ) + end + +end + + +--- On after Deployed event. +-- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Group#GROUP Airplane Cargo transport plane. +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Cargo.Cargo#CARGO Cargo +function AI_CARGO_AIRPLANE:onafterDeployed( Airplane, From, Event, To, DeployZone ) if Airplane and Airplane:IsAlive() then - self.Airplane = Airplane self.Transporting = false -- This will only be executed when there is no cargo onboard anymore. The dispatcher will then kick-off the pickup cycle! end end + + + --- Route the airplane from one airport or it's current position to another airbase. -- @param #AI_CARGO_AIRPLANE self -- @param Wrapper.Group#GROUP Airplane Airplane group to be routed. diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 71359203b..1ffc9b9b9 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -21,47 +21,304 @@ -- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER object recognize the cargo. -- Please consult the @{Cargo} module for more information. -- --- ## 1. AI\_CARGO\_DISPATCHER constructor +-- # 1) AI\_CARGO\_DISPATCHER constructor -- -- * @{#AI_CARGO_DISPATCHER.New}(): Creates a new AI\_CARGO\_DISPATCHER object. -- --- ## 2. AI\_CARGO\_DISPATCHER is a FSM +-- # 2) AI\_CARGO\_DISPATCHER is a FSM -- -- ![Process](..\Presentations\AI_PATROL\Dia2.JPG) -- --- ### 2.1. AI\_CARGO\_DISPATCHER States +-- ## 2.1) AI\_CARGO\_DISPATCHER States -- -- * **Monitoring**: The process is dispatching. -- * **Idle**: The process is idle. -- --- ### 2.2. AI\_CARGO\_DISPATCHER Events +-- ## 2.2) AI\_CARGO\_DISPATCHER Events -- -- * **Monitor**: Monitor and take action. -- * **Start**: Start the transport process. -- * **Stop**: Stop the transport process. -- * **Pickup**: Pickup cargo. -- * **Load**: Load the cargo. +-- * **Loading**: The dispatcher is coordinating the loading of a cargo. -- * **Loaded**: Flag that the cargo is loaded. +-- * **PickedUp**: The dispatcher has loaded all requested cargo into the CarrierGroup. -- * **Deploy**: Deploy cargo to a location. -- * **Unload**: Unload the cargo. -- * **Unloaded**: Flag that the cargo is unloaded. -- * **Home**: A Carrier is going home. -- --- ## 3. Set the pickup parameters. +-- # 3) Enhance your mission scripts with **Tailored** Event Handling! +-- +-- Use these methods to capture the events and tailor the events with your own code! +-- All classes derived from AI_CARGO_DISPATCHER can capture these events, and you can write your own code. +-- +-- In order to properly capture the events, it is mandatory that you execute the following actions using your script: +-- +-- * Copy / Paste the code section into your script. +-- * Change the CLASS literal to the object name you have in your script. +-- * Within the function, you can now write your own code! +-- * IntelliSense will recognize the type of the variables provided by the function. Note: the From, Event and To variables can be safely ignored, +-- but you need to declare them as they are automatically provided by the event handling system of MOOSE. +-- +-- You can send messages or fire off any other events within the code section. The sky is the limit! +-- +-- ## 3.1) Tailor the **Pickup** event +-- +-- Use this event handler to tailor the event when a CarrierGroup is routed towards a new pickup Coordinate and a specified Speed. +-- You can use this event handler to post messages to players, or provide status updates etc. +-- +-- +-- --- Pickup Handler OnAfter for CLASS. +-- -- Use this event handler to tailor the event when a CarrierGroup is routed towards a new pickup Coordinate and a specified Speed. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- @param #CLASS self +-- -- @param #string From A string that contains the "*from state name*" when the event was fired. +-- -- @param #string Event A string that contains the "*event name*" when the event was fired. +-- -- @param #string To A string that contains the "*to state name*" when the event was fired. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Core.Point#COORDINATE Coordinate The coordinate of the pickup location. +-- -- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the pickup Coordinate. +-- -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. +-- function CLASS:OnAfterPickup( From, Event, To, CarrierGroup, Coordinate, Speed, PickupZone ) +-- +-- -- Write here your own code. +-- +-- end +-- +-- +-- ## 3.2) Tailor the **Load** event +-- +-- Use this event handler to tailor the event when a CarrierGroup has initiated the loading or boarding of cargo within reporting or near range. +-- You can use this event handler to post messages to players, or provide status updates etc. +-- +-- +-- --- Load Handler OnAfter for CLASS. +-- -- Use this event handler to tailor the event when a CarrierGroup has initiated the loading or boarding of cargo within reporting or near range. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- @param #CLASS self +-- -- @param #string From A string that contains the "*from state name*" when the event was fired. +-- -- @param #string Event A string that contains the "*event name*" when the event was fired. +-- -- @param #string To A string that contains the "*to state name*" when the event was fired. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. +-- function CLASS:OnAfterLoad( From, Event, To, CarrierGroup, PickupZone ) +-- +-- -- Write here your own code. +-- +-- end +-- +-- +-- ## 3.3) Tailor the **Loading** event +-- +-- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup is in the process of loading or boarding of a cargo object. +-- You can use this event handler to post messages to players, or provide status updates etc. +-- +-- +-- --- Loading Handler OnAfter for CLASS. +-- -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup is in the process of loading or boarding of a cargo object. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- Note that this event is fired repeatedly until all cargo (units) have been boarded into the carrier. +-- -- @param #CLASS self +-- -- @param #string From A string that contains the "*from state name*" when the event was fired. +-- -- @param #string Event A string that contains the "*event name*" when the event was fired. +-- -- @param #string To A string that contains the "*to state name*" when the event was fired. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Cargo.Cargo#CARGO Cargo The cargo object. +-- -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo loading operation. +-- -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. +-- function CLASS:OnAfterLoading( From, Event, To, CarrierGroup, Cargo, CarrierUnit, PickupZone ) +-- +-- -- Write here your own code. +-- +-- end +-- +-- +-- ## 3.4) Tailor the **Loaded** event +-- +-- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup has loaded a cargo object. +-- You can use this event handler to post messages to players, or provide status updates etc. +-- Note that if more cargo objects were loading or boarding into the CarrierUnit, then this event can be fired multiple times for each different Cargo/CarrierUnit. +-- +-- The function provides the CarrierGroup, which is the main group that was loading the Cargo into the CarrierUnit. +-- A CarrierUnit is part of the larger CarrierGroup. +-- +-- +-- --- Loaded Handler OnAfter for CLASS. +-- -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup has loaded a cargo object. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- Note that if more cargo objects were loading or boarding into the CarrierUnit, then this event can be fired multiple times for each different Cargo/CarrierUnit. +-- -- A CarrierUnit can be part of the larger CarrierGroup. +-- -- @param #CLASS self +-- -- @param #string From A string that contains the "*from state name*" when the event was fired. +-- -- @param #string Event A string that contains the "*event name*" when the event was fired. +-- -- @param #string To A string that contains the "*to state name*" when the event was fired. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Cargo.Cargo#CARGO Cargo The cargo object. +-- -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo loading operation. +-- -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. +-- function CLASS:OnAfterLoaded( From, Event, To, CarrierGroup, Cargo, CarrierUnit, PickupZone ) +-- +-- -- Write here your own code. +-- +-- end +-- +-- +-- ## 3.5) Tailor the **PickedUp** event +-- +-- Use this event handler to tailor the event when a carrier has picked up all cargo objects into the CarrierGroup. +-- You can use this event handler to post messages to players, or provide status updates etc. +-- +-- +-- --- PickedUp Handler OnAfter for CLASS. +-- -- Use this event handler to tailor the event when a carrier has picked up all cargo objects into the CarrierGroup. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- @param #CLASS self +-- -- @param #string From A string that contains the "*from state name*" when the event was fired. +-- -- @param #string Event A string that contains the "*event name*" when the event was fired. +-- -- @param #string To A string that contains the "*to state name*" when the event was fired. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. +-- function CLASS:OnAfterPickedUp( From, Event, To, CarrierGroup, PickupZone ) +-- +-- -- Write here your own code. +-- +-- end +-- +-- +-- ## 3.6) Tailor the **Deploy** event +-- +-- Use this event handler to tailor the event when a CarrierGroup is routed to a deploy coordinate, to Unload all cargo objects in each CarrierUnit. +-- You can use this event handler to post messages to players, or provide status updates etc. +-- +-- +-- --- Deploy Handler OnAfter for CLASS. +-- -- Use this event handler to tailor the event when a CarrierGroup is routed to a deploy coordinate, to Unload all cargo objects in each CarrierUnit. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- @param #CLASS self +-- -- @param #string From A string that contains the "*from state name*" when the event was fired. +-- -- @param #string Event A string that contains the "*event name*" when the event was fired. +-- -- @param #string To A string that contains the "*to state name*" when the event was fired. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Core.Point#COORDINATE Coordinate The deploy coordinate. +-- -- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the deploy Coordinate. +-- -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +-- function CLASS:OnAfterDeploy( From, Event, To, CarrierGroup, Coordinate, Speed, DeployZone ) +-- +-- -- Write here your own code. +-- +-- end +-- +-- +-- ## 3.7) Tailor the **Unload** event +-- +-- Use this event handler to tailor the event when a CarrierGroup has initiated the unloading or unboarding of cargo. +-- You can use this event handler to post messages to players, or provide status updates etc. +-- +-- +-- --- Unload Handler OnAfter for CLASS. +-- -- Use this event handler to tailor the event when a CarrierGroup has initiated the unloading or unboarding of cargo. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- @param #CLASS self +-- -- @param #string From A string that contains the "*from state name*" when the event was fired. +-- -- @param #string Event A string that contains the "*event name*" when the event was fired. +-- -- @param #string To A string that contains the "*to state name*" when the event was fired. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +-- function CLASS:OnAfterUnload( From, Event, To, CarrierGroup, DeployZone ) +-- +-- -- Write here your own code. +-- +-- end +-- +-- +-- ## 3.8) Tailor the **Unloading** event +-- +-- +-- --- UnLoading Handler OnAfter for CLASS. +-- -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup is in the process of unloading or unboarding of a cargo object. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- Note that this event is fired repeatedly until all cargo (units) have been unboarded from the CarrierUnit. +-- -- @param #CLASS self +-- -- @param #string From A string that contains the "*from state name*" when the event was fired. +-- -- @param #string Event A string that contains the "*event name*" when the event was fired. +-- -- @param #string To A string that contains the "*to state name*" when the event was fired. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Cargo.Cargo#CARGO Cargo The cargo object. +-- -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo unloading operation. +-- -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +-- function CLASS:OnAfterUnload( From, Event, To, CarrierGroup, Cargo, CarrierUnit, DeployZone ) +-- +-- -- Write here your own code. +-- +-- end +-- +-- +-- ## 3.9) Tailor the **Unloaded** event +-- +-- +-- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup has unloaded a cargo object. +-- You can use this event handler to post messages to players, or provide status updates etc. +-- +-- --- Unloaded Handler OnAfter for CLASS. +-- -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup has unloaded a cargo object. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- Note that if more cargo objects were unloading or unboarding from the CarrierUnit, then this event can be fired multiple times for each different Cargo/CarrierUnit. +-- -- A CarrierUnit can be part of the larger CarrierGroup. +-- -- @param #CLASS self +-- -- @param #string From A string that contains the "*from state name*" when the event was fired. +-- -- @param #string Event A string that contains the "*event name*" when the event was fired. +-- -- @param #string To A string that contains the "*to state name*" when the event was fired. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Cargo.Cargo#CARGO Cargo The cargo object. +-- -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo unloading operation. +-- -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +-- function CLASS:OnAfterUnloaded( From, Event, To, CarrierGroup, Cargo, CarrierUnit, DeployZone ) +-- +-- -- Write here your own code. +-- +-- end +-- +-- +-- ## 3.10) Tailor the **Deployed** event +-- +-- Use this event handler to tailor the event when a carrier has deployed all cargo objects from the CarrierGroup. +-- You can use this event handler to post messages to players, or provide status updates etc. +-- +-- +-- --- Deployed Handler OnAfter for AI_CARGO_DISPATCHER. +-- -- Use this event handler to tailor the event when a carrier has deployed all cargo objects from the CarrierGroup. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterDeployed +-- -- @param #AI_CARGO_DISPATCHER self +-- -- @param #string From A string that contains the "*from state name*" when the event was fired. +-- -- @param #string Event A string that contains the "*event name*" when the event was fired. +-- -- @param #string To A string that contains the "*to state name*" when the event was fired. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +-- function CLASS:OnAfterDeployed( From, Event, To, CarrierGroup, DeployZone ) +-- +-- -- Write here your own code. +-- +-- end +-- +-- +-- # 3) Set the pickup parameters. -- -- Several parameters can be set to pickup cargo: -- -- * @{#AI_CARGO_DISPATCHER.SetPickupRadius}(): Sets or randomizes the pickup location for the carrier around the cargo coordinate in a radius defined an outer and optional inner radius. -- * @{#AI_CARGO_DISPATCHER.SetPickupSpeed}(): Set the speed or randomizes the speed in km/h to pickup the cargo. -- --- ## 4. Set the deploy parameters. +-- # 4) Set the deploy parameters. -- -- Several parameters can be set to deploy cargo: -- -- * @{#AI_CARGO_DISPATCHER.SetDeployRadius}(): Sets or randomizes the deploy location for the carrier around the cargo coordinate in a radius defined an outer and an optional inner radius. -- * @{#AI_CARGO_DISPATCHER.SetDeploySpeed}(): Set the speed or randomizes the speed in km/h to deploy the cargo. -- --- ## 5. Set the home zone when there isn't any more cargo to pickup. +-- #) 5. Set the home zone when there isn't any more cargo to pickup. -- -- A home zone can be specified to where the Carriers will move when there isn't any cargo left for pickup. -- Use @{#AI_CARGO_DISPATCHER.SetHomeZone}() to specify the home zone. @@ -74,7 +331,7 @@ AI_CARGO_DISPATCHER = { ClassName = "AI_CARGO_DISPATCHER", SetCarrier = nil, - DeployZonesSet = nil, + DeployZoneSet = nil, AI_Cargo = {}, PickupCargo = {} } @@ -115,12 +372,16 @@ function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo ) self:AddTransition( "*", "Pickup", "*" ) + self:AddTransition( "*", "Load", "*" ) self:AddTransition( "*", "Loading", "*" ) self:AddTransition( "*", "Loaded", "*" ) + self:AddTransition( "*", "PickedUp", "*" ) self:AddTransition( "*", "Deploy", "*" ) + self:AddTransition( "*", "Unload", "*" ) self:AddTransition( "*", "Unloading", "*" ) self:AddTransition( "*", "Unloaded", "*" ) + self:AddTransition( "*", "Deployed", "*" ) self:AddTransition( "*", "Home", "*" ) @@ -146,21 +407,23 @@ end -- @param #AI_CARGO_DISPATCHER self -- @param Core.Set#SET_GROUP SetCarrier -- @param Core.Set#SET_CARGO SetCargo --- @param Core.Set#SET_ZONE DeployZonesSet +-- @param Core.Set#SET_ZONE PickupZoneSet +-- @param Core.Set#SET_ZONE DeployZoneSet -- @return #AI_CARGO_DISPATCHER -- @usage -- -- -- Create a new cargo dispatcher -- SetCarriers = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() -- SetCargos = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() --- DeployZonesSet = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() +-- DeployZoneSet = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() -- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZone ) -- -function AI_CARGO_DISPATCHER:NewWithZones( SetCarriers, SetCargos, DeployZonesSet ) +function AI_CARGO_DISPATCHER:NewWithZones( SetCarriers, SetCargos, PickupZoneSet, DeployZoneSet ) local self = AI_CARGO_DISPATCHER:New( SetCarriers, SetCargos ) -- #AI_CARGO_DISPATCHER - self.DeployZonesSet = DeployZonesSet + self.PickupZoneSet = PickupZoneSet + self.DeployZoneSet = DeployZoneSet return self end @@ -376,28 +639,174 @@ function AI_CARGO_DISPATCHER:onafterMonitor() self.AI_Cargo[Carrier] = self:AICargo( Carrier, self.SetCargo, self.CombatRadius ) AI_Cargo = self.AI_Cargo[Carrier] - function AI_Cargo.OnAfterPickup( AI_Cargo, Carrier, From, Event, To, Cargo ) - self:Pickup( Carrier, Cargo ) + --- Pickup Handler OnAfter for AI_CARGO_DISPATCHER. + -- Use this event handler to tailor the event when a CarrierGroup is routed towards a new pickup Coordinate and a specified Speed. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterPickup + -- @param #AI_CARGO_DISPATCHER self + -- @param #string From A string that contains the "*from state name*" when the event was fired. + -- @param #string Event A string that contains the "*event name*" when the event was fired. + -- @param #string To A string that contains the "*to state name*" when the event was fired. + -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. + -- @param Core.Point#COORDINATE Coordinate The coordinate of the pickup location. + -- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the pickup Coordinate. + -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. + function AI_Cargo.OnAfterPickup( AI_Cargo, CarrierGroup, From, Event, To, Coordinate, Speed, PickupZone ) + self:Pickup( CarrierGroup, Coordinate, Speed, PickupZone ) end - function AI_Cargo.OnAfterLoad( AI_Cargo, Carrier, From, Event, To, Cargo ) - self:Loading( Carrier ) + --- Load Handler OnAfter for AI_CARGO_DISPATCHER. + -- Use this event handler to tailor the event when a CarrierGroup has initiated the loading or boarding of cargo within reporting or near range. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterLoad + -- @param #AI_CARGO_DISPATCHER self + -- @param #string From A string that contains the "*from state name*" when the event was fired. + -- @param #string Event A string that contains the "*event name*" when the event was fired. + -- @param #string To A string that contains the "*to state name*" when the event was fired. + -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. + -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. + + function AI_Cargo.OnAfterLoad( AI_Cargo, CarrierGroup, From, Event, To, PickupZone ) + self:Load( CarrierGroup, PickupZone ) end - function AI_Cargo.OnAfterLoaded( AI_Cargo, Carrier, From, Event, To, Cargo ) - self:Loaded( Carrier, Cargo ) + --- Loading Handler OnAfter for AI_CARGO_DISPATCHER. + -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup is in the process of loading or boarding of a cargo object. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- Note that this event is fired repeatedly until all cargo (units) have been boarded into the carrier. + -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterLoading + -- @param #AI_CARGO_DISPATCHER self + -- @param #string From A string that contains the "*from state name*" when the event was fired. + -- @param #string Event A string that contains the "*event name*" when the event was fired. + -- @param #string To A string that contains the "*to state name*" when the event was fired. + -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. + -- @param Cargo.Cargo#CARGO Cargo The cargo object. + -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo loading operation. + -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. + + function AI_Cargo.OnAfterBoard( AI_Cargo, CarrierGroup, From, Event, To, Cargo, CarrierUnit, PickupZone ) + self:Loading( CarrierGroup, Cargo, CarrierUnit, PickupZone ) end - function AI_Cargo.OnAfterDeploy( AI_Cargo, Carrier, From, Event, To, Cargo ) - self:Deploy( Carrier, Cargo ) + --- Loaded Handler OnAfter for AI_CARGO_DISPATCHER. + -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup has loaded a cargo object. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- Note that if more cargo objects were loading or boarding into the CarrierUnit, then this event can be fired multiple times for each different Cargo/CarrierUnit. + -- A CarrierUnit can be part of the larger CarrierGroup. + -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterLoaded + -- @param #AI_CARGO_DISPATCHER self + -- @param #string From A string that contains the "*from state name*" when the event was fired. + -- @param #string Event A string that contains the "*event name*" when the event was fired. + -- @param #string To A string that contains the "*to state name*" when the event was fired. + -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. + -- @param Cargo.Cargo#CARGO Cargo The cargo object. + -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo loading operation. + -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. + + function AI_Cargo.OnAfterLoaded( AI_Cargo, CarrierGroup, From, Event, To, Cargo, CarrierUnit, PickupZone ) + self:Loaded( CarrierGroup, Cargo, CarrierUnit, PickupZone ) + end + + --- PickedUp Handler OnAfter for AI_CARGO_DISPATCHER. + -- Use this event handler to tailor the event when a carrier has picked up all cargo objects into the CarrierGroup. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterPickedUp + -- @param #AI_CARGO_DISPATCHER self + -- @param #string From A string that contains the "*from state name*" when the event was fired. + -- @param #string Event A string that contains the "*event name*" when the event was fired. + -- @param #string To A string that contains the "*to state name*" when the event was fired. + -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. + -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. + + function AI_Cargo.OnAfterPickedUp( AI_Cargo, CarrierGroup, From, Event, To, PickupZone ) + self:PickedUp( CarrierGroup, PickupZone ) + end + + + --- Deploy Handler OnAfter for AI_CARGO_DISPATCHER. + -- Use this event handler to tailor the event when a CarrierGroup is routed to a deploy coordinate, to Unload all cargo objects in each CarrierUnit. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterDeploy + -- @param #AI_CARGO_DISPATCHER self + -- @param #string From A string that contains the "*from state name*" when the event was fired. + -- @param #string Event A string that contains the "*event name*" when the event was fired. + -- @param #string To A string that contains the "*to state name*" when the event was fired. + -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. + -- @param Core.Point#COORDINATE Coordinate The deploy coordinate. + -- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the deploy Coordinate. + -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. + + function AI_Cargo.OnAfterDeploy( AI_Cargo, CarrierGroup, From, Event, To, Coordinate, Speed, DeployZone ) + self:Deploy( CarrierGroup, Coordinate, Speed, DeployZone ) end - function AI_Cargo.OnAfterUnload( AI_Cargo, Carrier, From, Event, To, Cargo ) - self:Unloading( Carrier, Cargo ) + + --- Unload Handler OnAfter for AI_CARGO_DISPATCHER. + -- Use this event handler to tailor the event when a CarrierGroup has initiated the unloading or unboarding of cargo. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterUnload + -- @param #AI_CARGO_DISPATCHER self + -- @param #string From A string that contains the "*from state name*" when the event was fired. + -- @param #string Event A string that contains the "*event name*" when the event was fired. + -- @param #string To A string that contains the "*to state name*" when the event was fired. + -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. + -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. + + function AI_Cargo.OnAfterUnload( AI_Cargo, Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone ) + self:Unloading( Carrier, Cargo, CarrierUnit, DeployZone ) end - function AI_Cargo.OnAfterUnloaded( AI_Cargo, Carrier, From, Event, To, Cargo ) - self:Unloaded( Carrier, Cargo ) + --- UnLoading Handler OnAfter for AI_CARGO_DISPATCHER. + -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup is in the process of unloading or unboarding of a cargo object. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- Note that this event is fired repeatedly until all cargo (units) have been unboarded from the CarrierUnit. + -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterUnloading + -- @param #AI_CARGO_DISPATCHER self + -- @param #string From A string that contains the "*from state name*" when the event was fired. + -- @param #string Event A string that contains the "*event name*" when the event was fired. + -- @param #string To A string that contains the "*to state name*" when the event was fired. + -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. + -- @param Cargo.Cargo#CARGO Cargo The cargo object. + -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo unloading operation. + -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. + + function AI_Cargo.OnAfterUnboard( AI_Cargo, CarrierGroup, From, Event, To, Cargo, CarrierUnit, DeployZone ) + self:Unloading( CarrierGroup, Cargo, CarrierUnit, DeployZone ) + end + + + --- Unloaded Handler OnAfter for AI_CARGO_DISPATCHER. + -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup has unloaded a cargo object. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- Note that if more cargo objects were unloading or unboarding from the CarrierUnit, then this event can be fired multiple times for each different Cargo/CarrierUnit. + -- A CarrierUnit can be part of the larger CarrierGroup. + -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterUnloaded + -- @param #AI_CARGO_DISPATCHER self + -- @param #string From A string that contains the "*from state name*" when the event was fired. + -- @param #string Event A string that contains the "*event name*" when the event was fired. + -- @param #string To A string that contains the "*to state name*" when the event was fired. + -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. + -- @param Cargo.Cargo#CARGO Cargo The cargo object. + -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo unloading operation. + -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. + + function AI_Cargo.OnAfterUnloaded( AI_Cargo, Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone ) + self:Unloaded( Carrier, Cargo, CarrierUnit, DeployZone ) + end + + --- Deployed Handler OnAfter for AI_CARGO_DISPATCHER. + -- Use this event handler to tailor the event when a carrier has deployed all cargo objects from the CarrierGroup. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterDeployed + -- @param #AI_CARGO_DISPATCHER self + -- @param #string From A string that contains the "*from state name*" when the event was fired. + -- @param #string Event A string that contains the "*event name*" when the event was fired. + -- @param #string To A string that contains the "*to state name*" when the event was fired. + -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. + -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. + + function AI_Cargo.OnAfterDeployed( AI_Cargo, Carrier, From, Event, To, DeployZone ) + self:Deployed( Carrier, DeployZone ) end end @@ -410,6 +819,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() -- now find the first cargo that is Unloaded local PickupCargo = nil + local PickupZone = nil for CargoName, Cargo in UTILS.spairs( self.SetCargo:GetSet(), function( t, a, b ) return t[a]:GetWeight() < t[b]:GetWeight() end ) do local Cargo = Cargo -- Cargo.Cargo#CARGO @@ -417,33 +827,36 @@ function AI_CARGO_DISPATCHER:onafterMonitor() if Cargo:IsUnLoaded() == true and Cargo:IsDeployed() == false then local CargoCoordinate = Cargo:GetCoordinate() local CoordinateFree = true - for CarrierPickup, Coordinate in pairs( self.PickupCargo ) do - if CarrierPickup:IsAlive() == true then - if CargoCoordinate:Get2DDistance( Coordinate ) <= 25 then - CoordinateFree = false + PickupZone = self.PickupZoneSet and self.PickupZoneSet:IsCoordinateInZone( CargoCoordinate ) + if not self.PickupZoneSet or PickupZone then + for CarrierPickup, Coordinate in pairs( self.PickupCargo ) do + if CarrierPickup:IsAlive() == true then + if CargoCoordinate:Get2DDistance( Coordinate ) <= 25 then + CoordinateFree = false + break + end + else + self.PickupCargo[CarrierPickup] = nil + end + end + if CoordinateFree == true then + -- Check if this cargo can be picked-up by at least one carrier unit of AI_Cargo. + local LargestLoadCapacity = 0 + for _, Carrier in pairs( Carrier:GetUnits() ) do + local LoadCapacity = Carrier:GetCargoBayFreeWeight() + if LargestLoadCapacity < LoadCapacity then + LargestLoadCapacity = LoadCapacity + end + end + -- So if there is aa carrier that has the required load capacity to load the total weight of the cargo, dispatch the carrier. + -- Otherwise break and go to the next carrier. + -- This will skip cargo which is too large to be able to be loaded by carriers + -- and will secure an efficient dispatching scheme. + if LargestLoadCapacity >= Cargo:GetWeight() then + self.PickupCargo[Carrier] = CargoCoordinate + PickupCargo = Cargo break end - else - self.PickupCargo[CarrierPickup] = nil - end - end - if CoordinateFree == true then - -- Check if this cargo can be picked-up by at least one carrier unit of AI_Cargo. - local LargestLoadCapacity = 0 - for _, Carrier in pairs( Carrier:GetUnits() ) do - local LoadCapacity = Carrier:GetCargoBayFreeWeight() - if LargestLoadCapacity < LoadCapacity then - LargestLoadCapacity = LoadCapacity - end - end - -- So if there is aa carrier that has the required load capacity to load the total weight of the cargo, dispatch the carrier. - -- Otherwise break and go to the next carrier. - -- This will skip cargo which is too large to be able to be loaded by carriers - -- and will secure an efficient dispatching scheme. - if LargestLoadCapacity >= Cargo:GetWeight() then - self.PickupCargo[Carrier] = CargoCoordinate - PickupCargo = Cargo - break end end end @@ -452,16 +865,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() if PickupCargo then self.CarrierHome[Carrier] = nil local PickupCoordinate = PickupCargo:GetCoordinate():GetRandomCoordinateInRadius( self.PickupOuterRadius, self.PickupInnerRadius ) - - if self.PickupAirbasesSet then - -- Find airbase within 2km from the cargo with the set. - local PickupAirbase = self.PickupAirbasesSet:FindAirbaseInRange( PickupCoordinate, 4000 ) - if PickupAirbase then - AI_Cargo:Pickup( PickupAirbase, math.random( self.PickupMinSpeed, self.PickupMaxSpeed ) ) - end - else - AI_Cargo:Pickup( PickupCoordinate, math.random( self.PickupMinSpeed, self.PickupMaxSpeed ) ) - end + AI_Cargo:Pickup( PickupCoordinate, math.random( self.PickupMinSpeed, self.PickupMaxSpeed ), PickupZone ) break else if self.HomeZone then @@ -564,20 +968,12 @@ end -- @return #AI_CARGO_DISPATCHER function AI_CARGO_DISPATCHER:OnAfterLoaded( From, Event, To, Carrier, Cargo ) - if self.DeployZonesSet then - - local DeployZone = self.DeployZonesSet:GetRandomZone() - - local DeployCoordinate = DeployZone:GetCoordinate():GetRandomCoordinateInRadius( self.DeployOuterRadius, self.DeployInnerRadius ) - self.AI_Cargo[Carrier]:Deploy( DeployCoordinate, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ) ) - - end - - if self.DeployAirbasesSet then - + if self.DeployZoneSet then if self.AI_Cargo[Carrier]:IsTransporting() == true then - local DeployAirbase = self.DeployAirbasesSet:GetRandomAirbase() - self.AI_Cargo[Carrier]:Deploy( DeployAirbase, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ) ) + local DeployZone = self.DeployZoneSet:GetRandomZone() + + local DeployCoordinate = DeployZone:GetCoordinate():GetRandomCoordinateInRadius( self.DeployOuterRadius, self.DeployInnerRadius ) + self.AI_Cargo[Carrier]:Deploy( DeployCoordinate, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ), DeployZone ) end end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index 1612ce2c7..0f63f2f94 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -28,9 +28,18 @@ --- A dynamic cargo transportation capability for AI groups. -- -- Armoured Personnel APCs (APC), Trucks, Jeeps and other carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. --- The AI\_CARGO\_DISPATCHER\_APC module uses the @{Cargo} capabilities within the MOOSE framework. --- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER\_APC object recognize the cargo. --- Please consult the @{Cargo} module for more information. +-- +-- The AI_CARGO_DISPATCHER_APC module is derived from the AI_CARGO_DISPATCHER module. +-- +-- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_APC class, it is recommended that you +-- ** first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} !!!** +-- +-- Especially to learn how to **Tailor the different cargo handling events**, this will be very useful! +-- +-- On top, the AI_CARGO_DISPATCHER_APC class uses the @{Cargo} capabilities within the MOOSE framework. +-- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class. +-- CARGO derived objects must be declared within the mission to make the AI_CARGO_DISPATCHER_HELICOPTER object recognize the cargo. +-- -- -- ## 1. AI\_CARGO\_DISPATCHER\_APC constructor -- @@ -88,22 +97,23 @@ AI_CARGO_DISPATCHER_APC = { --- Creates a new AI_CARGO_DISPATCHER_APC object. -- @param #AI_CARGO_DISPATCHER_APC self --- @param Core.Set#SET_GROUP SetAPC The collection of APC @{Wrapper.Group}s. --- @param Core.Set#SET_CARGO SetCargo The collection of @{Cargo} derived objects. --- @param Core.Set#SET_ZONE SetDeployZone The collection of deploy @{Zone}s, which are used to where the cargo will be deployed by the APCs. +-- @param Core.Set#SET_GROUP APCSet The collection of APC @{Wrapper.Group}s. +-- @param Core.Set#SET_CARGO CargoSet The collection of @{Cargo} derived objects. +-- @param Core.Set#SET_ZONE PickupZoneSet (optional) The collection of pickup @{Zone}s, which are used to where the cargo can be picked up by the APCs. If nil, then cargo can be picked up everywhere. +-- @param Core.Set#SET_ZONE DeployZoneSet The collection of deploy @{Zone}s, which are used to where the cargo will be deployed by the APCs. -- @param DCS#Distance CombatRadius The cargo will be unloaded from the APC and engage the enemy if the enemy is within CombatRadius range. The radius is in meters, the default value is 500 meters. -- @return #AI_CARGO_DISPATCHER_APC -- @usage -- -- -- Create a new cargo dispatcher for the set of APCs, with a combatradius of 500. --- SetAPC = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() --- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() --- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() --- AICargoDispatcher = AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZone, 500 ) +-- APCSet = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() +-- CargoSet = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- DeployZoneSet = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() +-- AICargoDispatcher = AI_CARGO_DISPATCHER_APC:New( APCSet, SCargoSet, nil, DeployZoneSet, 500 ) -- -function AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZone, CombatRadius ) +function AI_CARGO_DISPATCHER_APC:New( APCSet, CargoSet, PickupZoneSet, DeployZoneSet, CombatRadius ) - local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithZones( SetAPC, SetCargo, SetDeployZone ) ) -- #AI_CARGO_DISPATCHER_APC + local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithZones( APCSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) -- #AI_CARGO_DISPATCHER_APC self.CombatRadius = CombatRadius or 500 @@ -116,7 +126,7 @@ function AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZone, CombatRad end -function AI_CARGO_DISPATCHER_APC:AICargo( APC, SetCargo ) +function AI_CARGO_DISPATCHER_APC:AICargo( APC, CargoSet ) - return AI_CARGO_APC:New( APC, SetCargo, self.CombatRadius ) + return AI_CARGO_APC:New( APC, CargoSet, self.CombatRadius ) end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua index 300fef9ae..fc6bde8de 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua @@ -1,5 +1,10 @@ --- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo using Planes. -- +-- **Features:** +-- +-- * The airplanes will fly towards the pickup airbases to pickup the cargo. +-- * The airplanes will fly towards the deploy airbases to deploy the cargo. +-- -- === -- -- ### Author: **FlightControl** @@ -16,9 +21,17 @@ --- Brings a dynamic cargo handling capability for AI groups. -- -- Airplanes can be mobilized to intelligently transport infantry and other cargo within the simulation. --- The AI_CARGO_DISPATCHER_AIRPLANE module uses the @{Cargo} capabilities within the MOOSE framework. --- CARGO derived objects must be declared within the mission to make the AI_CARGO_DISPATCHER_AIRPLANE object recognize the cargo. --- Please consult the @{Cargo} module for more information. +-- +-- The AI_CARGO_DISPATCHER_AIRPLANE module is derived from the AI_CARGO_DISPATCHER module. +-- +-- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_AIRPLANE class, it is recommended that you +-- ** first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} !!!** +-- +-- Especially to learn how to **Tailor the different cargo handling events**, this will be very useful! +-- +-- On top, the AI_CARGO_DISPATCHER_AIRPLANE class uses the @{Cargo} capabilities within the MOOSE framework. +-- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class. +-- CARGO derived objects must be declared within the mission to make the AI_CARGO_DISPATCHER_HELICOPTER object recognize the cargo. -- -- -- @@ -29,23 +42,25 @@ AI_CARGO_DISPATCHER_AIRPLANE = { --- Creates a new AI_CARGO_DISPATCHER_AIRPLANE object. -- @param #AI_CARGO_DISPATCHER_AIRPLANE self --- @param Core.Set#SET_GROUP SetAirplanes Set of cargo transport airplanes. --- @param Core.Set#SET_CARGO SetCargos Set of cargo, which is supposed to be transported. --- @param Core.Set#SET_AIRBASE PickupAirbasesSet Set of airbases where the cargo has to be picked up. --- @param Core.Set#SET_AIRBASE DeployAirbasesSet Set of airbases where the cargo is deployed. Choice for each cargo is random. +-- @param Core.Set#SET_GROUP AirplaneSet Set of cargo transport airplanes. +-- @param Core.Set#SET_CARGO CargoSet Set of cargo, which is supposed to be transported. +-- @param Core.Zone#SET_ZONE PickupZoneSet Set of zone airbases where the cargo has to be picked up. +-- @param Core.Zone#SET_ZONE DeployZoneSet Set of zone airbases where the cargo is deployed. Choice for each cargo is random. -- @return #AI_CARGO_DISPATCHER_AIRPLANE self -- @usage -- -- -- Create a new cargo dispatcher --- SetAirplanes = SET_GROUP:New():FilterPrefixes( "Airplane" ):FilterStart() --- SetCargos = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() --- PickupAirbasesSet = SET_AIRBASE:New() --- DeployAirbasesSet = SET_AIRBASE:New() --- AICargoDispatcher = AI_CARGO_DISPATCHER_AIRPLANE:New( SetAirplanes, SetCargos, PickupAirbasesSet, DeployAirbasesSet ) +-- AirplaneSet = SET_GROUP:New():FilterPrefixes( "Airplane" ):FilterStart() +-- CargoSet = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- PickupZoneSet = SET_AIRBASE:New() +-- DeployZoneSet = SET_AIRBASE:New() +-- PickupZoneSet:AddZone( ZONE_AIRBASE:New( "Gudauta", AIRBASE:FindByName( AIRBASE.Caucasus.Gudauta ), 3000 ) ) +-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( "Sochi", AIRBASE:FindByName( AIRBASE.Caucasus.Sochi_Adler ), 3000 ) ) +-- AICargoDispatcher = AI_CARGO_DISPATCHER_AIRPLANE:New( AirplaneSet, CargoSet, PickupZoneSet, DeployZoneSet ) -- -function AI_CARGO_DISPATCHER_AIRPLANE:New( SetAirplanes, SetCargos, PickupAirbasesSet, DeployAirbasesSet ) +function AI_CARGO_DISPATCHER_AIRPLANE:New( AirplaneSet, CargoSet, PickupZoneSet, DeployZoneSet ) - local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithAirbases( SetAirplanes, SetCargos, PickupAirbasesSet, DeployAirbasesSet ) ) -- #AI_CARGO_DISPATCHER_AIRPLANE + local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithZones( AirplaneSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) -- #AI_CARGO_DISPATCHER_AIRPLANE self:SetDeploySpeed( 200, 150 ) self:SetPickupSpeed( 200, 150 ) @@ -55,7 +70,7 @@ function AI_CARGO_DISPATCHER_AIRPLANE:New( SetAirplanes, SetCargos, PickupAirbas return self end -function AI_CARGO_DISPATCHER_AIRPLANE:AICargo( Airplane, SetCargo ) +function AI_CARGO_DISPATCHER_AIRPLANE:AICargo( Airplane, CargoSet ) - return AI_CARGO_AIRPLANE:New( Airplane, SetCargo ) + return AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index 26b5d9aab..61705d84d 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -1,7 +1,12 @@ --- **AI** -- Models the intelligent transportation of infantry and other cargo using Helicopters. -- --- The @{#AI_CARGO_DISPATCHER_HELICOPTER} classes implements the dynamic dispatching of cargo transportation tasks for helicopters. --- +-- **Features:** +-- +-- * The helicopters will fly towards the pickup locations to pickup the cargo. +-- * The helicopters will fly towards the deploy zones to deploy the cargo. +-- * Precision deployment as well as randomized deployment within the deploy zones are possible. +-- * Helicopters will orbit the deploy zones when there is no space for landing until the deploy zone is free. +-- -- === -- -- ### Author: **FlightControl** @@ -18,9 +23,18 @@ --- A dynamic cargo handling capability for AI helicopter groups. -- -- Helicopters can be mobilized to intelligently transport infantry and other cargo within the simulation. --- The AI\_CARGO\_DISPATCHER\_HELICOPTER module uses the @{Cargo} capabilities within the MOOSE framework. --- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER\_HELICOPTER object recognize the cargo. --- Please consult the @{Cargo} module for more information. +-- +-- +-- The AI_CARGO_DISPATCHER_HELICOPTER module is derived from the AI_CARGO_DISPATCHER module. +-- +-- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_HELICOPTER class, it is recommended that you +-- ** first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} !!!** +-- +-- Especially to learn how to **Tailor the different cargo handling events**, this will be very useful! +-- +-- On top, the AI_CARGO_DISPATCHER_HELICOPTER class uses the @{Cargo} capabilities within the MOOSE framework. +-- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class. +-- CARGO derived objects must be declared within the mission to make the AI_CARGO_DISPATCHER_HELICOPTER object recognize the cargo. -- -- --- -- @@ -88,21 +102,22 @@ AI_CARGO_DISPATCHER_HELICOPTER = { --- Creates a new AI_CARGO_DISPATCHER_HELICOPTER object. -- @param #AI_CARGO_DISPATCHER_HELICOPTER self --- @param Core.Set#SET_GROUP SetHelicopter The collection of Helicopter @{Wrapper.Group}s. --- @param Core.Set#SET_CARGO SetCargo The collection of @{Cargo} derived objects. --- @param Core.Set#SET_ZONE SetDeployZones The collection of deploy @{Zone}s, which are used to where the cargo will be deployed by the Helicopters. +-- @param Core.Set#SET_GROUP HelicopterSet The collection of Helicopter @{Wrapper.Group}s. +-- @param Core.Set#SET_CARGO CargoSet The collection of @{Cargo} derived objects. +-- @param Core.Set#SET_ZONE PickupZoneSet (optional) The collection of pickup @{Zone}s, which are used to where the cargo can be picked up by the APCs. If nil, then cargo can be picked up everywhere. +-- @param Core.Set#SET_ZONE DeployZoneSet The collection of deploy @{Zone}s, which are used to where the cargo will be deployed by the Helicopters. -- @return #AI_CARGO_DISPATCHER_HELICOPTER -- @usage -- -- -- Create a new cargo dispatcher --- SetHelicopter = SET_GROUP:New():FilterPrefixes( "Helicopter" ):FilterStart() --- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() --- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() --- AICargoDispatcher = AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargo ) +-- HelicopterSet = SET_GROUP:New():FilterPrefixes( "Helicopter" ):FilterStart() +-- CargoSet = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- DeployZoneSet = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() +-- AICargoDispatcher = AI_CARGO_DISPATCHER_HELICOPTER:New( HelicopterSet, SetCargo, nil, DeployZoneSet ) -- -function AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargo, SetDeployZones ) +function AI_CARGO_DISPATCHER_HELICOPTER:New( HelicopterSet, CargoSet, PickupZoneSet, DeployZoneSet ) - local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithZones( SetHelicopter, SetCargo, SetDeployZones ) ) -- #AI_CARGO_DISPATCHER_HELICOPTER + local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithZones( HelicopterSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) -- #AI_CARGO_DISPATCHER_HELICOPTER self:SetDeploySpeed( 200, 150 ) self:SetPickupSpeed( 200, 150 ) @@ -112,8 +127,8 @@ function AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargo, SetDeployZ return self end -function AI_CARGO_DISPATCHER_HELICOPTER:AICargo( Helicopter, SetCargo ) +function AI_CARGO_DISPATCHER_HELICOPTER:AICargo( Helicopter, CargoSet ) - return AI_CARGO_HELICOPTER:New( Helicopter, SetCargo ) + return AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) end diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 6c9d5a15e..62cdbf43a 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -46,10 +46,12 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) self:AddTransition( { "Unloaded", "Loading" }, "Load", "Boarding" ) self:AddTransition( "Boarding", "Board", "Boarding" ) - self:AddTransition( "Boarding", "Loaded", "Loaded" ) + self:AddTransition( "Boarding", "Loaded", "Boarding" ) + self:AddTransition( "Boarding", "PickedUp", "Loaded" ) self:AddTransition( "Loaded", "Unload", "Unboarding" ) self:AddTransition( "Unboarding", "Unboard", "Unboarding" ) - self:AddTransition( "Unboarding", "Unloaded", "Unloaded" ) + self:AddTransition( "Unboarding", "Unloaded", "Unboarding" ) + self:AddTransition( "Unboarding", "Deployed", "Unloaded" ) self:AddTransition( "*", "Landed", "*" ) self:AddTransition( "*", "Queue", "*" ) @@ -233,7 +235,7 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To ) if self.RoutePickup == true then if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 10 then --self:Load( Helicopter:GetPointVec2() ) - self:Load() + self:Load( self.PickupZone ) self.RoutePickup = false self.Relocating = true end @@ -241,7 +243,7 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To ) if self.RouteDeploy == true then if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 10 then - self:Unload( true ) + self:Unload( self.DeployZone ) self.RouteDeploy = false self.Transporting = false self.Relocating = false @@ -259,7 +261,7 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate -- @param #number Speed -function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordinate, Speed ) +function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordinate, Speed, DeployZone ) local HelicopterInZone = false @@ -268,7 +270,7 @@ function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordina local Distance = Coordinate:DistanceFromPointVec2( Helicopter:GetCoordinate() ) if Distance > 2000 then - self:__Queue( -10, Coordinate ) + self:__Queue( -10, Coordinate, Speed, DeployZone ) else local ZoneFree = true @@ -317,8 +319,12 @@ function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordina -- Now route the helicopter Helicopter:Route( Route, 0 ) + + -- Keep the DeployZone, because when the helo has landed, we want to provide the DeployZone to the mission designer as part of the Unloaded event. + self.DeployZone = DeployZone + else - self:__Queue( -10, Coordinate ) + self:__Queue( -10, Coordinate, Speed, DeployZone ) end end else @@ -379,39 +385,42 @@ end -- @param #string From From state. -- @param #string Event Event -- @param #string To To state. -function AI_CARGO_HELICOPTER:onbeforeLoad( Helicopter, From, Event, To) +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO_HELICOPTER:onbeforeLoad( Helicopter, From, Event, To, PickupZone ) local Boarding = false + + local LoadInterval = 10 + local LoadDelay = 10 if Helicopter and Helicopter:IsAlive() then self.BoardingCount = 0 - if Helicopter and Helicopter:IsAlive() then - for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do - local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT + for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do + local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT local CargoBayFreeWeight = HelicopterUnit:GetCargoBayFreeWeight() self:F({CargoBayFreeWeight=CargoBayFreeWeight}) for _, Cargo in pairs( self.CargoSet:GetSet() ) do local Cargo = Cargo -- Cargo.Cargo#CARGO - self:F( { IsUnLoaded = Cargo:IsUnLoaded() } ) - if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then - if Cargo:IsInLoadRadius( HelicopterUnit:GetCoordinate() ) then - self:F( { "In radius", HelicopterUnit:GetName() } ) - - local CargoWeight = Cargo:GetWeight() - - -- Only when there is space within the bay to load the next cargo item! - if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then - - --Cargo:Ungroup() - Cargo:Board( HelicopterUnit, 25 ) - self:__Board( 1, Cargo, HelicopterUnit ) - self.Helicopter_Cargo[HelicopterUnit] = Cargo - Boarding = true - break - end + self:F( { IsUnLoaded = Cargo:IsUnLoaded() } ) + if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then + if Cargo:IsInLoadRadius( HelicopterUnit:GetCoordinate() ) then + self:F( { "In radius", HelicopterUnit:GetName() } ) + + local CargoWeight = Cargo:GetWeight() + + -- Only when there is space within the bay to load the next cargo item! + if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then + + --Cargo:Ungroup() + Cargo:__Board( LoadDelay, HelicopterUnit, 25 ) + LoadDelay = LoadDelay + LoadInterval + self:__Board( LoadDelay, Cargo, HelicopterUnit, PickupZone ) + self.Helicopter_Cargo[HelicopterUnit] = Cargo + Boarding = true + CargoBayFreeWeight = CargoBayFreeWeight - CargoWeight end end end @@ -431,47 +440,32 @@ end -- @param #string To To state. -- @param Cargo.Cargo#CARGO Cargo Cargo object. -- @param Wrapper.Unit#UNIT HelicopterUnit -function AI_CARGO_HELICOPTER:onafterBoard( Helicopter, From, Event, To, Cargo, HelicopterUnit ) +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO_HELICOPTER:onafterBoard( Helicopter, From, Event, To, Cargo, HelicopterUnit, PickupZone ) self:F( { Helicopter, From, Event, To, Cargo, HelicopterUnit } ) if Helicopter and Helicopter:IsAlive() then self:F({ IsLoaded = Cargo:IsLoaded() } ) if not Cargo:IsLoaded() then - self:__Board( 10, Cargo, HelicopterUnit ) - else - local CargoBayFreeWeight = HelicopterUnit:GetCargoBayFreeWeight() - self:F({CargoBayFreeWeight=CargoBayFreeWeight}) - for _, Cargo in pairs( self.CargoSet:GetSet() ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - if Cargo:IsUnLoaded() then - if Cargo:IsInLoadRadius( HelicopterUnit:GetCoordinate() ) then - local CargoWeight = Cargo:GetWeight() - - -- Only when there is space within the bay to load the next cargo item! - if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then - Cargo:Board( HelicopterUnit, 25 ) - self:__Board( 10, Cargo, HelicopterUnit ) - self.Helicopter_Cargo[HelicopterUnit] = Cargo - return - end - end - end - end - self:__Loaded( 1, Cargo ) -- Will only be executed when no more cargo is boarded. + self:__Board( 10, Cargo, HelicopterUnit, PickupZone ) + return end end + + self:__Loaded( 10, Cargo, HelicopterUnit, PickupZone ) -- Will only be executed when no more cargo is boarded. end ---- On before Loaded event. +--- On After Loaded event. -- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @return #boolean Cargo loaded. -function AI_CARGO_HELICOPTER:onbeforeLoaded( Helicopter, From, Event, To, Cargo ) +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO_HELICOPTER:onafterLoaded( Helicopter, From, Event, To, Cargo, HelicopterUnit, PickupZone ) self:F( { Helicopter, From, Event, To } ) local Loaded = true @@ -486,14 +480,16 @@ function AI_CARGO_HELICOPTER:onbeforeLoaded( Helicopter, From, Event, To, Cargo end end - return Loaded + if Loaded then + self:PickedUp( PickupZone ) + end end ---- On after Loaded event. Check if cargo is loaded. +--- On after PickedUp event, raised when all cargo has been loaded into the CarrierGroup. -- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -- @param #string From From state. @@ -501,8 +497,9 @@ end -- @param #string To To state. -- @param Cargo.Cargo#CARGO Cargo Cargo object. -- @return #boolean Cargo is loaded. -function AI_CARGO_HELICOPTER:onafterLoaded( Helicopter, From, Event, To, Cargo ) - self:F( { Helicopter, From, Event, To, Cargo } ) +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO_HELICOPTER:onafterPickedUp( Helicopter, From, Event, To, PickupZone ) + self:F( { Helicopter, From, Event, To } ) if Helicopter and Helicopter:IsAlive() then self.Transporting = true @@ -516,16 +513,20 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #boolean Deployed Cargo is deployed. -function AI_CARGO_HELICOPTER:onafterUnload( Helicopter, From, Event, To, Deployed ) +function AI_CARGO_HELICOPTER:onafterUnload( Helicopter, From, Event, To, DeployZone ) + + local UnboardInterval = 10 + local UnboardDelay = 10 if Helicopter and Helicopter:IsAlive() then for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT for _, Cargo in pairs( HelicopterUnit:GetCargo() ) do if Cargo:IsLoaded() then - Cargo:UnBoard() + Cargo:__UnBoard( UnboardDelay ) + UnboardDelay = UnboardDelay + UnboardInterval Cargo:SetDeployed( true ) - self:__Unboard( 10, Cargo, Deployed ) + self:__Unboard( UnboardDelay, Cargo, HelicopterUnit, DeployZone ) end end end @@ -542,30 +543,20 @@ end -- @param #string To To state. -- @param Cargo.Cargo#CARGO Cargo Cargo object. -- @param #boolean Deployed Cargo is deployed. -function AI_CARGO_HELICOPTER:onafterUnboard( Helicopter, From, Event, To, Cargo, Deployed ) +function AI_CARGO_HELICOPTER:onafterUnboard( Helicopter, From, Event, To, Cargo, HelicopterUnit, DeployZone ) if Helicopter and Helicopter:IsAlive() then if not Cargo:IsUnLoaded() then - self:__Unboard( 10, Cargo, Deployed ) - else - for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do - local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT - for _, Cargo in pairs( HelicopterUnit:GetCargo() ) do - if Cargo:IsLoaded() then - Cargo:UnBoard() - Cargo:SetDeployed( true ) - self:__Unboard( 10, Cargo, Deployed ) - return - end - end - end - self:__Unloaded( 1, Cargo, Deployed ) + self:__Unboard( 10, Cargo, HelicopterUnit, DeployZone ) + return end end + + self:Unloaded( Cargo, HelicopterUnit, DeployZone ) end ---- On before Unloaded event. +--- On after Unloaded event. -- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -- @param #string From From state. @@ -574,8 +565,8 @@ end -- @param Cargo.Cargo#CARGO Cargo Cargo object. -- @param #boolean Deployed Cargo is deployed. -- @return #boolean True if all cargo has been unloaded. -function AI_CARGO_HELICOPTER:onbeforeUnloaded( Helicopter, From, Event, To, Cargo, Deployed ) - self:F( { APC, From, Event, To, Cargo:GetName(), Deployed = Deployed } ) +function AI_CARGO_HELICOPTER:onafterUnloaded( Helicopter, From, Event, To, Cargo, HelicopterUnit, DeployZone ) + self:F( { Helicopter, From, Event, To, Cargo:GetName(), HelicopterUnit:GetName(), DeployZone = DeployZone } ) local AllUnloaded = true @@ -592,19 +583,21 @@ function AI_CARGO_HELICOPTER:onbeforeUnloaded( Helicopter, From, Event, To, Carg end if AllUnloaded == true then - if Deployed == true then + if DeployZone then self.Helicopter_Cargo = {} end self.Helicopter = Helicopter end end - self:F( { AllUnloaded = AllUnloaded } ) - return AllUnloaded + if AllUnloaded == true then + self:Deployed( DeployZone ) + end end ---- On after Unloaded event. + +--- On after Deployed event. -- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -- @param #string From From state. @@ -612,7 +605,9 @@ end -- @param #string To To state. -- @param Cargo.Cargo#CARGO Cargo Cargo object. -- @param #boolean Deployed Cargo is deployed. -function AI_CARGO_HELICOPTER:onafterUnloaded( Helicopter, From, Event, To, Cargo, Deployed ) +-- @return #boolean True if all cargo has been unloaded. +function AI_CARGO_HELICOPTER:onafterDeployed( Helicopter, From, Event, To, DeployZone ) + self:F( { Helicopter, From, Event, To, DeployZone = DeployZone } ) self:Orbit( Helicopter:GetCoordinate(), 50 ) @@ -622,7 +617,7 @@ function AI_CARGO_HELICOPTER:onafterUnloaded( Helicopter, From, Event, To, Cargo AI_CARGO_QUEUE[Helicopter] = nil end, Helicopter ) - + end --- On after Pickup event. @@ -633,7 +628,8 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate Pickup place. -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. -function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordinate, Speed ) +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordinate, Speed, PickupZone ) if Helicopter and Helicopter:IsAlive() ~= nil then @@ -683,7 +679,8 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin -- Now route the helicopter Helicopter:Route( Route, 1 ) - + + self.PickupZone = PickupZone self.Transporting = true end @@ -693,8 +690,8 @@ end -- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP AICargoHelicopter -- @param Core.Point#COORDINATE Coordinate Coordinate -function AI_CARGO_HELICOPTER:_Deploy( AICargoHelicopter, Coordinate ) - AICargoHelicopter:__Queue( -10, Coordinate, 100 ) +function AI_CARGO_HELICOPTER:_Deploy( AICargoHelicopter, Coordinate, DeployZone ) + AICargoHelicopter:__Queue( -10, Coordinate, 100, DeployZone ) end --- On after Deploy event. @@ -705,7 +702,7 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate Place at which the cargo is deployed. -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. -function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordinate, Speed ) +function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordinate, Speed, DeployZone ) if Helicopter and Helicopter:IsAlive() ~= nil then @@ -750,7 +747,7 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin local Tasks = {} - Tasks[#Tasks+1] = Helicopter:TaskFunction( "AI_CARGO_HELICOPTER._Deploy", self, Coordinate ) + Tasks[#Tasks+1] = Helicopter:TaskFunction( "AI_CARGO_HELICOPTER._Deploy", self, Coordinate, DeployZone ) Tasks[#Tasks+1] = Helicopter:TaskOrbitCircle( math.random( 30, 100 ), _speed, CoordinateTo:GetRandomCoordinateInRadius( 800, 500 ) ) --Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() ) diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 63f9203d1..ade21edd0 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -333,7 +333,7 @@ do -- CARGO_GROUP -- For each Cargo object within the CARGO_GROUP, route each object to the CargoLoadPointVec2 for CargoID, Cargo in pairs( self.CargoSet:GetSet() ) do - self:T( { Cargo:GetName(), Cargo.current } ) + --self:T( { Cargo:GetName(), Cargo.current } ) if not Cargo:is( "Loaded" ) @@ -355,7 +355,7 @@ do -- CARGO_GROUP if not Cancelled then if not Boarded then - self:__Boarding( 1, CargoCarrier, NearRadius, ... ) + self:__Boarding( -5, CargoCarrier, NearRadius, ... ) else self:F("Group Cargo is loaded") self:__Load( 1, CargoCarrier, ... ) diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index 621a9a3a9..68e77ac61 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -285,15 +285,16 @@ do -- CARGO_UNIT -- @param Wrapper.Client#CLIENT CargoCarrier -- @param #number NearRadius Default 25 m. function CARGO_UNIT:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) - --self:F( { From, Event, To, CargoCarrier.UnitName, NearRadius } ) + self:F( { From, Event, To, CargoCarrier:GetName() } ) if CargoCarrier and CargoCarrier:IsAlive() and self.CargoObject and self.CargoObject:IsAlive() then - if CargoCarrier:InAir() == false then + if (CargoCarrier:IsAir() and not CargoCarrier:InAir()) or true then + local NearRadius = CargoCarrier:GetBoundingRadius( NearRadius ) + 5 if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then self:__Load( 1, CargoCarrier, ... ) else - self:__Boarding( -1, CargoCarrier, NearRadius, ... ) + self:__Boarding( -5, CargoCarrier, NearRadius, ... ) self.RunCount = self.RunCount + 1 if self.RunCount >= 40 then self.RunCount = 0 diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 2b9c1c8b5..650c22323 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -4778,7 +4778,7 @@ function SET_ZONE:New() return self end ---- Add ZONEs to SET_ZONE. +--- Add ZONEs by a search name to SET_ZONE. -- @param Core.Set#SET_ZONE self -- @param #string AddZoneNames A single name or an array of ZONE_BASE names. -- @return self @@ -4793,6 +4793,18 @@ function SET_ZONE:AddZonesByName( AddZoneNames ) return self end +--- Add ZONEs to SET_ZONE. +-- @param Core.Set#SET_ZONE self +-- @param Core.Zone#ZONE_BASE Zone A ZONE_BASE object. +-- @return self +function SET_ZONE:AddZone( Zone ) + + self:Add( Zone:GetName(), Zone ) + + return self +end + + --- Remove ZONEs from SET_ZONE. -- @param Core.Set#SET_ZONE self -- @param Core.Zone#ZONE_BASE RemoveZoneNames A single name or an array of ZONE_BASE names. @@ -5003,3 +5015,22 @@ function SET_ZONE:OnEventDeleteZone( EventData ) --R2.1 end end end + +--- Validate if a coordinate is in one of the zones in the set. +-- Returns the ZONE object where the coordiante is located. +-- If zones overlap, the first zone that validates the test is returned. +-- @param #SET_ZONE self +-- @param Core.Point#COORDINATE Coordinate The coordinate to be searched. +-- @return Core.Zone#ZONE_BASE The zone that validates the coordinate location. +-- @return #nil No zone has been found. +function SET_ZONE:IsCoordinateInZone( Coordinate ) + + for _, Zone in pairs( self:GetSet() ) do + local Zone = Zone -- Core.Zone#ZONE_BASE + if Zone:IsCoordinateInZone( Coordinate ) then + return Zone + end + end + + return nil +end diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index fd85bb5ee..37ce7ec6f 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -1591,4 +1591,103 @@ function ZONE_POLYGON:FindByName( ZoneName ) return ZoneFound end +do -- ZONE_AIRBASE + --- @type ZONE_AIRBASE + -- @extends #ZONE_RADIUS + + + --- The ZONE_AIRBASE class defines by a zone around a @{Wrapper.Airbase#AIRBASE} with a radius. + -- This class implements the inherited functions from @{Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties. + -- + -- @field #ZONE_AIRBASE + ZONE_AIRBASE = { + ClassName="ZONE_AIRBASE", + } + + + + --- Constructor to create a ZONE_AIRBASE instance, taking the zone name, a zone @{Wrapper.Airbase#AIRBASE} and a radius. + -- @param #ZONE_AIRBASE self + -- @param #string ZoneName Name of the zone. + -- @param Wrapper.Airbase#AIRBASE ZoneAirbase The @{Wrapper.Airbase} as the center of the zone. + -- @param DCS#Distance Radius The radius of the zone. + -- @return #ZONE_AIRBASE self + function ZONE_AIRBASE:New( AirbaseName ) + + + local Airbase = AIRBASE:FindByName( AirbaseName ) + + local self = BASE:Inherit( self, ZONE_RADIUS:New( AirbaseName, Airbase:GetVec2(), 4000 ) ) + + self._.ZoneAirbase = Airbase + self._.ZoneVec2Cache = self._.ZoneAirbase:GetVec2() + + -- Zone objects are added to the _DATABASE and SET_ZONE objects. + _EVENTDISPATCHER:CreateEventNewZone( self ) + + return self + end + + --- Get the airbase as part of the ZONE_AIRBASE object. + -- @param #ZONE_AIRBASE self + -- @return Wrapper.Airbase#AIRBASE The airbase. + function ZONE_AIRBASE:GetAirbase() + return self._.ZoneAirbase + end + + --- Returns the current location of the @{Wrapper.Group}. + -- @param #ZONE_AIRBASE self + -- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Group} location. + function ZONE_AIRBASE:GetVec2() + self:F( self.ZoneName ) + + local ZoneVec2 = nil + + if self._.ZoneAirbase:IsAlive() then + ZoneVec2 = self._.ZoneAirbase:GetVec2() + self._.ZoneVec2Cache = ZoneVec2 + else + ZoneVec2 = self._.ZoneVec2Cache + end + + self:T( { ZoneVec2 } ) + + return ZoneVec2 + end + + --- Returns a random location within the zone of the @{Wrapper.Group}. + -- @param #ZONE_AIRBASE self + -- @return DCS#Vec2 The random location of the zone based on the @{Wrapper.Group} location. + function ZONE_AIRBASE:GetRandomVec2() + self:F( self.ZoneName ) + + local Point = {} + local Vec2 = self._.ZoneAirbase:GetVec2() + + local angle = math.random() * math.pi*2; + Point.x = Vec2.x + math.cos( angle ) * math.random() * self:GetRadius(); + Point.y = Vec2.y + math.sin( angle ) * math.random() * self:GetRadius(); + + self:T( { Point } ) + + return Point + end + + --- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. + -- @param #ZONE_AIRBASE self + -- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. + -- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone. + -- @return Core.Point#POINT_VEC2 The @{Core.Point#POINT_VEC2} object reflecting the random 3D location within the zone. + function ZONE_AIRBASE:GetRandomPointVec2( inner, outer ) + self:F( self.ZoneName, inner, outer ) + + local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2() ) + + self:T3( { PointVec2 } ) + + return PointVec2 + end + + +end From 31fba973e5465dbb559f7a61fd46e6db3083b6b9 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Wed, 5 Sep 2018 06:59:22 +0200 Subject: [PATCH 312/420] Progress --- Moose Development/Moose/AI/AI_Cargo.lua | 348 +---------------- Moose Development/Moose/AI/AI_Cargo_APC.lua | 399 +------------------- Moose Setup/Moose.files | 1 + 3 files changed, 11 insertions(+), 737 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua index 3359a425e..d2ba6f494 100644 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -83,12 +83,12 @@ AI_CARGO = { -- @param Core.Set#SET_CARGO CargoSet -- @param #number CombatRadius -- @return #AI_CARGO -function AI_CARGO:New( Carrier, CargoSet, CombatRadius ) +function AI_CARGO:New( Carrier, CargoSet ) - local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_CARGO + local self = BASE:Inherit( self, FSM_CONTROLLABLE:New( Carrier ) ) -- #AI_CARGO self.CargoSet = CargoSet -- Core.Set#SET_CARGO - self.CombatRadius = CombatRadius + self.CargoCarrier = Carrier -- Wrapper.Group#GROUP self:SetStartState( "Unloaded" ) @@ -105,14 +105,6 @@ function AI_CARGO:New( Carrier, CargoSet, CombatRadius ) self:AddTransition( "Unboarding", "Unloaded", "Unboarding" ) self:AddTransition( "Unboarding", "Deployed", "Unloaded" ) - self:AddTransition( "*", "Monitor", "*" ) - self:AddTransition( "*", "Follow", "Following" ) - self:AddTransition( "*", "Guard", "Unloaded" ) - self:AddTransition( "*", "Home", "*" ) - - self:AddTransition( "*", "Destroyed", "Destroyed" ) - - --- Pickup Handler OnBefore for AI_CARGO -- @function [parent=#AI_CARGO] OnBeforePickup -- @param #AI_CARGO self @@ -194,11 +186,6 @@ function AI_CARGO:New( Carrier, CargoSet, CombatRadius ) -- @param #string Event -- @param #string To - - self:__Monitor( 1 ) - - self:SetCarrier( Carrier ) - for _, CarrierUnit in pairs( Carrier:GetUnits() ) do CarrierUnit:SetCargoBayWeightLimit() end @@ -210,53 +197,6 @@ function AI_CARGO:New( Carrier, CargoSet, CombatRadius ) end ---- Set the Carrier. --- @param #AI_CARGO self --- @param Wrapper.Group#GROUP CargoCarrier --- @return #AI_CARGO -function AI_CARGO:SetCarrier( CargoCarrier ) - - self.CargoCarrier = CargoCarrier -- Wrapper.Group#GROUP - self.CargoCarrier:SetState( self.CargoCarrier, "AI_CARGO", self ) - - CargoCarrier:HandleEvent( EVENTS.Dead ) - CargoCarrier:HandleEvent( EVENTS.Hit ) - - function CargoCarrier:OnEventDead( EventData ) - self:F({"dead"}) - local AICargoTroops = self:GetState( self, "AI_CARGO" ) - self:F({AICargoTroops=AICargoTroops}) - if AICargoTroops then - self:F({}) - if not AICargoTroops:Is( "Loaded" ) then - -- There are enemies within combat range. Unload the CargoCarrier. - AICargoTroops:Destroyed() - end - end - end - - function CargoCarrier:OnEventHit( EventData ) - self:F({"hit"}) - local AICargoTroops = self:GetState( self, "AI_CARGO" ) - if AICargoTroops then - self:F( { OnHitLoaded = AICargoTroops:Is( "Loaded" ) } ) - if AICargoTroops:Is( "Loaded" ) or AICargoTroops:Is( "Boarding" ) then - -- There are enemies within combat range. Unload the CargoCarrier. - AICargoTroops:Unload( false ) - end - end - end - - self.Zone = ZONE_UNIT:New( self.CargoCarrier:GetName() .. "-Zone", self.CargoCarrier, self.CombatRadius ) - self.Coalition = self.CargoCarrier:GetCoalition() - - self:SetControllable( CargoCarrier ) - - self:Guard() - - return self -end - function AI_CARGO:IsTransporting() @@ -268,144 +208,6 @@ function AI_CARGO:IsRelocating() return self.Relocating == true end ---- Find a free Carrier within a range. --- @param #AI_CARGO self --- @param Core.Point#COORDINATE Coordinate --- @param #number Radius --- @return Wrapper.Group#GROUP NewCarrier -function AI_CARGO:FindCarrier( Coordinate, Radius ) - - local CoordinateZone = ZONE_RADIUS:New( "Zone" , Coordinate:GetVec2(), Radius ) - CoordinateZone:Scan( { Object.Category.UNIT } ) - for _, DCSUnit in pairs( CoordinateZone:GetScannedUnits() ) do - local NearUnit = UNIT:Find( DCSUnit ) - self:F({NearUnit=NearUnit}) - if not NearUnit:GetState( NearUnit, "AI_CARGO" ) then - local Attributes = NearUnit:GetDesc() - self:F({Desc=Attributes}) - if NearUnit:HasAttribute( "Trucks" ) then - return NearUnit:GetGroup() - end - end - end - - return nil - -end - - - ---- Follow Infantry to the Carrier. --- @param #AI_CARGO self --- @param #AI_CARGO Me --- @param Wrapper.Unit#UNIT CarrierUnit --- @param Cargo.CargoGroup#CARGO_GROUP Cargo --- @return #AI_CARGO -function AI_CARGO:FollowToCarrier( Me, CarrierUnit, CargoGroup ) - - local InfantryGroup = CargoGroup:GetGroup() - - self:F( { self = self:GetClassNameAndID(), InfantryGroup = InfantryGroup:GetName() } ) - - --if self:Is( "Following" ) then - - if CarrierUnit:IsAlive() then - -- We check if the Cargo is near to the CargoCarrier. - if InfantryGroup:IsPartlyInZone( ZONE_UNIT:New( "Radius", CarrierUnit, 25 ) ) then - - -- The Cargo does not need to follow the Carrier. - Me:Guard() - - else - - self:F( { InfantryGroup = InfantryGroup:GetName() } ) - - if InfantryGroup:IsAlive() then - - self:F( { InfantryGroup = InfantryGroup:GetName() } ) - - local Waypoints = {} - - -- Calculate the new Route. - local FromCoord = InfantryGroup:GetCoordinate() - local FromGround = FromCoord:WaypointGround( 10, "Diamond" ) - self:F({FromGround=FromGround}) - table.insert( Waypoints, FromGround ) - - local ToCoord = CarrierUnit:GetCoordinate():GetRandomCoordinateInRadius( 10, 5 ) - local ToGround = ToCoord:WaypointGround( 10, "Diamond" ) - self:F({ToGround=ToGround}) - table.insert( Waypoints, ToGround ) - - local TaskRoute = InfantryGroup:TaskFunction( "AI_CARGO.FollowToCarrier", Me, CarrierUnit, CargoGroup ) - - self:F({Waypoints = Waypoints}) - local Waypoint = Waypoints[#Waypoints] - InfantryGroup:SetTaskWaypoint( Waypoint, TaskRoute ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone. - - InfantryGroup:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. - end - end - end -end - - ---- On after Monitor event. --- @param #AI_CARGO self --- @param Wrapper.Group#GROUP Carrier --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function AI_CARGO:onafterMonitor( Carrier, From, Event, To ) - self:F( { Carrier, From, Event, To } ) - - if Carrier and Carrier:IsAlive() then - if self.CarrierCoordinate then - if self:IsRelocating() == true then - local Coordinate = Carrier:GetCoordinate() - self.Zone:Scan( { Object.Category.UNIT } ) - if self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then - if self:Is( "Unloaded" ) or self:Is( "Following" ) then - -- There are no enemies within combat range. Load the CargoCarrier. - self:Load() - end - else - if self:Is( "Loaded" ) then - -- There are enemies within combat range. Unload the CargoCarrier. - self:__Unload( 1 ) - else - if self:Is( "Unloaded" ) then - self:Follow() - end - if self:Is( "Following" ) then - for Cargo, CarrierUnit in pairs( self.Carrier_Cargo ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - if Cargo:IsAlive() then - if not Cargo:IsNear( CarrierUnit, 40 ) then - CarrierUnit:RouteStop() - self.CarrierStopped = true - else - if self.CarrierStopped then - if Cargo:IsNear( CarrierUnit, 25 ) then - CarrierUnit:RouteResume() - self.CarrierStopped = nil - end - end - end - end - end - end - end - end - end - - end - self.CarrierCoordinate = Carrier:GetCoordinate() - end - - self:__Monitor( -5 ) - -end --- On before Load event. @@ -489,7 +291,6 @@ function AI_CARGO:onbeforeLoad( Carrier, From, Event, To, PickupZone ) if not Loaded then -- If the cargo wasn't loaded in one of the carriers, then we need to stop the loading. - break end end @@ -678,146 +479,3 @@ function AI_CARGO:onafterDeployed( Carrier, From, Event, To, DeployZone ) end ---- On after Follow event. --- @param #AI_CARGO self --- @param Wrapper.Group#GROUP Carrier --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function AI_CARGO:onafterFollow( Carrier, From, Event, To ) - self:F( { Carrier, From, Event, To } ) - - self:F( "Follow" ) - if Carrier and Carrier:IsAlive() then - for Cargo, CarrierUnit in pairs( self.Carrier_Cargo ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - if Cargo:IsUnLoaded() then - self:FollowToCarrier( self, CarrierUnit, Cargo ) - CarrierUnit:RouteResume() - end - end - end - -end - - ---- @param #AI_CARGO --- @param Wrapper.Group#GROUP Carrier -function AI_CARGO._Pickup( Carrier, self, PickupZone ) - - Carrier:F( { "AI_CARGO._Pickup:", Carrier:GetName() } ) - - if Carrier:IsAlive() then - self:Load( PickupZone) - end -end - - -function AI_CARGO._Deploy( Carrier, self, Coordinate, DeployZone ) - - Carrier:F( { "AI_CARGO._Deploy:", Carrier } ) - - if Carrier:IsAlive() then - self:Unload( DeployZone ) - end -end - - - ---- On after Pickup event. --- @param #AI_CARGO self --- @param Wrapper.Group#GROUP Carrier --- @param From --- @param Event --- @param To --- @param Core.Point#COORDINATE Coordinate of the pickup point. --- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. --- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. -function AI_CARGO:onafterPickup( Carrier, From, Event, To, Coordinate, Speed, PickupZone ) - - if Carrier and Carrier:IsAlive() then - - if Coordinate then - self.RoutePickup = true - - local _speed=Speed or Carrier:GetSpeedMax()*0.5 - - local Waypoints = Carrier:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true ) - - local TaskFunction = Carrier:TaskFunction( "AI_CARGO._Pickup", self, PickupZone ) - - self:F({Waypoints = Waypoints}) - local Waypoint = Waypoints[#Waypoints] - Carrier:SetTaskWaypoint( Waypoint, TaskFunction ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone. - - Carrier:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. - else - AI_CARGO._Pickup( Carrier, self, PickupZone ) - end - - self.Relocating = true - self.Transporting = false - end - -end - - ---- On after Deploy event. --- @param #AI_CARGO self --- @param Wrapper.Group#GROUP Carrier --- @param From --- @param Event --- @param To --- @param Core.Point#COORDINATE Coordinate Deploy place. --- @param #number Speed Speed in km/h to drive to the depoly coordinate. Default is 50% of max possible speed the unit can go. -function AI_CARGO:onafterDeploy( Carrier, From, Event, To, Coordinate, Speed, DeployZone ) - - if Carrier and Carrier:IsAlive() then - - self.RouteDeploy = true - - local _speed=Speed or Carrier:GetSpeedMax()*0.5 - - local Waypoints = Carrier:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true ) - - local TaskFunction = Carrier:TaskFunction( "AI_CARGO._Deploy", self, Coordinate, DeployZone ) - - self:F({Waypoints = Waypoints}) - local Waypoint = Waypoints[#Waypoints] - Carrier:SetTaskWaypoint( Waypoint, TaskFunction ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone. - - Carrier:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. - - self.Relocating = false - self.Transporting = true - end - -end - - ---- On after Home event. --- @param #AI_CARGO self --- @param Wrapper.Group#GROUP Carrier --- @param From --- @param Event --- @param To --- @param Core.Point#COORDINATE Coordinate Home place. --- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. -function AI_CARGO:onafterHome( Carrier, From, Event, To, Coordinate, Speed ) - - if Carrier and Carrier:IsAlive() ~= nil then - - self.RouteHome = true - - local _speed=Speed or Carrier:GetSpeedMax()*0.5 - - local Waypoints = Carrier:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true ) - - self:F({Waypoints = Waypoints}) - local Waypoint = Waypoints[#Waypoints] - - Carrier:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. - - end - -end diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index ae3de6f15..4c00f17c6 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -10,7 +10,7 @@ -- @image AI_Cargo_Dispatching_For_APC.JPG --- @type AI_CARGO_APC --- @extends Core.Fsm#FSM_CONTROLLABLE +-- @extends AI.AI_Cargo#AI_CARGO --- Brings a dynamic cargo handling capability for AI groups. @@ -74,7 +74,6 @@ AI_CARGO_APC = { ClassName = "AI_CARGO_APC", Coordinate = nil, -- Core.Point#COORDINATE, - APC_Cargo = {}, } --- Creates a new AI_CARGO_APC object. @@ -85,26 +84,10 @@ AI_CARGO_APC = { -- @return #AI_CARGO_APC function AI_CARGO_APC:New( APC, CargoSet, CombatRadius ) - local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_CARGO_APC + local self = BASE:Inherit( self, AI_CARGO:New( APC, CargoSet ) ) -- #AI_CARGO_APC - self.CargoSet = CargoSet -- Core.Set#SET_CARGO self.CombatRadius = CombatRadius - self:SetStartState( "Unloaded" ) - - self:AddTransition( "Unloaded", "Pickup", "*" ) - self:AddTransition( "Loaded", "Deploy", "*" ) - - self:AddTransition( "*", "Load", "Boarding" ) - self:AddTransition( { "Boarding", "Loaded" }, "Board", "Boarding" ) - self:AddTransition( "Boarding", "Loaded", "Boarding" ) - self:AddTransition( "Boarding", "PickedUp", "Loaded" ) - - self:AddTransition( "Loaded", "Unload", "Unboarding" ) - self:AddTransition( "Unboarding", "Unboard", "Unboarding" ) - self:AddTransition( "Unboarding", "Unloaded", "Unboarding" ) - self:AddTransition( "Unboarding", "Deployed", "Unloaded" ) - self:AddTransition( "*", "Monitor", "*" ) self:AddTransition( "*", "Follow", "Following" ) self:AddTransition( "*", "Guard", "Unloaded" ) @@ -112,100 +95,10 @@ function AI_CARGO_APC:New( APC, CargoSet, CombatRadius ) self:AddTransition( "*", "Destroyed", "Destroyed" ) - - --- Pickup Handler OnBefore for AI_CARGO_APC - -- @function [parent=#AI_CARGO_APC] OnBeforePickup - -- @param #AI_CARGO_APC self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param Core.Point#COORDINATE Coordinate - -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. - -- @return #boolean - - --- Pickup Handler OnAfter for AI_CARGO_APC - -- @function [parent=#AI_CARGO_APC] OnAfterPickup - -- @param #AI_CARGO_APC self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param Core.Point#COORDINATE Coordinate - -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. - - --- Pickup Trigger for AI_CARGO_APC - -- @function [parent=#AI_CARGO_APC] Pickup - -- @param #AI_CARGO_APC self - -- @param Core.Point#COORDINATE Coordinate - -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. - - --- Pickup Asynchronous Trigger for AI_CARGO_APC - -- @function [parent=#AI_CARGO_APC] __Pickup - -- @param #AI_CARGO_APC self - -- @param #number Delay - -- @param Core.Point#COORDINATE Coordinate Pickup place. If not given, loading starts at the current location. - -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. - - --- Deploy Handler OnBefore for AI_CARGO_APC - -- @function [parent=#AI_CARGO_APC] OnBeforeDeploy - -- @param #AI_CARGO_APC self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param Core.Point#COORDINATE Coordinate - -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. - -- @return #boolean - - --- Deploy Handler OnAfter for AI_CARGO_APC - -- @function [parent=#AI_CARGO_APC] OnAfterDeploy - -- @param #AI_CARGO_APC self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param Core.Point#COORDINATE Coordinate - -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. - - --- Deploy Trigger for AI_CARGO_APC - -- @function [parent=#AI_CARGO_APC] Deploy - -- @param #AI_CARGO_APC self - -- @param Core.Point#COORDINATE Coordinate - -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. - - --- Deploy Asynchronous Trigger for AI_CARGO_APC - -- @function [parent=#AI_CARGO_APC] __Deploy - -- @param #AI_CARGO_APC self - -- @param #number Delay - -- @param Core.Point#COORDINATE Coordinate - -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. - - - --- Loaded Handler OnAfter for AI_CARGO_APC - -- @function [parent=#AI_CARGO_APC] OnAfterLoaded - -- @param #AI_CARGO_APC self - -- @param Wrapper.Group#GROUP APC - -- @param #string From - -- @param #string Event - -- @param #string To - - --- Unloaded Handler OnAfter for AI_CARGO_APC - -- @function [parent=#AI_CARGO_APC] OnAfterUnloaded - -- @param #AI_CARGO_APC self - -- @param Wrapper.Group#GROUP APC - -- @param #string From - -- @param #string Event - -- @param #string To - - self:__Monitor( 1 ) self:SetCarrier( APC ) - for _, APCUnit in pairs( APC:GetUnits() ) do - APCUnit:SetCargoBayWeightLimit() - end - - self.Transporting = false - self.Relocating = false - return self end @@ -258,16 +151,6 @@ function AI_CARGO_APC:SetCarrier( CargoCarrier ) end -function AI_CARGO_APC:IsTransporting() - - return self.Transporting == true -end - -function AI_CARGO_APC:IsRelocating() - - return self.Relocating == true -end - --- Find a free Carrier within a range. -- @param #AI_CARGO_APC self -- @param Core.Point#COORDINATE Coordinate @@ -361,7 +244,7 @@ function AI_CARGO_APC:onafterMonitor( APC, From, Event, To ) if APC and APC:IsAlive() then if self.CarrierCoordinate then - if self:IsRelocating() == true then + if self:IsTransporting() == true then local Coordinate = APC:GetCoordinate() self.Zone:Scan( { Object.Category.UNIT } ) if self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then @@ -378,7 +261,7 @@ function AI_CARGO_APC:onafterMonitor( APC, From, Event, To ) self:Follow() end if self:Is( "Following" ) then - for Cargo, APCUnit in pairs( self.APC_Cargo ) do + for Cargo, APCUnit in pairs( self.Carrier_Cargo ) do local Cargo = Cargo -- Cargo.Cargo#CARGO if Cargo:IsAlive() then if not Cargo:IsNear( APCUnit, 40 ) then @@ -408,276 +291,6 @@ function AI_CARGO_APC:onafterMonitor( APC, From, Event, To ) end ---- On before Load event. --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP APC --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. -function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To, PickupZone ) - self:F( { APC, From, Event, To } ) - - local Boarding = false - - local LoadInterval = 10 - local LoadDelay = 10 - local APC_List = {} - local APC_Weight = {} - - if APC and APC:IsAlive() then - self.APC_Cargo = {} - for _, APCUnit in pairs( APC:GetUnits() ) do - local APCUnit = APCUnit -- Wrapper.Unit#UNIT - - local CargoBayFreeWeight = APCUnit:GetCargoBayFreeWeight() - self:F({CargoBayFreeWeight=CargoBayFreeWeight}) - - APC_List[#APC_List+1] = APCUnit - APC_Weight[APCUnit] = CargoBayFreeWeight - end - - local APC_Count = #APC_List - local APC_Index = 1 - - for _, Cargo in UTILS.spairs( self.CargoSet:GetSet(), function( t, a, b ) return t[a]:GetWeight() > t[b]:GetWeight() end ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - - self:F( { IsUnLoaded = Cargo:IsUnLoaded(), IsDeployed = Cargo:IsDeployed(), Cargo:GetName(), APC:GetName() } ) - - local Loaded = false - - -- Try all APCs, but start from the one according the APC_Index - for APC_Loop = 1, #APC_List do - - local APCUnit = APC_List[APC_Index] -- Wrapper.Unit#UNIT - - -- This counters loop through the available APCs. - APC_Index = APC_Index + 1 - if APC_Index > APC_Count then - APC_Index = 1 - end - - if Cargo:IsUnLoaded() then -- and not Cargo:IsDeployed() then - if Cargo:IsInLoadRadius( APCUnit:GetCoordinate() ) then - self:F( { "In radius", APCUnit:GetName() } ) - - local CargoWeight = Cargo:GetWeight() - - -- Only when there is space within the bay to load the next cargo item! - if APC_Weight[APCUnit] > CargoWeight then --and CargoBayFreeVolume > CargoVolume then - APC:RouteStop() - --Cargo:Ungroup() - Cargo:__Board( LoadDelay, APCUnit, 25 ) - LoadDelay = LoadDelay + LoadInterval - self:__Board( LoadDelay, Cargo, APCUnit, PickupZone ) - - -- So now this APCUnit has Cargo that is being loaded. - -- This will be used further in the logic to follow and to check cargo status. - self.APC_Cargo[Cargo] = APCUnit - Boarding = true - APC_Weight[APCUnit] = APC_Weight[APCUnit] - CargoWeight - Loaded = true - - -- Ok, we loaded a cargo, now we can stop the loop. - break - end - end - end - - end - - if not Loaded then - -- If the cargo wasn't loaded in one of the carriers, then we need to stop the loading. - break - end - - end - end - - return Boarding - -end - ---- On after Board event. --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP APC --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Cargo.Cargo#CARGO Cargo Cargo object. --- @param Wrapper.Unit#UNIT APCUnit --- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. -function AI_CARGO_APC:onafterBoard( APC, From, Event, To, Cargo, APCUnit, PickupZone ) - self:F( { APC, From, Event, To, Cargo, APCUnit:GetName() } ) - - if APC and APC:IsAlive() then - self:F({ IsLoaded = Cargo:IsLoaded(), Cargo:GetName(), APC:GetName() } ) - if not Cargo:IsLoaded() then - self:__Board( 10, Cargo, APCUnit, PickupZone ) - return - end - end - - self:__Loaded( 10, Cargo, APCUnit, PickupZone ) - -end - ---- On after Loaded event. --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP APC --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @return #boolean Cargo loaded. --- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. -function AI_CARGO_APC:onafterLoaded( APC, From, Event, To, Cargo, PickupZone ) - self:F( { APC, From, Event, To } ) - - local Loaded = true - - if APC and APC:IsAlive() then - for Cargo, APCUnit in pairs( self.APC_Cargo ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - self:F( { IsLoaded = Cargo:IsLoaded(), IsDestroyed = Cargo:IsDestroyed(), Cargo:GetName(), APC:GetName() } ) - if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then - Loaded = false - end - end - end - - if Loaded then - self:PickedUp( PickupZone ) - end - -end - ---- On after PickedUp event. --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP APC --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. -function AI_CARGO_APC:onafterPickedUp( APC, From, Event, To, PickupZone ) - self:F( { APC, From, Event, To } ) - - self.Transporting = true - APC:RouteResume() - -end - - - - ---- On after Unload event. --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP APC --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. -function AI_CARGO_APC:onafterUnload( APC, From, Event, To, DeployZone ) - self:F( { APC, From, Event, To, DeployZone } ) - - local UnboardInterval = 10 - local UnboardDelay = 10 - - if APC and APC:IsAlive() then - for _, APCUnit in pairs( APC:GetUnits() ) do - local APCUnit = APCUnit -- Wrapper.Unit#UNIT - APC:RouteStop() - for _, Cargo in pairs( APCUnit:GetCargo() ) do - if Cargo:IsLoaded() then - Cargo:__UnBoard( UnboardDelay ) - UnboardDelay = UnboardDelay + UnboardInterval - Cargo:SetDeployed( true ) - self:__Unboard( UnboardDelay, Cargo, APCUnit, DeployZone ) - end - end - end - end - -end - ---- On after Unboard event. --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP APC --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param #string Cargo.Cargo#CARGO Cargo Cargo object. --- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. -function AI_CARGO_APC:onafterUnboard( APC, From, Event, To, Cargo, APCUnit, DeployZone ) - self:F( { APC, From, Event, To, Cargo:GetName() } ) - - if APC and APC:IsAlive() then - if not Cargo:IsUnLoaded() then - self:__Unboard( 10, Cargo, APCUnit, DeployZone ) - return - end - end - - self:Unloaded( Cargo, APCUnit, DeployZone ) - -end - ---- On after Unloaded event. --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP APC --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param #string Cargo.Cargo#CARGO Cargo Cargo object. --- @param #boolean Deployed Cargo is deployed. --- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. -function AI_CARGO_APC:onafterUnloaded( APC, From, Event, To, Cargo, APCUnit, DeployZone ) - self:F( { APC, From, Event, To, Cargo:GetName(), DeployZone = DeployZone } ) - - local AllUnloaded = true - - --Cargo:Regroup() - - if APC and APC:IsAlive() then - for _, APCUnit in pairs( APC:GetUnits() ) do - local APCUnit = APCUnit -- Wrapper.Unit#UNIT - local IsEmpty = APCUnit:IsCargoEmpty() - self:I({ IsEmpty = IsEmpty }) - if not IsEmpty then - AllUnloaded = false - break - end - end - - if AllUnloaded == true then - if DeployZone == true then - self.APC_Cargo = {} - end - self.CargoCarrier = APC - end - end - - if AllUnloaded == true then - self:Deployed( DeployZone ) - end - -end - ---- On after Deployed event. --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP APC --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. -function AI_CARGO_APC:onafterDeployed( APC, From, Event, To, DeployZone ) - self:F( { APC, From, Event, To, DeployZone = DeployZone } ) - - self.Transporting = false - self:__Guard( 0.1 ) - -end - --- On after Follow event. -- @param #AI_CARGO_APC self -- @param Wrapper.Group#GROUP APC @@ -689,7 +302,7 @@ function AI_CARGO_APC:onafterFollow( APC, From, Event, To ) self:F( "Follow" ) if APC and APC:IsAlive() then - for Cargo, APCUnit in pairs( self.APC_Cargo ) do + for Cargo, APCUnit in pairs( self.Carrier_Cargo ) do local Cargo = Cargo -- Cargo.Cargo#CARGO if Cargo:IsUnLoaded() then self:FollowToCarrier( self, APCUnit, Cargo ) @@ -709,6 +322,7 @@ function AI_CARGO_APC._Pickup( APC, self, PickupZone ) if APC:IsAlive() then self:Load( PickupZone) + self.Relocating = false end end @@ -719,6 +333,7 @@ function AI_CARGO_APC._Deploy( APC, self, Coordinate, DeployZone ) if APC:IsAlive() then self:Unload( DeployZone ) + self.Transporting = false end end diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index d7da38110..9ef0e3f57 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -70,6 +70,7 @@ AI/AI_Cap.lua AI/AI_Cas.lua AI/AI_Bai.lua AI/AI_Formation.lua +AI/AI_Cargo.lua AI/AI_Cargo_APC.lua AI/AI_Cargo_Helicopter.lua AI/AI_Cargo_Airplane.lua From 01add98b7a0a639e37c9e73dbc978ea734ee05aa Mon Sep 17 00:00:00 2001 From: FlightControl Date: Wed, 5 Sep 2018 16:33:31 +0200 Subject: [PATCH 313/420] Improved logic --- Moose Development/Moose/AI/AI_Cargo.lua | 2 - Moose Development/Moose/AI/AI_Cargo_APC.lua | 3 + .../Moose/AI/AI_Cargo_Airplane.lua | 177 +-------------- .../Moose/AI/AI_Cargo_Helicopter.lua | 201 +----------------- .../Moose/Wrapper/Controllable.lua | 4 +- 5 files changed, 7 insertions(+), 380 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua index d2ba6f494..bd379f299 100644 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -363,7 +363,6 @@ end function AI_CARGO:onafterPickedUp( Carrier, From, Event, To, PickupZone ) self:F( { Carrier, From, Event, To } ) - self.Transporting = true Carrier:RouteResume() end @@ -474,7 +473,6 @@ end function AI_CARGO:onafterDeployed( Carrier, From, Event, To, DeployZone ) self:F( { Carrier, From, Event, To, DeployZone = DeployZone } ) - self.Transporting = false self:__Guard( 0.1 ) end diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 4c00f17c6..f991346a9 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -263,6 +263,7 @@ function AI_CARGO_APC:onafterMonitor( APC, From, Event, To ) if self:Is( "Following" ) then for Cargo, APCUnit in pairs( self.Carrier_Cargo ) do local Cargo = Cargo -- Cargo.Cargo#CARGO + local APCUnit = APCUnit -- Wrapper.Unit#UNIT if Cargo:IsAlive() then if not Cargo:IsNear( APCUnit, 40 ) then APCUnit:RouteStop() @@ -323,6 +324,7 @@ function AI_CARGO_APC._Pickup( APC, self, PickupZone ) if APC:IsAlive() then self:Load( PickupZone) self.Relocating = false + self.Transporting = true end end @@ -334,6 +336,7 @@ function AI_CARGO_APC._Deploy( APC, self, Coordinate, DeployZone ) if APC:IsAlive() then self:Unload( DeployZone ) self.Transporting = false + self.Relocating = false end end diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 55e5a79fd..882ea6d36 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -19,7 +19,6 @@ AI_CARGO_AIRPLANE = { ClassName = "AI_CARGO_AIRPLANE", Coordinate = nil, -- Core.Point#COORDINATE - Airplane_Cargo = {}, } --- Creates a new AI_CARGO_AIRPLANE object. @@ -29,25 +28,10 @@ AI_CARGO_AIRPLANE = { -- @return #AI_CARGO_AIRPLANE function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) - local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_CARGO_AIRPLANE + local self = BASE:Inherit( self, AI_CARGO:New( Airplane, CargoSet ) ) -- #AI_CARGO_AIRPLANE self.CargoSet = CargoSet -- Cargo.CargoGroup#CARGO_GROUP - self:SetStartState( "Unloaded" ) - - self:AddTransition( { "Unloaded", "Loaded" }, "Pickup", "*" ) - self:AddTransition( "Loaded", "Deploy", "*" ) - - self:AddTransition( { "Unloaded", "Boarding" }, "Load", "Boarding" ) - self:AddTransition( "Boarding", "Board", "Boarding" ) - self:AddTransition( "Boarding", "Loaded", "Boarding" ) - self:AddTransition( "Boarding", "PickedUp", "Loaded" ) - - self:AddTransition( "Loaded", "Unload", "Unboarding" ) - self:AddTransition( "Unboarding", "Unboard", "Unboarding" ) - self:AddTransition( "Unboarding" , "Unloaded", "Unboarding" ) - self:AddTransition( "Unboarding" , "Deployed", "Unloaded" ) - self:AddTransition( "*", "Landed", "*" ) self:AddTransition( "*", "Home" , "*" ) @@ -368,110 +352,6 @@ function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Coordinate, end ---- On before Load event. Checks if cargo is inside the load radius and if so starts the boarding process. --- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane Transport plane. --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. -function AI_CARGO_AIRPLANE:onbeforeLoad( Airplane, From, Event, To, PickupZone ) - - - local Boarding = false - - local LoadInterval = 10 - local LoadDelay = 10 - - if Airplane and Airplane:IsAlive() ~= nil then - - for _, AirplaneUnit in pairs( Airplane:GetUnits() ) do - local AirplaneUnit = AirplaneUnit -- Wrapper.Unit#UNIT - local CargoBayFreeWeight = AirplaneUnit:GetCargoBayFreeWeight() - self:F({CargoBayFreeWeight=CargoBayFreeWeight}) - - for _, Cargo in UTILS.spairs( self.CargoSet:GetSet(), function( t, a, b ) return t[a]:GetWeight() > t[b]:GetWeight() end ) do - self:F({Cargo:GetName()}) - local Cargo=Cargo --Cargo.Cargo#CARGO - if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then - - if Cargo:IsInLoadRadius( AirplaneUnit:GetCoordinate() ) then - - local CargoWeight = Cargo:GetWeight() - - -- Only when there is space within the bay to load the next cargo item! - if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then - Cargo:__Board( LoadDelay, AirplaneUnit, 25 ) - LoadDelay = LoadDelay + LoadInterval - self:__Board( LoadDelay, Cargo, AirplaneUnit, PickupZone ) - self.Airplane_Cargo[AirplaneUnit] = Cargo - Boarding = true - CargoBayFreeWeight = CargoBayFreeWeight - CargoWeight - end - end - end - end - end - end - - return Boarding - -end - ---- On after Board event. Cargo is inside the load radius and boarding is performed. --- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane Cargo transport plane. --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Cargo.Cargo#CARGO Cargo Cargo object. --- @param Wrapper.Unit#UNIT AirplaneUnit --- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. -function AI_CARGO_AIRPLANE:onafterBoard( Airplane, From, Event, To, Cargo, AirplaneUnit, PickupZone ) - - if Airplane and Airplane:IsAlive() then - - self:F({ IsLoaded = Cargo:IsLoaded() } ) - - if not Cargo:IsLoaded() then - self:__Board( 10, Cargo, AirplaneUnit, PickupZone ) - return - end - end - - self:__Loaded( 10, Cargo, AirplaneUnit, PickupZone ) - -end - - ---- On After Loaded event. --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Group#GROUP Helicopter --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @return #boolean Cargo loaded. --- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. -function AI_CARGO_AIRPLANE:onafterLoaded( AirplaneGroup, From, Event, To, Cargo, AirplaneUnit, PickupZone ) - self:F( { AirplaneGroup, From, Event, To } ) - - local Loaded = true - - if AirplaneGroup and AirplaneGroup:IsAlive() then - for AirplaneUnit, Cargo in pairs( self.Airplane_Cargo ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - self:F( { IsLoaded = Cargo:IsLoaded(), IsDestroyed = Cargo:IsDestroyed(), Cargo:GetName(), AirplaneGroup:GetName() } ) - if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then - Loaded = false - end - end - end - - if Loaded then - self:PickedUp( PickupZone ) - end - -end --- On after PickedUp event. All cargo is inside the carrier and ready to be transported. -- @param #AI_CARGO_AIRPLANE self @@ -521,61 +401,6 @@ function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To, DeployZone end ---- On after Unboard event. Checks if unboarding process is finished. --- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane Cargo transport plane. --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function AI_CARGO_AIRPLANE:onafterUnboard( Airplane, From, Event, To, Cargo, AirplaneUnit, DeployZone ) - - self:E( { "Unboard", Cargo } ) - - if Airplane and Airplane:IsAlive() then - if not Cargo:IsUnLoaded() then - self:__Unboard( 10, Cargo, AirplaneUnit, DeployZone ) - return - end - end - - self:Unloaded( Cargo, AirplaneUnit, DeployZone ) - -end - ---- On after Unloaded event. Cargo has been unloaded, i.e. the unboarding process is finished. --- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane Cargo transport plane. --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Cargo.Cargo#CARGO Cargo -function AI_CARGO_AIRPLANE:onafterUnloaded( Airplane, From, Event, To, Cargo, AirplaneUnit, DeployZone ) - - local AllUnloaded = true - - if AirplaneUnit and AirplaneUnit:IsAlive() then - for _, AirplaneUnit in pairs( AirplaneUnit:GetUnits() ) do - local IsEmpty = AirplaneUnit:IsCargoEmpty() - self:I({ IsEmpty = IsEmpty }) - if not IsEmpty then - AllUnloaded = false - break - end - end - - if AllUnloaded == true then - if DeployZone then - self.Airplane_Cargo = {} - end - self.Airplane = AirplaneUnit - end - end - - if AllUnloaded == true then - self:Deployed( DeployZone ) - end - -end --- On after Deployed event. diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 62cdbf43a..b05c75dfe 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -21,7 +21,6 @@ AI_CARGO_HELICOPTER = { ClassName = "AI_CARGO_HELICOPTER", Coordinate = nil, -- Core.Point#COORDINATE, - Helicopter_Cargo = {}, } AI_CARGO_QUEUE = {} @@ -33,7 +32,7 @@ AI_CARGO_QUEUE = {} -- @return #AI_CARGO_HELICOPTER function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) - local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_CARGO_HELICOPTER + local self = BASE:Inherit( self, AI_CARGO:New( Helicopter, CargoSet ) ) -- #AI_CARGO_HELICOPTER self.CargoSet = CargoSet -- Cargo.CargoGroup#CARGO_GROUP @@ -379,115 +378,6 @@ function AI_CARGO_HELICOPTER:onafterOrbit( Helicopter, From, Event, To, Coordina end ---- On Before event Load. --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Group#GROUP Helicopter --- @param #string From From state. --- @param #string Event Event --- @param #string To To state. --- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. -function AI_CARGO_HELICOPTER:onbeforeLoad( Helicopter, From, Event, To, PickupZone ) - - local Boarding = false - - local LoadInterval = 10 - local LoadDelay = 10 - - if Helicopter and Helicopter:IsAlive() then - - self.BoardingCount = 0 - - for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do - local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT - local CargoBayFreeWeight = HelicopterUnit:GetCargoBayFreeWeight() - self:F({CargoBayFreeWeight=CargoBayFreeWeight}) - - for _, Cargo in pairs( self.CargoSet:GetSet() ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - self:F( { IsUnLoaded = Cargo:IsUnLoaded() } ) - if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then - if Cargo:IsInLoadRadius( HelicopterUnit:GetCoordinate() ) then - self:F( { "In radius", HelicopterUnit:GetName() } ) - - local CargoWeight = Cargo:GetWeight() - - -- Only when there is space within the bay to load the next cargo item! - if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then - - --Cargo:Ungroup() - Cargo:__Board( LoadDelay, HelicopterUnit, 25 ) - LoadDelay = LoadDelay + LoadInterval - self:__Board( LoadDelay, Cargo, HelicopterUnit, PickupZone ) - self.Helicopter_Cargo[HelicopterUnit] = Cargo - Boarding = true - CargoBayFreeWeight = CargoBayFreeWeight - CargoWeight - end - end - end - end - end - end - - return Boarding - -end - ---- On after Board event. --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Group#GROUP Helicopter --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Cargo.Cargo#CARGO Cargo Cargo object. --- @param Wrapper.Unit#UNIT HelicopterUnit --- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. -function AI_CARGO_HELICOPTER:onafterBoard( Helicopter, From, Event, To, Cargo, HelicopterUnit, PickupZone ) - self:F( { Helicopter, From, Event, To, Cargo, HelicopterUnit } ) - - if Helicopter and Helicopter:IsAlive() then - self:F({ IsLoaded = Cargo:IsLoaded() } ) - if not Cargo:IsLoaded() then - self:__Board( 10, Cargo, HelicopterUnit, PickupZone ) - return - end - end - - self:__Loaded( 10, Cargo, HelicopterUnit, PickupZone ) -- Will only be executed when no more cargo is boarded. - -end - - ---- On After Loaded event. --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Group#GROUP Helicopter --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @return #boolean Cargo loaded. --- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. -function AI_CARGO_HELICOPTER:onafterLoaded( Helicopter, From, Event, To, Cargo, HelicopterUnit, PickupZone ) - self:F( { Helicopter, From, Event, To } ) - - local Loaded = true - - if Helicopter and Helicopter:IsAlive() then - for HelicopterUnit, Cargo in pairs( self.Helicopter_Cargo ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - self:F( { IsLoaded = Cargo:IsLoaded(), IsDestroyed = Cargo:IsDestroyed(), Cargo:GetName(), Helicopter:GetName() } ) - if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then - Loaded = false - end - end - end - - if Loaded then - self:PickedUp( PickupZone ) - end - -end - - - --- On after PickedUp event, raised when all cargo has been loaded into the CarrierGroup. -- @param #AI_CARGO_HELICOPTER self @@ -507,95 +397,6 @@ function AI_CARGO_HELICOPTER:onafterPickedUp( Helicopter, From, Event, To, Picku end ---- On after Unload event. --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Group#GROUP Helicopter --- @param #string From From state. --- @param #string Event Event. --- @param #boolean Deployed Cargo is deployed. -function AI_CARGO_HELICOPTER:onafterUnload( Helicopter, From, Event, To, DeployZone ) - - local UnboardInterval = 10 - local UnboardDelay = 10 - - if Helicopter and Helicopter:IsAlive() then - for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do - local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT - for _, Cargo in pairs( HelicopterUnit:GetCargo() ) do - if Cargo:IsLoaded() then - Cargo:__UnBoard( UnboardDelay ) - UnboardDelay = UnboardDelay + UnboardInterval - Cargo:SetDeployed( true ) - self:__Unboard( UnboardDelay, Cargo, HelicopterUnit, DeployZone ) - end - end - end - end - - -end - ---- On after Unboard event. --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Group#GROUP Helicopter --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Cargo.Cargo#CARGO Cargo Cargo object. --- @param #boolean Deployed Cargo is deployed. -function AI_CARGO_HELICOPTER:onafterUnboard( Helicopter, From, Event, To, Cargo, HelicopterUnit, DeployZone ) - - if Helicopter and Helicopter:IsAlive() then - if not Cargo:IsUnLoaded() then - self:__Unboard( 10, Cargo, HelicopterUnit, DeployZone ) - return - end - end - - self:Unloaded( Cargo, HelicopterUnit, DeployZone ) - -end - ---- On after Unloaded event. --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Group#GROUP Helicopter --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Cargo.Cargo#CARGO Cargo Cargo object. --- @param #boolean Deployed Cargo is deployed. --- @return #boolean True if all cargo has been unloaded. -function AI_CARGO_HELICOPTER:onafterUnloaded( Helicopter, From, Event, To, Cargo, HelicopterUnit, DeployZone ) - self:F( { Helicopter, From, Event, To, Cargo:GetName(), HelicopterUnit:GetName(), DeployZone = DeployZone } ) - - local AllUnloaded = true - - --Cargo:Regroup() - - if Helicopter and Helicopter:IsAlive() then - for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do - local IsEmpty = HelicopterUnit:IsCargoEmpty() - self:I({ IsEmpty = IsEmpty }) - if not IsEmpty then - AllUnloaded = false - break - end - end - - if AllUnloaded == true then - if DeployZone then - self.Helicopter_Cargo = {} - end - self.Helicopter = Helicopter - end - end - - if AllUnloaded == true then - self:Deployed( DeployZone ) - end - -end - --- On after Deployed event. -- @param #AI_CARGO_HELICOPTER self diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index a6f83c5c0..ef91a1cff 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1937,7 +1937,7 @@ do -- Route methods -- @param #CONTROLLABLE self -- @return #CONTROLLABLE function CONTROLLABLE:RouteStop() - self:F("RouteStop") + self:F(self:GetName() .. "RouteStop") local CommandStop = self:CommandStopRoute( true ) self:SetCommand( CommandStop ) @@ -1948,7 +1948,7 @@ do -- Route methods -- @param #CONTROLLABLE self -- @return #CONTROLLABLE function CONTROLLABLE:RouteResume() - self:F("RouteResume") + self:F( self:GetName() .. " RouteResume") local CommandResume = self:CommandStopRoute( false ) self:SetCommand( CommandResume ) From 7ab11d8fef25f2ec9afdb77f76cfb7d24dc785d6 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 5 Sep 2018 16:41:59 +0200 Subject: [PATCH 314/420] Warehouse v0.3.5w --- .../Moose/Functional/Warehouse.lua | 269 +++++++++++++----- 1 file changed, 190 insertions(+), 79 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 9d328706b..c70314a4b 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -107,15 +107,20 @@ -- The positioning of the warehouse static object is very important for a couple of reasons. Firstly, a warehouse needs a good infrastructure so that spawned assets -- have a proper road connection or can reach the associated airbase easily. -- +-- ## Constructor and Start +-- -- Once the static warehouse object is placed in the mission editor it can be used as a MOOSE warehouse by the @{#WAREHOUSE.New}(*warehousestatic*, *alias*) constructor, -- like for example: -- --- warehouse=WAREHOUSE:New(STATIC:FindByName("Warehouse Static Batumi"), "My Warehouse Alias") --- warehouse:Start() +-- warehouse=WAREHOUSE:New(STATIC:FindByName("Warehouse Static Batumi"), "My Warehouse Alias") +-- warehouse:Start() -- --- So the first parameter *warehousestatic* is the static MOOSE object. By default, the name of the warehouse will be the same as the name given to the static object. +-- The first parameter *warehousestatic* is the static MOOSE object. By default, the name of the warehouse will be the same as the name given to the static object. -- The second parameter *alias* can be used to choose a more convenient name if desired. This will be the name the warehouse calls itself when reporting messages. -- +-- Note that a warehouse also needs to be started in order to be in service. This is done with the @{#WAREHOUSE.Start}() or @{#WAREHOUSE.__Start}(*delay*) functions. +-- The warehouse is now fully operational and requests are being processed. +-- -- # Adding Assets -- -- Assets can be added to the warehouse stock by using the @{#WAREHOUSE.AddAsset}(*group*, *ngroups*, *forceattribute*) function. The parameter *group* has to be a MOOSE @{Wrapper.Group#GROUP}. @@ -123,8 +128,8 @@ -- -- Note that the group should be a late activated template group, which was defined in the mission editor. -- --- infrantry=GROUP:FindByName("Some Infantry Group") --- warehouse:AddAsset(infantry, 5) +-- infrantry=GROUP:FindByName("Some Infantry Group") +-- warehouse:AddAsset(infantry, 5) -- -- This will add five infantry groups to the warehouse stock. -- @@ -137,10 +142,10 @@ -- -- # Requesting Assets -- --- Assets of the warehouse can be requested by other MOOSE warehouses. A request will first be scrutinize to check if can be fulfilled at all. If the request is valid, it is +-- Assets of the warehouse can be requested by other MOOSE warehouses. A request will first be scrutinized to check if can be fulfilled at all. If the request is valid, it is -- put into the warehouse queue and processed as soon as possible. -- --- A request can be assed by the @{#WAREHOUSE.AddRequest}(*warehouse*, *AssetDescriptor*, *AssetDescriptorValue*, *nAsset*, *TransportType*, *nTransport*, *Prio*, *Assignment*) function. +-- A request can be added by the @{#WAREHOUSE.AddRequest}(*warehouse*, *AssetDescriptor*, *AssetDescriptorValue*, *nAsset*, *TransportType*, *nTransport*, *Prio*, *Assignment*) function. -- The parameters are -- -- * *warehouse*: The requesting MOOSE @{#WAREHOUSE}. Assets will be delivered there. @@ -152,22 +157,22 @@ -- * *Prio*: (Optional) A number between 1 (high) and 100 (low) describing the priority of the request. Request with high priority are processed first. Default is 50, i.e. medium priority. -- * *Assignment*: (Optional) A free to choose string describing the assignment. For self requests, this can be used to assign the spawned groups to specific tasks. -- --- So for example: +-- For example: -- --- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 5, WAREHOUSE.TransportType.APC, 2, 20) +-- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 5, WAREHOUSE.TransportType.APC, 2, 20) -- -- Here, warehouse Kobuleti requests 5 infantry groups from warehouse Batumi. These "cargo" assets should be transported from Batumi to Kobuleti by 2 APCS. -- Note that the warehouse at Batumi needs to have at least five infantry groups and two APC groups in their stock if the request can be processed. -- If either to few infantry or APC groups are available when the request is made, the request is held in the warehouse queue until enough cargo and -- transport assets are available. -- --- Also not that the above request is for five infantry units. So any group in stock that has the generalized attribute "INFANTRY" can be selected. +-- Also note that the above request is for five infantry groups. So any group in stock that has the generalized attribute "INFANTRY" can be selected. -- -- ## Requesting a Specific Unit Type -- -- A more specific request could look like: -- --- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.UNITTYPE, "A-10C", 2) +-- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.UNITTYPE, "A-10C", 2) -- -- Here, Kobuleti requests a specific unit type, in particular two groups of A-10Cs. Note that the spelling is important as it must exacly be the same as -- what one get's when using the DCS unit type. @@ -176,7 +181,7 @@ -- -- An even more specific request would be: -- --- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.TEMPLATENAME, "Group Name as in ME", 3) +-- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.TEMPLATENAME, "Group Name as in ME", 3) -- -- In this case three groups named "Group Name as in ME" are requested. So this explicitly request the groups named like that in the Mission Editor. -- @@ -184,7 +189,7 @@ -- -- On the other hand, very general unspecifc requests can be made as -- --- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.CATEGORY, Group.Category.Ground, 10) +-- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.CATEGORY, Group.Category.Ground, 10) -- -- Here, Kubuleti requests 10 ground groups and does not care which ones. This could be a mix of infantry, APCs, trucks etc. -- @@ -193,7 +198,7 @@ -- Assets in the warehouse' stock can used for user defined tasks realtively easily. They can be spawned into the game by a "self request", i.e. the warehouse -- requests the assets from itself: -- --- warehouseBatumi:AddRequest(warehouseBatumi, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 5) +-- warehouseBatumi:AddRequest(warehouseBatumi, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 5) -- -- This would simply spawn five infantry groups in the spawn zone of the Batumi warehouse if/when they are available. -- @@ -202,21 +207,21 @@ -- If a warehouse requests assets from itself, it triggers the event **SelfReqeuest**. The mission designer can capture this event with the associated -- @{#WAREHOUSE.OnAfterSelfRequest}(*From*, *Event*, *To*, *groupset*, *request*) function. -- --- --- OnAfterSelfRequest user function. Access groups spawned from the warehouse for further tasking. --- -- @param #WAREHOUSE self --- -- @param #string From From state. --- -- @param #string Event Event. --- -- @param #string To To state. --- -- @param Core.Set#SET_GROUP groupset The set of cargo groups that was delivered to the warehouse itself. --- -- @param #WAREHOUSE.Pendingitem request Pending self request. --- function WAREHOUSE:onafterSelfRequest(From, Event, To, groupset, request) --- --- for _,group in pairs(groupset:GetSetObjects()) do --- local group=group --Wrapper.Group#GROUP --- group:SmokeGreen() --- end --- --- end +-- --- OnAfterSelfRequest user function. Access groups spawned from the warehouse for further tasking. +-- -- @param #WAREHOUSE self +-- -- @param #string From From state. +-- -- @param #string Event Event. +-- -- @param #string To To state. +-- -- @param Core.Set#SET_GROUP groupset The set of cargo groups that was delivered to the warehouse itself. +-- -- @param #WAREHOUSE.Pendingitem request Pending self request. +-- function WAREHOUSE:OnAfterSelfRequest(From, Event, To, groupset, request) +-- +-- for _,group in pairs(groupset:GetSetObjects()) do +-- local group=group --Wrapper.Group#GROUP +-- group:SmokeGreen() +-- end +-- +-- end -- -- The variable *groupset* is a @{Core.Set#SET_GOUP} object and holds all asset groups from the request. The code above shows, how the mission designer can access the groups -- for further tasking. Here, the groups are only smoked but, of course, you can use them for whatever task you fancy. @@ -297,9 +302,19 @@ -- -- ## Invalid Requests -- --- Invalid request are requests which cannot be processes **ever** because there is some logical or physical argument for it. Or simply because that feature was not implemented (yet). +-- Invalid request are requests which can **never** be processes because there is some logical or physical argument against it. Or simply because that feature was not implemented (yet). -- --- * One warehuse requests airborne assets from another warehouse but at one (or even both) warehouses do not have an associated airbase. +-- * All airborne assets need an associated airbase of any kind on the sending *and* receiving warhouse. +-- * Airplanes need an airdrome at the sending and receiving warehouses. +-- * Not enough parking spots of the right terminal type at the sending warehouse. +-- * No parking spots of the right terminal type at the receiving warehouse. +-- * Ground assets need a road connection between both warehouses. +-- * Ground assets cannot be send directly to ships, i.e. warehouses on ships. +-- * Naval units need a user defined shipping lane between both warehouses. +-- * Warehouses need a user defined port zone to spawn naval assets. +-- * If transport by airplane, both warehouses must have and airdrome. +-- * If transport by APC, both warehouses must have a road connection. +-- * If transport by helicopter, the sending airbase must have an associated airbase. -- -- All invalid requests are removed from the warehouse queue! -- @@ -308,9 +323,18 @@ -- Temporarily unprocessable requests are possible in priciple, but cannot be processed at the given time the warehouse checks its queue. -- -- * No enough parking spaces are available for the requests assets but the airbase has enough parking spots in total so that this request is possible once other aircraft have taken off. +-- * Requesting warehouse is not in state "Running" (could be stopped, not yet started or under attack). +-- * Not enough cargo assets available at this moment. +-- * Not enough free parking spots for all cargo or transport airborne assets at the moment. +-- * Not enough transport assets to carry all cargo assets. -- -- Temporarily unprocessable requests are held in the queue. If at some point in time, the situation changes so that these requests can be processed, they are executed. -- +-- ## Processing Speed +-- +-- A warehouse has a limited speed to process requests. Each time the status of the warehouse is updated only one requests is processed. +-- The time interval between status updates is 30 seconds by default and can be adjusted via the @{#WAREHOUSE.SetStatusUpdate}(*interval*) function. +-- -- === -- -- # Strategic Considerations @@ -320,11 +344,11 @@ -- -- ## Capturing a Warehouse' Airbase -- --- If a warehouse has an associated airbase, it can be captured by the enemy. In this case, the warehouse looses it ability so employ all airborne assets and is also cut-off --- from supply by airborne units. +-- If a warehouse has an associated airbase, it can be captured by the enemy. In this case, the warehouse looses its ability so employ all airborne assets and is also cut-off +-- from supply by airplanes. Supply of ground troops via helicopters is still possible, because they deliver the troops into the spawn zone. -- --- Technically, the capturing of the airbase is triggered by the DCS S_EVENT_CAPTURE_BASE event. So the capturing takes place when only enemy ground units are in the --- airbase zone whilst no ground units of the present airbase owner are in that zone. +-- Technically, the capturing of the airbase is triggered by the DCS [S\_EVENT\_BASE\_CAPTURED](https://wiki.hoggitworld.com/view/DCS_event_base_captured) event. +-- So the capturing takes place when only enemy ground units are in the airbase zone whilst no ground units of the present airbase owner are in that zone. -- -- The warehouse will also create an event named "AirbaseCaptured", which can be captured by the @{#WAREHOUSE.OnAfterAirbaseCaptured} function. So the warehouse can react on -- this attack and for example spawn ground groups to re-capture its airbase. @@ -335,16 +359,24 @@ -- ## Capturing the Warehouse -- -- A warehouse can also be captured by the enemy coalition. If enemy ground troops enter the warehouse zone the event **Attacked** is triggered which can be captured by the --- @{#WAREHOUSE.OnAfterAttacked} event. +-- @{#WAREHOUSE.OnAfterAttacked} event. By default the warehouse zone circular zone with a radius of 500 meters located at the center of the physical warehouse. +-- The warehouse zone can be set via the @{#WAREHOUSE.SetWarehouseZone}(*zone*) function. The parameter *zone* must also be a cirular zone. -- --- If a warehouse is attacked it will spawn all its ground assets in the spawn zone which can than be used to defend the warehouse zone. +-- The @{#WAREHOUSE.OnAfterAttacked} function can be used by the mission designer to react to the enemy attack. For example by deploying some or all ground troops +-- currently in stock to defend the warehouse. Note that the warehouse also has a self defence option which can be enabled by the @{#WAREHOUSE.SetAutoDefenceOn}() +-- function. In this case, the warehouse will automatically spawn all ground troops. If the spawn zone is further away from the warehouse zone, all mobile troops +-- are routed to the warehouse zone. -- -- If only ground troops of the enemy coalition are present in the warehouse zone, the warehouse and all its assets falls into the hands of the enemy. -- In this case the event **Captured** is triggered which can be captured by the @{#WAREHOUSE.OnAfterCaptured} function. -- --- The warehouse turn to the capturing coalition, i.e. its physical representation, and all assets as well. In paticular, all requests to the warehouse will +-- The warehouse turns to the capturing coalition, i.e. its physical representation, and all assets as well. In paticular, all requests to the warehouse will -- spawn assets beloning to the new owner. -- +-- If the enemy troops could be defeated, i.e. no more troops of the opposite coalition are in the warehouse zone, the event **Defeated** is triggered and +-- the @{#WAREHOUSE.OnAfterDefeated} function can be used to adapt to the new situation. For example putting back all spawned defender troops back into +-- the warehouse stock. Note that if the automatic defence is enabled, all defenders are automatically put back into the warehouse on the **Defeated** event. +-- -- ## Destroying a Warehouse -- -- If an enemy destroy the physical warehouse structure, the warehouse will of course stop all its services. In priciple, all assets contained in the warehouse are @@ -631,7 +663,7 @@ -- After 10 seconds we make a self request for a rescue helicopter. Note, that the @{#WAREHOUSE.AddRequest} function has a parameter which lets you -- specify an "Assignment". This can be later used to identify the request and take the right actions. -- --- Once the request is processed, the @{#WAREHOUSE.OnafterSelfRequest} function is called. This is where we hook in and postprocess the spawned assets. +-- Once the request is processed, the @{#WAREHOUSE.OnAfterSelfRequest} function is called. This is where we hook in and postprocess the spawned assets. -- In particular, we use the @{AI.AI_Formation#AI_FORMATION} class to make some nice escorts for our carrier. -- -- When the resue helo is spawned, we can check that this is the correct asset and make the helo go into formation with the carrier. @@ -895,20 +927,21 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.3.5" +WAREHOUSE.version="0.3.5w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- TODO: Test mortars! Check also general requests like all ground. Is this a problem for self propelled if immobile units are among the assets? Check if transport. -- TODO: Add transport units from dispatchers back to warehouse stock once they completed their mission. -- TODO: Added habours as interface for transport to from warehouses? -- TODO: Set ROE for spawned groups. --- TODO: Add possibility to add active groups. Need to create a pseudo template before destroy. +-- DONE: Add possibility to add active groups. Need to create a pseudo template before destroy. <== Does not seem to be necessary any more. -- TODO: Write documentation. -- TODO: Handle the case when units of a group die during the transfer. Adjust template?! See Grouping in SPAWN. -- TODO: Add save/load capability of warehouse <==> percistance after mission restart. Difficult! --- TODO: Add a time stamp when an asset is added to the stock and for requests. +-- DONE: Add a time stamp when an asset is added to the stock and for requests. -- DONE: How to get a specific request once the cargo is delivered? Make addrequest addasset non FSM function? Callback for requests like in SPAWN? -- DONE: Add autoselfdefence switch and user function. Default should be off. -- DONE: Warehouse re-capturing not working?! @@ -1779,6 +1812,9 @@ function WAREHOUSE:onafterStatus(From, Event, To) -- Update coordinate in case we have a "moving" warehouse (e.g. on a carrier). self.coordinate=self.warehouse:GetCoordinate() + -- Check if any pending jobs are done and can be deleted from the + self:_JobDone() + -- Print status. self:_DisplayStatus() @@ -1821,6 +1857,39 @@ function WAREHOUSE:onafterStatus(From, Event, To) self:__Status(self.dTstatus) end + +--- Function that checks if a pending job is done and can be removed from queue +-- @param #WAREHOUSE self +function WAREHOUSE:_JobDone() + + local done={} + for _,request in pairs(self.pending) do + local request=request --#WAREHOUSE.Pendingitem + + local ncargo=0 + if request.cargogroupset then + ncargo=request.cargogroupset:Count() + end + + local ntransport=0 + if request.transportgroupset then + ntransport=request.transportgroupset:Count() + end + + --TODO: Check if any transports, e.g. APCs were not used and are still standing around in the spawn zone. + -- Also planes and helos might not be used? + + if ncargo==0 and ntransport==0 then + table.insert(done, request) + end + + end + + -- Remove pending requests if done. + for _,request in pairs(done) do + self:_DeleteQueueItem(request, self.pending) + end +end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- On after "AddAsset" event. Add a group to the warehouse stock. If the group is alive, it is destroyed. @@ -2805,7 +2874,6 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Get group obejet. local group=Cargo:GetObject() --Wrapper.Group#GROUP - -- Get warehouse state. local warehouse=Carrier:GetState(Carrier, "WAREHOUSE") --#WAREHOUSE @@ -2832,7 +2900,10 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) warehouse:I(warehouse.wid..text) -- Add carrier back to warehouse stock. Actual unit is destroyed. - warehouse:AddAsset(Carrier) + --warehouse:AddAsset(Carrier) + + -- Call arrived event for carrier. + warehouse:__Arrived(1, Carrier) end @@ -3011,17 +3082,38 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) group:SmokeOrange() end - -- Update pending request. - local request=self:_UpdatePending(group) + -- Update pending request. Increase ndelivered/ntransporthome and delete group from corresponding group set. + local request, isCargo=self:_UpdatePending(group) if request then -- Number of cargo assets still in group set. - local ncargo=request.cargogroupset:Count() + if isCargo==true then - -- Debug message. - local text=string.format("Cargo %d of %s arrived at warehouse %s. Assets still to deliver %d.", request.ndelivered, tostring(request.nasset), request.warehouse.alias, ncargo) - self:_DebugMessage(text, 5) + -- Current size of cargo group set. + local ncargo=request.cargogroupset:Count() + + -- Debug message. + local text=string.format("Cargo %d of %s arrived at warehouse %s. Assets still to deliver %d.", + request.ndelivered, tostring(request.nasset), request.warehouse.alias, ncargo) + self:_DebugMessage(text, 5) + + -- All cargo delivered. + if ncargo==0 then + self:__Delivered(5, request) + end + + elseif isCargo==false then + + -- Current size of cargo group set. + local ntransport=request.transportgroupset:Count() + + -- Debug message. + local text=string.format("Transport %d of %s arrived at warehouse %s. Assets still to deliver %d.", + request.ntransporthome, tostring(request.ntransport), request.warehouse.alias, ntransport) + self:_DebugMessage(text, 5) + + end -- Route mobile ground group to the warehouse. Group has 60 seconds to get there or it is despawned and added as asset to the new warehouse regardless. if group:IsGround() and group:GetSpeedMax()>1 then @@ -3029,12 +3121,7 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) end -- Move asset from pending queue into new warehouse. - request.warehouse:__AddAsset(60, group) - - -- All cargo delivered. - if request and ncargo==0 then - self:__Delivered(5, request) - end + request.warehouse:__AddAsset(60, group) end @@ -3058,6 +3145,7 @@ end -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group The group that has arrived at its destination. -- @return #WAREHOUSE.Pendingitem The updated request from the pending queue. +-- @return #boolean If true, group is a cargo asset. If false, group is a transport asset. If nil, group is neither cargo nor transport. function WAREHOUSE:_UpdatePending(group) -- Get request from group name. @@ -3066,27 +3154,53 @@ function WAREHOUSE:_UpdatePending(group) -- Get the IDs for this group. In particular, we use the asset ID to figure out which group was delivered. local wid,aid,rid=self:_GetIDsFromGroup(group) + local isCargo=nil + if request then - -- Loop over cargo groups. - for _,_cargogroup in pairs(request.cargogroupset:GetSetObjects()) do - local cargogroup=_cargogroup --Wrapper.Group#GROUP - - -- IDs of cargo group. - local cwid,caid,crid=self:_GetIDsFromGroup(cargogroup) - - -- Remove group from cargo group set. - if caid==aid then - request.cargogroupset:Remove(cargogroup:GetName()) - request.ndelivered=request.ndelivered+1 - break + -- If this request was already delivered. + if self.delivered[rid]==true then + + -- Loop over transport groups. + for _,_transportgroup in pairs(request.transportgroupset:GetSetObjects()) do + local transportgroup=_transportgroup --Wrapper.Group#GROUP + + -- IDs of cargo group. + local cwid,caid,crid=self:_GetIDsFromGroup(transportgroup) + + -- Remove group from transport group set and increase home counter. + if caid==aid then + request.transportgroupset:Remove(transportgroup:GetName()) + request.ntransporthome=request.ntransporthome+1 + isCargo=false + break + end end + + else + + -- Loop over cargo groups. + for _,_cargogroup in pairs(request.cargogroupset:GetSetObjects()) do + local cargogroup=_cargogroup --Wrapper.Group#GROUP + + -- IDs of cargo group. + local cwid,caid,crid=self:_GetIDsFromGroup(cargogroup) + + -- Remove group from cargo group set and increase delivered counter. + if caid==aid then + request.cargogroupset:Remove(cargogroup:GetName()) + request.ndelivered=request.ndelivered+1 + isCargo=true + break + end + end + end else self:E(self.wid..string.format("WARNING: pending request could not be updated since request did not exist in pending queue!")) end - return request,wid,aid,rid + return request, isCargo end @@ -3105,11 +3219,8 @@ function WAREHOUSE:onafterDelivered(From, Event, To, request) -- Make some noise :) self:_Fireworks(request.warehouse.coordinate) - -- Add table + -- Set delivered status for this request uid. self.delivered[request.uid]=true - - -- Remove pending request: - self:_DeleteQueueItem(request, self.pending) end @@ -3923,19 +4034,19 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) -- Check if at least one asset was requested. if request.nasset==0 then - self:E(self.wid..string.format("ERROR: Incorrect request. Request for zero assets not possible. Can happen when, e.g. \"all\" ground assets are requests but none in stock.")) + self:E(self.wid..string.format("ERROR: INVALID request. Request for zero assets not possible. Can happen when, e.g. \"all\" ground assets are requests but none in stock.")) valid=false end -- Request from enemy coalition? if self.coalition~=request.warehouse.coalition then - self:E(self.wid..string.format("ERROR: Incorrect request. Requesting warehouse is of wrong coaltion! Own coalition %d. Requesting warehouse %d", self.coalition, request.warehouse.coalition)) + self:E(self.wid..string.format("ERROR: INVALID request. Requesting warehouse is of wrong coaltion! Own coalition %d. Requesting warehouse %d", self.coalition, request.warehouse.coalition)) valid=false end -- Is receiving warehouse stopped? if request.warehouse:IsStopped() then - self:E(self.wid..string.format("ERROR: Incorrect request. Requesting warehouse is stopped!")) + self:E(self.wid..string.format("ERROR: INVALID request. Requesting warehouse is stopped!")) valid=false end @@ -3950,7 +4061,7 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) -- Delete invalid requests. for _,_request in pairs(invalid) do - self:E(self.wid..string.format("Deleting invalid request id=%d.",_request.uid)) + self:E(self.wid..string.format("Deleting INVALID request id=%d.",_request.uid)) self:_DeleteQueueItem(_request, self.queue) end @@ -4037,7 +4148,7 @@ function WAREHOUSE:_CheckRequestValid(request) local np_destination=request.airbase:GetParkingSpotsNumber(termtype) -- Debug info. - self:E(string.format("Asset attribute = %s, terminal type = %d, spots at departure = %d, destination = %d", asset.attribute, termtype, np_departure, np_destination)) + self:T(string.format("Asset attribute = %s, terminal type = %d, spots at departure = %d, destination = %d", asset.attribute, termtype, np_departure, np_destination)) -- Not enough parking at sending warehouse. --if (np_departure < request.nasset) and not (self.category==Airbase.Category.SHIP or self.category==Airbase.Category.HELIPAD) then @@ -4061,7 +4172,7 @@ function WAREHOUSE:_CheckRequestValid(request) -- TODO: May needs refinement if warehouse is on land and requestor is ship in harbour?! if (request.category==Airbase.Category.SHIP or self.category==Airbase.Category.SHIP) then self:E("ERROR: Incorrect request. Ground asset requested but warehouse or requestor is SHIP!") - --valid=false + valid=false end if asset_train then From 415b74019691fa7ee265fdf140634c669f76d892 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Wed, 5 Sep 2018 20:11:41 +0200 Subject: [PATCH 315/420] Workable solution? --- Moose Development/Moose/AI/AI_Cargo.lua | 66 ++++--------------- Moose Development/Moose/AI/AI_Cargo_APC.lua | 9 +-- .../Moose/AI/AI_Cargo_Airplane.lua | 33 ++++++++-- .../Moose/AI/AI_Cargo_Dispatcher.lua | 13 ++-- .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 2 +- .../AI/AI_Cargo_Dispatcher_Helicopter.lua | 2 +- .../Moose/AI/AI_Cargo_Helicopter.lua | 33 ++++++++-- 7 files changed, 83 insertions(+), 75 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua index bd379f299..81be2f8e3 100644 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -1,4 +1,4 @@ ---- **AI** -- (R2.3) - Models the intelligent transportation of infantry and other cargo. +--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo. -- -- === -- @@ -7,69 +7,25 @@ -- === -- -- @module AI.AI_Cargo --- @image AI_Cargo_Dispatching.JPG +-- @image Cargo.JPG --- @type AI_CARGO -- @extends Core.Fsm#FSM_CONTROLLABLE ---- Brings a dynamic cargo handling capability for AI groups. +--- Base class for the dynamic cargo handling capability for AI groups. -- --- Armoured Personnel Carriers (Carrier), Trucks, Jeeps and other ground based carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. --- The AI\_CARGO\Carrier module uses the @{Cargo} capabilities within the MOOSE framework. --- CARGO derived objects must be declared within the mission to make the AI\_CARGO\Carrier object recognize the cargo. +-- Carriers can be mobilized to intelligently transport infantry and other cargo within the simulation. +-- The AI_CARGO module uses the @{Cargo} capabilities within the MOOSE framework. +-- CARGO derived objects must be declared within the mission to make the AI_CARGO object recognize the cargo. -- Please consult the @{Cargo} module for more information. -- --- ## Cargo loading. --- --- The module will load automatically cargo when the Carriers are within boarding or loading range. --- The boarding or loading range is specified when the cargo is created in the simulation, and therefore, this range depends on the type of cargo --- and the specified boarding range. --- --- ## Enemies nearby. --- --- When the Carriers are approaching enemy units, something special is happening. --- The Carriers will stop moving, and the loaded infantry will unboard and follow the Carriers and will help to defend the group. --- The carrier will hold the route once the unboarded infantry is further than 50 meters from the Carriers, --- to ensure that the Carriers are not too far away from the following running infantry. --- Once all enemies are cleared, the infantry will board again automatically into the Carriers. Once boarded, the Carriers will follow its pre-defined route. --- --- A combat range needs to be specified in meters at the @{#AI_CARGO.New}() method. --- This combat range will trigger the unboarding of troops when enemies are within the combat range around the Carriers. --- During my tests, I've noticed that there is a balance between ensuring that the infantry is within sufficient hit range (effectiveness) versus --- vulnerability of the infantry. It all depends on the kind of enemies that are expected to be encountered. --- A combat range of 350 meters to 500 meters has been proven to be the most effective and efficient. --- --- ## Infantry health. --- --- When infantry is unboarded from the Carriers, the infantry is actually respawned into the battlefield. --- As a result, the unboarding infantry is very _healthy_ every time it unboards. --- This is due to the limitation of the DCS simulator, which is not able to specify the health of new spawned units as a parameter. --- However, infantry that was destroyed when unboarded and following the Carriers, won't be respawned again. Destroyed is destroyed. --- As a result, there is some additional strength that is gained when an unboarding action happens, but in terms of simulation balance this has --- marginal impact on the overall battlefield simulation. Fortunately, the firing strength of infantry is limited, and thus, respacing healthy infantry every --- time is not so much of an issue ... --- --- ## Control the Carriers on the map. --- --- It is possible also as a human ground commander to influence the path of the Carriers, by pointing a new path using the DCS user interface on the map. --- In this case, the Carriers will change the direction towards its new indicated route. However, there is a catch! --- Once the Carriers are near the enemy, and infantry is unboarded, the Carriers won't be able to hold the route until the infantry could catch up. --- The Carriers will simply drive on and won't stop! This is a limitation in ED that prevents user actions being controlled by the scripting engine. --- No workaround is possible on this. --- --- ## Cargo deployment. --- --- Using the @{#AI_CARGO.Deploy}() method, you are able to direct the Carriers towards a point on the battlefield to unboard/unload the cargo at the specific coordinate. --- The Carriers will follow nearby roads as much as possible, to ensure fast and clean cargo transportation between the objects and villages in the simulation environment. --- --- ## Cargo pickup. --- --- Using the @{#AI_CARGO.Pickup}() method, you are able to direct the Carriers towards a point on the battlefield to board/load the cargo at the specific coordinate. --- The Carriers will follow nearby roads as much as possible, to ensure fast and clean cargo transportation between the objects and villages in the simulation environment. --- --- +-- The derived classes from this module are: -- +-- * @{AI.AI_Cargo_APC} - Cargo transportation using APCs and other vehicles between zones. +-- * @{AI.AI_Cargo_Helicopter} - Cargo transportation using helicopters between zones. +-- * @{AI.AI_Cargo_Airplane} - Cargo transportation using airplanes to and from airbases. +-- -- @field #AI_CARGO AI_CARGO = { ClassName = "AI_CARGO", diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index f991346a9..9404c448a 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -1,4 +1,4 @@ ---- **AI** -- (R2.3) - Models the intelligent transportation of infantry and other cargo. +--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo. -- -- === -- @@ -13,11 +13,12 @@ -- @extends AI.AI_Cargo#AI_CARGO ---- Brings a dynamic cargo handling capability for AI groups. +--- Brings a dynamic cargo handling capability for an AI vehicle group. -- -- Armoured Personnel Carriers (APC), Trucks, Jeeps and other ground based carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. --- The AI\_CARGO\APC module uses the @{Cargo} capabilities within the MOOSE framework. --- CARGO derived objects must be declared within the mission to make the AI\_CARGO\APC object recognize the cargo. +-- +-- The AI_CARGO_APC class uses the @{Cargo} capabilities within the MOOSE framework. +-- @{Cargo} must be declared within the mission to make the AI_CARGO_APC object recognize the cargo. -- Please consult the @{Cargo} module for more information. -- -- ## Cargo loading. diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 882ea6d36..d19de0cb1 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -1,4 +1,4 @@ ---- **AI** -- (R2.3) - Models the intelligent transportation of infantry (cargo). +--- **AI** -- (R2.4) - Models the intelligent transportation of infantry (cargo). -- -- === -- @@ -13,7 +13,34 @@ -- @extends Core.Fsm#FSM_CONTROLLABLE ---- Implements the transportation of cargo by airplanes. +--- Brings a dynamic cargo handling capability for an AI airplane group. +-- +-- Airplane carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation between airbases. +-- +-- The AI_CARGO_AIRPLANE module uses the @{Cargo} capabilities within the MOOSE framework. +-- @{Cargo} must be declared within the mission to make AI_CARGO_AIRPLANE recognize the cargo. +-- Please consult the @{Cargo} module for more information. +-- +-- ## Cargo pickup. +-- +-- Using the @{#AI_CARGO_AIRPLANE.Pickup}() method, you are able to direct the helicopters towards a point on the battlefield to board/load the cargo at the specific coordinate. +-- Ensure that the landing zone is horizontally flat, and that trees cannot be found in the landing vicinity, or the helicopters won't land or will even crash! +-- +-- ## Cargo deployment. +-- +-- Using the @{#AI_CARGO_AIRPLANE.Deploy}() method, you are able to direct the helicopters towards a point on the battlefield to unboard/unload the cargo at the specific coordinate. +-- Ensure that the landing zone is horizontally flat, and that trees cannot be found in the landing vicinity, or the helicopters won't land or will even crash! +-- +-- ## Infantry health. +-- +-- When infantry is unboarded from the APCs, the infantry is actually respawned into the battlefield. +-- As a result, the unboarding infantry is very _healthy_ every time it unboards. +-- This is due to the limitation of the DCS simulator, which is not able to specify the health of new spawned units as a parameter. +-- However, infantry that was destroyed when unboarded, won't be respawned again. Destroyed is destroyed. +-- As a result, there is some additional strength that is gained when an unboarding action happens, but in terms of simulation balance this has +-- marginal impact on the overall battlefield simulation. Fortunately, the firing strength of infantry is limited, and thus, respacing healthy infantry every +-- time is not so much of an issue ... +-- -- -- @field #AI_CARGO_AIRPLANE AI_CARGO_AIRPLANE = { @@ -30,8 +57,6 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) local self = BASE:Inherit( self, AI_CARGO:New( Airplane, CargoSet ) ) -- #AI_CARGO_AIRPLANE - self.CargoSet = CargoSet -- Cargo.CargoGroup#CARGO_GROUP - self:AddTransition( "*", "Landed", "*" ) self:AddTransition( "*", "Home" , "*" ) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 1ffc9b9b9..3ee909975 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -16,25 +16,25 @@ --- A dynamic cargo handling capability for AI groups. -- -- Carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. --- The AI\_CARGO\_DISPATCHER module uses the @{Cargo} capabilities within the MOOSE framework, to enable Carrier GROUP objects +-- The AI_CARGO_DISPATCHER module uses the @{Cargo} capabilities within the MOOSE framework, to enable Carrier GROUP objects -- to transport @{Cargo} towards several deploy zones. --- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER object recognize the cargo. +-- @{Cargo} must be declared within the mission to make the AI_CARGO_DISPATCHER object recognize the cargo. -- Please consult the @{Cargo} module for more information. -- --- # 1) AI\_CARGO\_DISPATCHER constructor +-- # 1) AI_CARGO_DISPATCHER constructor -- -- * @{#AI_CARGO_DISPATCHER.New}(): Creates a new AI\_CARGO\_DISPATCHER object. -- --- # 2) AI\_CARGO\_DISPATCHER is a FSM +-- # 2) AI_CARGO_DISPATCHER is a FSM -- -- ![Process](..\Presentations\AI_PATROL\Dia2.JPG) -- --- ## 2.1) AI\_CARGO\_DISPATCHER States +-- ## 2.1) AI_CARGO_DISPATCHER States -- -- * **Monitoring**: The process is dispatching. -- * **Idle**: The process is idle. -- --- ## 2.2) AI\_CARGO\_DISPATCHER Events +-- ## 2.2) AI_CARGO_DISPATCHER Events -- -- * **Monitor**: Monitor and take action. -- * **Start**: Start the transport process. @@ -47,6 +47,7 @@ -- * **Deploy**: Deploy cargo to a location. -- * **Unload**: Unload the cargo. -- * **Unloaded**: Flag that the cargo is unloaded. +-- * **Deployed**: All cargo is unloaded from the carriers in the group. -- * **Home**: A Carrier is going home. -- -- # 3) Enhance your mission scripts with **Tailored** Event Handling! diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index 0f63f2f94..78fe76f31 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -1,4 +1,4 @@ ---- **AI** -- Models the intelligent transportation of infantry and other cargo using APCs. +--- **AI** -- (2.4) - Models the intelligent transportation of infantry and other cargo using APCs. -- -- **Features:** -- diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index 61705d84d..5c64ef549 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -1,4 +1,4 @@ ---- **AI** -- Models the intelligent transportation of infantry and other cargo using Helicopters. +--- **AI** -- (2.4) - Models the intelligent transportation of infantry and other cargo using Helicopters. -- -- **Features:** -- diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index b05c75dfe..7e32c9495 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -1,4 +1,4 @@ ---- **AI** -- (R2.3) - Models the intelligent transportation of infantry (cargo). +--- **AI** -- (R2.4) - Models the intelligent transportation of infantry (cargo). -- -- === -- @@ -13,7 +13,34 @@ -- @extends Core.Fsm#FSM_CONTROLLABLE ---- # AI\_CARGO\_TROOPS class, extends @{Core.Fsm#FSM_CONTROLLABLE} +--- Brings a dynamic cargo handling capability for an AI helicopter group. +-- +-- Helicopter carriers can be mobilized to intelligently transport infantry and other cargo within the simulation. +-- +-- The AI_CARGO_HELICOPTER class uses the @{Cargo} capabilities within the MOOSE framework. +-- @{Cargo} must be declared within the mission to make the AI_CARGO_HELICOPTER object recognize the cargo. +-- Please consult the @{Cargo} module for more information. +-- +-- ## Cargo pickup. +-- +-- Using the @{#AI_CARGO_HELICOPTER.Pickup}() method, you are able to direct the helicopters towards a point on the battlefield to board/load the cargo at the specific coordinate. +-- Ensure that the landing zone is horizontally flat, and that trees cannot be found in the landing vicinity, or the helicopters won't land or will even crash! +-- +-- ## Cargo deployment. +-- +-- Using the @{#AI_CARGO_HELICOPTER.Deploy}() method, you are able to direct the helicopters towards a point on the battlefield to unboard/unload the cargo at the specific coordinate. +-- Ensure that the landing zone is horizontally flat, and that trees cannot be found in the landing vicinity, or the helicopters won't land or will even crash! +-- +-- ## Infantry health. +-- +-- When infantry is unboarded from the APCs, the infantry is actually respawned into the battlefield. +-- As a result, the unboarding infantry is very _healthy_ every time it unboards. +-- This is due to the limitation of the DCS simulator, which is not able to specify the health of new spawned units as a parameter. +-- However, infantry that was destroyed when unboarded, won't be respawned again. Destroyed is destroyed. +-- As a result, there is some additional strength that is gained when an unboarding action happens, but in terms of simulation balance this has +-- marginal impact on the overall battlefield simulation. Fortunately, the firing strength of infantry is limited, and thus, respacing healthy infantry every +-- time is not so much of an issue ... +-- -- -- === -- @@ -34,8 +61,6 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) local self = BASE:Inherit( self, AI_CARGO:New( Helicopter, CargoSet ) ) -- #AI_CARGO_HELICOPTER - self.CargoSet = CargoSet -- Cargo.CargoGroup#CARGO_GROUP - self.Zone = ZONE_GROUP:New( Helicopter:GetName(), Helicopter, 300 ) self:SetStartState( "Unloaded" ) From ab19c696c3c3bedc378337716a5717803dc20514 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Wed, 5 Sep 2018 20:55:56 +0200 Subject: [PATCH 316/420] Fixing an issue with cargo links. --- Moose Development/Moose/AI/AI_Cargo.lua | 4 ++-- Moose Development/Moose/AI/AI_Cargo_APC.lua | 6 +++--- Moose Development/Moose/AI/AI_Cargo_Airplane.lua | 6 +++--- Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua | 8 ++++---- Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua | 4 ++-- .../Moose/AI/AI_Cargo_Dispatcher_Airplane.lua | 2 +- .../Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua | 4 ++-- Moose Development/Moose/AI/AI_Cargo_Helicopter.lua | 6 +++--- 8 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua index 81be2f8e3..8e145a8c4 100644 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -16,9 +16,9 @@ --- Base class for the dynamic cargo handling capability for AI groups. -- -- Carriers can be mobilized to intelligently transport infantry and other cargo within the simulation. --- The AI_CARGO module uses the @{Cargo} capabilities within the MOOSE framework. +-- The AI_CARGO module uses the @{Cargo.Cargo} capabilities within the MOOSE framework. -- CARGO derived objects must be declared within the mission to make the AI_CARGO object recognize the cargo. --- Please consult the @{Cargo} module for more information. +-- Please consult the @{Cargo.Cargo} module for more information. -- -- The derived classes from this module are: -- diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 9404c448a..fb16616ea 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -17,9 +17,9 @@ -- -- Armoured Personnel Carriers (APC), Trucks, Jeeps and other ground based carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. -- --- The AI_CARGO_APC class uses the @{Cargo} capabilities within the MOOSE framework. --- @{Cargo} must be declared within the mission to make the AI_CARGO_APC object recognize the cargo. --- Please consult the @{Cargo} module for more information. +-- The AI_CARGO_APC class uses the @{Cargo.Cargo} capabilities within the MOOSE framework. +-- @{Cargo.Cargo} must be declared within the mission to make the AI_CARGO_APC object recognize the cargo. +-- Please consult the @{Cargo.Cargo} module for more information. -- -- ## Cargo loading. -- diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index d19de0cb1..0b4be9907 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -17,9 +17,9 @@ -- -- Airplane carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation between airbases. -- --- The AI_CARGO_AIRPLANE module uses the @{Cargo} capabilities within the MOOSE framework. --- @{Cargo} must be declared within the mission to make AI_CARGO_AIRPLANE recognize the cargo. --- Please consult the @{Cargo} module for more information. +-- The AI_CARGO_AIRPLANE module uses the @{Cargo.Cargo} capabilities within the MOOSE framework. +-- @{Cargo.Cargo} must be declared within the mission to make AI_CARGO_AIRPLANE recognize the cargo. +-- Please consult the @{Cargo.Cargo} module for more information. -- -- ## Cargo pickup. -- diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 3ee909975..ed7148e8a 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -16,10 +16,10 @@ --- A dynamic cargo handling capability for AI groups. -- -- Carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. --- The AI_CARGO_DISPATCHER module uses the @{Cargo} capabilities within the MOOSE framework, to enable Carrier GROUP objects --- to transport @{Cargo} towards several deploy zones. --- @{Cargo} must be declared within the mission to make the AI_CARGO_DISPATCHER object recognize the cargo. --- Please consult the @{Cargo} module for more information. +-- The AI_CARGO_DISPATCHER module uses the @{Cargo.Cargo} capabilities within the MOOSE framework, to enable Carrier GROUP objects +-- to transport @{Cargo.Cargo} towards several deploy zones. +-- @{Cargo.Cargo} must be declared within the mission to make the AI_CARGO_DISPATCHER object recognize the cargo. +-- Please consult the @{Cargo.Cargo} module for more information. -- -- # 1) AI_CARGO_DISPATCHER constructor -- diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index 78fe76f31..020e6c15e 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -36,7 +36,7 @@ -- -- Especially to learn how to **Tailor the different cargo handling events**, this will be very useful! -- --- On top, the AI_CARGO_DISPATCHER_APC class uses the @{Cargo} capabilities within the MOOSE framework. +-- On top, the AI_CARGO_DISPATCHER_APC class uses the @{Cargo.Cargo} capabilities within the MOOSE framework. -- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class. -- CARGO derived objects must be declared within the mission to make the AI_CARGO_DISPATCHER_HELICOPTER object recognize the cargo. -- @@ -98,7 +98,7 @@ AI_CARGO_DISPATCHER_APC = { --- Creates a new AI_CARGO_DISPATCHER_APC object. -- @param #AI_CARGO_DISPATCHER_APC self -- @param Core.Set#SET_GROUP APCSet The collection of APC @{Wrapper.Group}s. --- @param Core.Set#SET_CARGO CargoSet The collection of @{Cargo} derived objects. +-- @param Core.Set#SET_CARGO CargoSet The collection of @{Cargo.Cargo} derived objects. -- @param Core.Set#SET_ZONE PickupZoneSet (optional) The collection of pickup @{Zone}s, which are used to where the cargo can be picked up by the APCs. If nil, then cargo can be picked up everywhere. -- @param Core.Set#SET_ZONE DeployZoneSet The collection of deploy @{Zone}s, which are used to where the cargo will be deployed by the APCs. -- @param DCS#Distance CombatRadius The cargo will be unloaded from the APC and engage the enemy if the enemy is within CombatRadius range. The radius is in meters, the default value is 500 meters. diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua index fc6bde8de..e094b3532 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua @@ -29,7 +29,7 @@ -- -- Especially to learn how to **Tailor the different cargo handling events**, this will be very useful! -- --- On top, the AI_CARGO_DISPATCHER_AIRPLANE class uses the @{Cargo} capabilities within the MOOSE framework. +-- On top, the AI_CARGO_DISPATCHER_AIRPLANE class uses the @{Cargo.Cargo} capabilities within the MOOSE framework. -- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class. -- CARGO derived objects must be declared within the mission to make the AI_CARGO_DISPATCHER_HELICOPTER object recognize the cargo. -- diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index 5c64ef549..8da835dff 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -32,7 +32,7 @@ -- -- Especially to learn how to **Tailor the different cargo handling events**, this will be very useful! -- --- On top, the AI_CARGO_DISPATCHER_HELICOPTER class uses the @{Cargo} capabilities within the MOOSE framework. +-- On top, the AI_CARGO_DISPATCHER_HELICOPTER class uses the @{Cargo.Cargo} capabilities within the MOOSE framework. -- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class. -- CARGO derived objects must be declared within the mission to make the AI_CARGO_DISPATCHER_HELICOPTER object recognize the cargo. -- @@ -103,7 +103,7 @@ AI_CARGO_DISPATCHER_HELICOPTER = { --- Creates a new AI_CARGO_DISPATCHER_HELICOPTER object. -- @param #AI_CARGO_DISPATCHER_HELICOPTER self -- @param Core.Set#SET_GROUP HelicopterSet The collection of Helicopter @{Wrapper.Group}s. --- @param Core.Set#SET_CARGO CargoSet The collection of @{Cargo} derived objects. +-- @param Core.Set#SET_CARGO CargoSet The collection of @{Cargo.Cargo} derived objects. -- @param Core.Set#SET_ZONE PickupZoneSet (optional) The collection of pickup @{Zone}s, which are used to where the cargo can be picked up by the APCs. If nil, then cargo can be picked up everywhere. -- @param Core.Set#SET_ZONE DeployZoneSet The collection of deploy @{Zone}s, which are used to where the cargo will be deployed by the Helicopters. -- @return #AI_CARGO_DISPATCHER_HELICOPTER diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 7e32c9495..2c82fb1e3 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -17,9 +17,9 @@ -- -- Helicopter carriers can be mobilized to intelligently transport infantry and other cargo within the simulation. -- --- The AI_CARGO_HELICOPTER class uses the @{Cargo} capabilities within the MOOSE framework. --- @{Cargo} must be declared within the mission to make the AI_CARGO_HELICOPTER object recognize the cargo. --- Please consult the @{Cargo} module for more information. +-- The AI_CARGO_HELICOPTER class uses the @{Cargo.Cargo} capabilities within the MOOSE framework. +-- @{Cargo.Cargo} must be declared within the mission to make the AI_CARGO_HELICOPTER object recognize the cargo. +-- Please consult the @{Cargo.Cargo} module for more information. -- -- ## Cargo pickup. -- From ea60e4358499c8378cbce0c61750e026293ada04 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Wed, 5 Sep 2018 21:24:11 +0200 Subject: [PATCH 317/420] Documentation updates --- .../Moose/AI/AI_Cargo_Dispatcher.lua | 56 ++++++++++++++----- .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 2 +- .../Moose/AI/AI_Cargo_Dispatcher_Airplane.lua | 2 +- .../AI/AI_Cargo_Dispatcher_Helicopter.lua | 2 +- 4 files changed, 45 insertions(+), 17 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index ed7148e8a..d8319f7cc 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -27,8 +27,35 @@ -- -- # 2) AI_CARGO_DISPATCHER is a FSM -- --- ![Process](..\Presentations\AI_PATROL\Dia2.JPG) +-- This section must be read as follows. Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed. +-- The first column is the **From** state, the second column the **Event**, and the third column the **To** state. -- +-- So, each of the rows have the following structure. +-- +-- * **From** => **Event** => **To** +-- +-- Important to know is that an event can only be executed if the **current state** is the **From** state. +-- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed, +-- and the resulting state will be the **To** state. +-- +-- These are the different possible state transitions of this state machine implementation: +-- +-- * Idle => Start => Monitoring +-- * Monitoring => Monitor => Monitoring +-- * Monitoring => Stop => Idle +-- +-- * Monitoring => Pickup => Monitoring +-- * Monitoring => Load => Monitoring +-- * Monitoring => Loading => Monitoring +-- * Monitoring => Loaded => Monitoring +-- * Monitoring => PickedUp => Monitoring +-- * Monitoring => Deploy => Monitoring +-- * Monitoring => Unload => Monitoring +-- * Monitoring => Unloaded => Monitoring +-- * Monitoring => Deployed => Monitoring +-- * Monitoring => Home => Monitoring +-- +-- -- ## 2.1) AI_CARGO_DISPATCHER States -- -- * **Monitoring**: The process is dispatching. @@ -36,9 +63,10 @@ -- -- ## 2.2) AI_CARGO_DISPATCHER Events -- --- * **Monitor**: Monitor and take action. -- * **Start**: Start the transport process. -- * **Stop**: Stop the transport process. +-- * **Monitor**: Monitor and take action. +-- -- * **Pickup**: Pickup cargo. -- * **Load**: Load the cargo. -- * **Loading**: The dispatcher is coordinating the loading of a cargo. @@ -319,7 +347,7 @@ -- * @{#AI_CARGO_DISPATCHER.SetDeployRadius}(): Sets or randomizes the deploy location for the carrier around the cargo coordinate in a radius defined an outer and an optional inner radius. -- * @{#AI_CARGO_DISPATCHER.SetDeploySpeed}(): Set the speed or randomizes the speed in km/h to deploy the cargo. -- --- #) 5. Set the home zone when there isn't any more cargo to pickup. +-- # 5) Set the home zone when there isn't any more cargo to pickup. -- -- A home zone can be specified to where the Carriers will move when there isn't any cargo left for pickup. -- Use @{#AI_CARGO_DISPATCHER.SetHomeZone}() to specify the home zone. @@ -372,19 +400,19 @@ function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo ) self:AddTransition( "Monitoring", "Stop", "Idle" ) - self:AddTransition( "*", "Pickup", "*" ) - self:AddTransition( "*", "Load", "*" ) - self:AddTransition( "*", "Loading", "*" ) - self:AddTransition( "*", "Loaded", "*" ) - self:AddTransition( "*", "PickedUp", "*" ) + self:AddTransition( "Monitoring", "Pickup", "Monitoring" ) + self:AddTransition( "Monitoring", "Load", "Monitoring" ) + self:AddTransition( "Monitoring", "Loading", "Monitoring" ) + self:AddTransition( "Monitoring", "Loaded", "Monitoring" ) + self:AddTransition( "Monitoring", "PickedUp", "Monitoring" ) - self:AddTransition( "*", "Deploy", "*" ) - self:AddTransition( "*", "Unload", "*" ) - self:AddTransition( "*", "Unloading", "*" ) - self:AddTransition( "*", "Unloaded", "*" ) - self:AddTransition( "*", "Deployed", "*" ) + self:AddTransition( "Monitoring", "Deploy", "Monitoring" ) + self:AddTransition( "Monitoring", "Unload", "Monitoring" ) + self:AddTransition( "Monitoring", "Unloading", "Monitoring" ) + self:AddTransition( "Monitoring", "Unloaded", "Monitoring" ) + self:AddTransition( "Monitoring", "Deployed", "Monitoring" ) - self:AddTransition( "*", "Home", "*" ) + self:AddTransition( "Monitoring", "Home", "Monitoring" ) self.MonitorTimeInterval = 30 self.DeployRadiusInner = 200 diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index 020e6c15e..fffaaf90f 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -32,7 +32,7 @@ -- The AI_CARGO_DISPATCHER_APC module is derived from the AI_CARGO_DISPATCHER module. -- -- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_APC class, it is recommended that you --- ** first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} !!!** +-- **first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!!** -- -- Especially to learn how to **Tailor the different cargo handling events**, this will be very useful! -- diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua index e094b3532..1df08d56f 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua @@ -25,7 +25,7 @@ -- The AI_CARGO_DISPATCHER_AIRPLANE module is derived from the AI_CARGO_DISPATCHER module. -- -- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_AIRPLANE class, it is recommended that you --- ** first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} !!!** +-- **first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!!** -- -- Especially to learn how to **Tailor the different cargo handling events**, this will be very useful! -- diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index 8da835dff..69beb31f4 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -28,7 +28,7 @@ -- The AI_CARGO_DISPATCHER_HELICOPTER module is derived from the AI_CARGO_DISPATCHER module. -- -- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_HELICOPTER class, it is recommended that you --- ** first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} !!!** +-- **first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!!** -- -- Especially to learn how to **Tailor the different cargo handling events**, this will be very useful! -- From c160ecbce53d705844ea5e5889706b1addbc7b94 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Wed, 5 Sep 2018 21:37:01 +0200 Subject: [PATCH 318/420] Cargo documentation optimization --- Moose Development/Moose/Cargo/CargoGroup.lua | 12 ++++-------- Moose Development/Moose/Cargo/CargoSlingload.lua | 5 +++++ Moose Development/Moose/Cargo/CargoUnit.lua | 5 ++++- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index ade21edd0..95ac1c266 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -1,10 +1,6 @@ --- **Cargo** -- Management of grouped cargo logistics, which are based on a @{Wrapper.Group} object. -- -- === --- --- ![Banner Image](..\Presentations\CARGO\Dia1.JPG) --- --- === -- -- ### [Demo Missions]() -- @@ -33,12 +29,12 @@ do -- CARGO_GROUP -- -- The above cargo classes are used by the AI\_CARGO\_ classes to allow AI groups to transport cargo: -- - -- * AI Armoured Personnel Carriers to transport cargo and engage in battles, using the @{AI.AI_Cargo_APC#AI_CARGO_APC} class. - -- * AI Helicopters to transport cargo, using the @{AI.AI_Cargo_Helicopter#AI_CARGO_HELICOPTER} class. - -- * AI Planes to transport cargo, using the @{AI.AI_Cargo_Plane#AI_CARGO_PLANE} class. + -- * AI Armoured Personnel Carriers to transport cargo and engage in battles, using the @{AI.AI_Cargo_APC} module. + -- * AI Helicopters to transport cargo, using the @{AI.AI_Cargo_Helicopter} module. + -- * AI Planes to transport cargo, using the @{AI.AI_Cargo_Airplane} module. -- * AI Ships is planned. -- - -- The above cargo classes are also used by the TASK\_CARGO\_ classes to allow human players to transport cargo as part of a tasking: + -- The above cargo classes are also used by the TASK_CARGO_ classes to allow human players to transport cargo as part of a tasking: -- -- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT} to transport cargo by human players. -- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_CSAR} to transport downed pilots by human players. diff --git a/Moose Development/Moose/Cargo/CargoSlingload.lua b/Moose Development/Moose/Cargo/CargoSlingload.lua index ff948d4e6..452bbe3e8 100644 --- a/Moose Development/Moose/Cargo/CargoSlingload.lua +++ b/Moose Development/Moose/Cargo/CargoSlingload.lua @@ -25,6 +25,11 @@ do -- CARGO_SLINGLOAD --- Defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. -- + -- The above cargo classes are also used by the TASK_CARGO_ classes to allow human players to transport cargo as part of a tasking: + -- + -- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT} to transport cargo by human players. + -- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_CSAR} to transport downed pilots by human players. + -- -- === -- -- @field #CARGO_SLINGLOAD diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index 68e77ac61..18ff9743d 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -23,7 +23,10 @@ do -- CARGO_UNIT -- @extends Cargo.Cargo#CARGO_REPRESENTABLE --- Defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. - -- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO\_UNIT objects to and from carriers. + -- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO_UNIT objects to and from carriers. + -- Note that ground forces behave in a group, and thus, act in formation, regardless if one unit is commanded to move. + -- + -- This class is used in CARGO_GROUP, and is not meant to be used by mission designers individually. -- -- === -- From e97badd092cf49c27da5e5e5650428ec1cd2c843 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Wed, 5 Sep 2018 21:57:50 +0200 Subject: [PATCH 319/420] Optimize --- Moose Development/Moose/Cargo/CargoCrate.lua | 12 ++++++++++++ Moose Development/Moose/Cargo/CargoGroup.lua | 4 +--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua index 53011f56d..a1258d633 100644 --- a/Moose Development/Moose/Cargo/CargoCrate.lua +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -25,6 +25,18 @@ do -- CARGO_CRATE --- Defines a cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier. -- Use the event functions as described above to Load, UnLoad, Board, UnBoard the CARGO\_CRATE objects to and from carriers. -- + -- The above cargo classes are used by the following AI_CARGO_ classes to allow AI groups to transport cargo: + -- + -- * AI Armoured Personnel Carriers to transport cargo and engage in battles, using the @{AI.AI_Cargo_APC} module. + -- * AI Helicopters to transport cargo, using the @{AI.AI_Cargo_Helicopter} module. + -- * AI Planes to transport cargo, using the @{AI.AI_Cargo_Airplane} module. + -- * AI Ships is planned. + -- + -- The above cargo classes are also used by the TASK_CARGO_ classes to allow human players to transport cargo as part of a tasking: + -- + -- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT} to transport cargo by human players. + -- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_CSAR} to transport downed pilots by human players. + -- -- === -- -- @field #CARGO_CRATE diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 95ac1c266..abcbb1e83 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -27,7 +27,7 @@ do -- CARGO_GROUP --- Defines a cargo that is represented by a @{Wrapper.Group} object within the simulator. -- The cargo can be Loaded, UnLoaded, Boarded, UnBoarded to and from Carriers. -- - -- The above cargo classes are used by the AI\_CARGO\_ classes to allow AI groups to transport cargo: + -- The above cargo classes are used by the following AI_CARGO_ classes to allow AI groups to transport cargo: -- -- * AI Armoured Personnel Carriers to transport cargo and engage in battles, using the @{AI.AI_Cargo_APC} module. -- * AI Helicopters to transport cargo, using the @{AI.AI_Cargo_Helicopter} module. @@ -39,8 +39,6 @@ do -- CARGO_GROUP -- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT} to transport cargo by human players. -- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_CSAR} to transport downed pilots by human players. -- - -- The - -- -- @field #CARGO_GROUP CARGO_GROUP -- CARGO_GROUP = { From a039745b0f17c13092edfc3811e3e942a461cc8d Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 5 Sep 2018 21:58:44 +0200 Subject: [PATCH 320/420] Warehouse v0.3.6 moved updatepending function to addasset. not a good idea, since now the pending queue of the wrong warehouse is updated! --- .../Moose/Functional/Warehouse.lua | 108 ++++++++++++------ 1 file changed, 72 insertions(+), 36 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index c70314a4b..579c2c3d1 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -927,7 +927,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.3.5w" +WAREHOUSE.version="0.3.6" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -1912,9 +1912,70 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu if group then + -- Update pending request. Increase ndelivered/ntransporthome and delete group from corresponding group set. + local request, isCargo=self:_UpdatePending(group) + + if request then + + -- Number of cargo assets still in group set. + if isCargo==true then + + -- Current size of cargo group set. + local ncargo=request.cargogroupset:Count() + + -- Debug message. + local text=string.format("Cargo %d of %s added to warehouse %s stock. Assets still to deliver %d.", + request.ndelivered, tostring(request.nasset), request.warehouse.alias, ncargo) + self:_DebugMessage(text, 5) + + -- All cargo delivered. + if ncargo==0 then + self:__Delivered(5, request) + end + + elseif isCargo==false then + + -- Current size of cargo group set. + local ntransport=request.transportgroupset:Count() + + -- Debug message. + local text=string.format("Transport %d of %s added to warehouse %s stock. Transports still missing %d.", + request.ntransporthome, tostring(request.ntransport), request.warehouse.alias, ntransport) + self:_DebugMessage(text, 5) + + end + + -- Get the asset from the global DB. + local asset=self:_FindAssetInDB(group) + + -- Note the group is only added once, i.e. the ngroups parameter is ignored here. + -- This is because usually these request comes from an asset that has been transfered from another warehouse and hence should only be added once. + if asset~=nil then + self:_DebugMessage(string.format("Adding known asset uid=%d, attribute = %s to warehouse stock.", asset.uid, asset.attribute), 5) + table.insert(self.stock, asset) + else + self:_ErrorMessage(string.format("ERROR known asset could not be found in global warehouse db!"), 0) + end + + else + + -- Debug info. + self:_DebugMessage(self.wid..string.format("Adding %d NEW assets of group %s to warehouse %s.", n, tostring(group:GetName()), self.alias), 5) + + -- This is a group that is not in the db yet. Add it n times. + local assets=self:_RegisterAsset(group, n, forceattribute) + + -- Add created assets to stock of this warehouse. + for _,asset in pairs(assets) do + table.insert(self.stock, asset) + end + + end + + --[[ -- Get unique ids from group name. local wid,aid,rid=self:_GetIDsFromGroup(group) - + -- Check if this is an known or a new asset group. if aid~=nil and wid~=nil then @@ -1943,6 +2004,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu table.insert(self.stock, asset) end end + ]] -- Destroy group if it is alive. -- TODO: This causes a problem, when a completely new asset is added, i.e. not from a template group. @@ -2548,7 +2610,6 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Set time stamp. Pending.timestamp=timer.getAbsTime() - env.info("Timestamp="..Pending.timestamp) -- Spawn assets of this request. local _spawngroups=self:_SpawnAssetRequest(Pending) --Core.Set#SET_GROUP @@ -2567,6 +2628,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Add groups to pending item. Pending.cargogroupset=_spawngroups + --Pending.cargogroupset:FilterStart() ------------------------------------------------------------------------------------------------------------------------------------ -- Self request: assets are spawned at warehouse but not transported anywhere. @@ -3081,39 +3143,11 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) if self.Debug then group:SmokeOrange() end - - -- Update pending request. Increase ndelivered/ntransporthome and delete group from corresponding group set. - local request, isCargo=self:_UpdatePending(group) + -- Get request from group name. + local request=self:_GetRequestOfGroup(group, self.pending) + if request then - - -- Number of cargo assets still in group set. - if isCargo==true then - - -- Current size of cargo group set. - local ncargo=request.cargogroupset:Count() - - -- Debug message. - local text=string.format("Cargo %d of %s arrived at warehouse %s. Assets still to deliver %d.", - request.ndelivered, tostring(request.nasset), request.warehouse.alias, ncargo) - self:_DebugMessage(text, 5) - - -- All cargo delivered. - if ncargo==0 then - self:__Delivered(5, request) - end - - elseif isCargo==false then - - -- Current size of cargo group set. - local ntransport=request.transportgroupset:Count() - - -- Debug message. - local text=string.format("Transport %d of %s arrived at warehouse %s. Assets still to deliver %d.", - request.ntransporthome, tostring(request.ntransport), request.warehouse.alias, ntransport) - self:_DebugMessage(text, 5) - - end -- Route mobile ground group to the warehouse. Group has 60 seconds to get there or it is despawned and added as asset to the new warehouse regardless. if group:IsGround() and group:GetSpeedMax()>1 then @@ -3121,7 +3155,7 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) end -- Move asset from pending queue into new warehouse. - request.warehouse:__AddAsset(60, group) + request.warehouse:__AddAsset(60, group) end @@ -3172,6 +3206,7 @@ function WAREHOUSE:_UpdatePending(group) if caid==aid then request.transportgroupset:Remove(transportgroup:GetName()) request.ntransporthome=request.ntransporthome+1 + env.info("FF transport back home # "..request.ntransporthome) isCargo=false break end @@ -3190,6 +3225,7 @@ function WAREHOUSE:_UpdatePending(group) if caid==aid then request.cargogroupset:Remove(cargogroup:GetName()) request.ndelivered=request.ndelivered+1 + env.info("FF delivered cargo # "..request.ndelivered) isCargo=true break end @@ -5113,7 +5149,7 @@ function WAREHOUSE:_DeleteQueueItem(qitem, queue) for i=1,#queue do local _item=queue[i] --#WAREHOUSE.Queueitem if _item.uid==qitem.uid then - self:E(self.wid..string.format("Deleting queue item %d.", qitem.uid)) + self:I(self.wid..string.format("Deleting queue item %d.", qitem.uid)) table.remove(queue,i) break end From 012122e8daa66d9495df7644e990b5447164a6c7 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 6 Sep 2018 16:22:56 +0200 Subject: [PATCH 321/420] Warehouse v0.3.6w --- .../Moose/Functional/Warehouse.lua | 293 ++++++++++-------- 1 file changed, 168 insertions(+), 125 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 579c2c3d1..c1e1813f8 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -45,7 +45,8 @@ -- @field #table stock Table holding all assets in stock. Table entries are of type @{#WAREHOUSE.Assetitem}. -- @field #table queue Table holding all queued requests. Table entries are of type @{#WAREHOUSE.Queueitem}. -- @field #table pending Table holding all pending requests, i.e. those that are currently in progress. Table elements are of type @{#WAREHOUSE.Pendingitem}. --- @field #table delivered Table holding all delivered requests. +-- @field #table transporting Table holding assets currently transporting cargo assets. +-- @field #table delivered Table holding all delivered requests. Table elements are #boolean. If true, all cargo has been delivered. -- @field #table defending Table holding all defending requests, i.e. self requests that were if the warehouse is under attack. Table elements are of type @{#WAREHOUSE.Pendingitem}. -- @field Core.Zone#ZONE portzone Zone defining the port of a warehouse. This is where naval assets are spawned. -- @field #table shippinglanes Table holding the user defined shipping between warehouses. @@ -342,7 +343,7 @@ -- Due to the fact that a warehouse holds (or can hold) a lot of valuable assets, it makes a (potentially) juicy target for enemy attacks. -- There are several interesting situations, which can occurr. -- --- ## Capturing a Warehouse' Airbase +-- ## Capturing a Warehouses Airbase -- -- If a warehouse has an associated airbase, it can be captured by the enemy. In this case, the warehouse looses its ability so employ all airborne assets and is also cut-off -- from supply by airplanes. Supply of ground troops via helicopters is still possible, because they deliver the troops into the spawn zone. @@ -782,6 +783,7 @@ WAREHOUSE = { stock = {}, queue = {}, pending = {}, + transporting = {}, delivered = {}, defending = {}, portzone = nil, @@ -920,14 +922,16 @@ WAREHOUSE.TransportType = { -- @type WAREHOUSE.db -- @field #number AssetID Unique ID of each asset. This is a running number, which is increased each time a new asset is added. -- @field #table Assets Table holding registered assets, which are of type @{Functional.Warehouse#WAREHOUSE.Assetitem}. +-- @field #table Warehouses Table holding all defined @{#WAREHOUSE} objects by their unique ids. WAREHOUSE.db = { - AssetID = 0, - Assets = {}, + AssetID = 0, + Assets = {}, + Warehouses = {} } --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.3.6" +WAREHOUSE.version="0.3.6w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -1018,6 +1022,9 @@ function WAREHOUSE:New(warehouse, alias) self.zone=ZONE_RADIUS:New(string.format("Warehouse zone %s", self.warehouse:GetName()), warehouse:GetVec2(), 500) self.spawnzone=ZONE_RADIUS:New(string.format("Warehouse %s spawn zone", self.warehouse:GetName()), warehouse:GetVec2(), 200) + -- Add warehouse to database. + WAREHOUSE.db.Warehouses[self.uid]=self + -- Start State. self:SetStartState("NotReadyYet") @@ -1151,7 +1158,8 @@ function WAREHOUSE:New(warehouse, alias) -- @param #WAREHOUSE.Queueitem Request Information table of the request. - --- Triggers the FSM event "Arrived", i.e. when a group has arrived at the destination. + --- Triggers the FSM event "Arrived", i.e. when a group has arrived at the destination warehosue. + -- This function should always be called from the receiving and not the sending warehouse because assets are added back to the -- @function [parent=#WAREHOUSE] Arrived -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group Group that has arrived. @@ -1911,54 +1919,75 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu end if group then + + -- Try to get UIDs from group name. Is this group a known or a new asset? + local wid,aid,rid=self:_GetIDsFromGroup(group) - -- Update pending request. Increase ndelivered/ntransporthome and delete group from corresponding group set. - local request, isCargo=self:_UpdatePending(group) - - if request then - - -- Number of cargo assets still in group set. - if isCargo==true then + if wid and aid and rid then + --------------------------- + -- This is a known asset -- + --------------------------- - -- Current size of cargo group set. - local ncargo=request.cargogroupset:Count() + -- Get the warehouse this group belonged to. (could also be the same for self requests). + local warehouseOld=self:_FindWarehouseInDB(wid) + + -- Now get the request from the pending queue and update it. + + -- Update pending request. Increase ndelivered/ntransporthome and delete group from corresponding group set. + local request, isCargo=warehouseOld:_UpdatePending(group) + + if request then + + -- Number of cargo assets still in group set. + if isCargo==true then - -- Debug message. - local text=string.format("Cargo %d of %s added to warehouse %s stock. Assets still to deliver %d.", - request.ndelivered, tostring(request.nasset), request.warehouse.alias, ncargo) - self:_DebugMessage(text, 5) + -- Current size of cargo group set. + local ncargo=request.cargogroupset:Count() + + -- Debug message. + local text=string.format("Cargo %d of %s added to warehouse %s stock. Assets still to deliver %d.", + request.ndelivered, tostring(request.nasset), request.warehouse.alias, ncargo) + self:_DebugMessage(text, 5) + + -- All cargo delivered. + if ncargo==0 then + warehouseOld:Delivered(request) + end + + elseif isCargo==false then + + -- Current size of cargo group set. + local ntransport=request.transportgroupset:Count() + + -- Debug message. + local text=string.format("Transport %d of %s added to warehouse %s stock. Transports still missing %d.", + request.ntransporthome, tostring(request.ntransport), request.warehouse.alias, ntransport) + self:_DebugMessage(text, 5) - -- All cargo delivered. - if ncargo==0 then - self:__Delivered(5, request) end + + -- Get the asset from the global DB. + local asset=self:_FindAssetInDB(group) - elseif isCargo==false then - - -- Current size of cargo group set. - local ntransport=request.transportgroupset:Count() - - -- Debug message. - local text=string.format("Transport %d of %s added to warehouse %s stock. Transports still missing %d.", - request.ntransporthome, tostring(request.ntransport), request.warehouse.alias, ntransport) - self:_DebugMessage(text, 5) - + -- Note the group is only added once, i.e. the ngroups parameter is ignored here. + -- This is because usually these request comes from an asset that has been transfered from another warehouse and hence should only be added once. + if asset~=nil then + self:_DebugMessage(string.format("Adding known asset uid=%d, attribute = %s to warehouse stock.", asset.uid, asset.attribute), 5) + table.insert(self.stock, asset) + else + self:_ErrorMessage(string.format("ERROR known asset could not be found in global warehouse db!"), 0) + end + + else + -- Request did not exist! + self:E("ERROR: Request does not exist in addAsset! This should not happen!") end - -- Get the asset from the global DB. - local asset=self:_FindAssetInDB(group) - - -- Note the group is only added once, i.e. the ngroups parameter is ignored here. - -- This is because usually these request comes from an asset that has been transfered from another warehouse and hence should only be added once. - if asset~=nil then - self:_DebugMessage(string.format("Adding known asset uid=%d, attribute = %s to warehouse stock.", asset.uid, asset.attribute), 5) - table.insert(self.stock, asset) - else - self:_ErrorMessage(string.format("ERROR known asset could not be found in global warehouse db!"), 0) - end - else - + ------------------------- + -- This is a NEW asset -- + ------------------------- + -- Debug info. self:_DebugMessage(self.wid..string.format("Adding %d NEW assets of group %s to warehouse %s.", n, tostring(group:GetName()), self.alias), 5) @@ -1971,47 +2000,11 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu end end - - --[[ - -- Get unique ids from group name. - local wid,aid,rid=self:_GetIDsFromGroup(group) - - -- Check if this is an known or a new asset group. - if aid~=nil and wid~=nil then - - -- We got a warehouse and asset id ==> this is an "old" group. - local asset=self:_FindAssetInDB(group) - - -- Note the group is only added once, i.e. the ngroups parameter is ignored here. - -- This is because usually these request comes from an asset that has been transfered from another warehouse and hence should only be added once. - if asset~=nil then - self:_DebugMessage(string.format("Adding known asset uid=%d, attribute = %s to warehouse stock.", asset.uid, asset.attribute), 5) - table.insert(self.stock, asset) - else - self:_ErrorMessage(string.format("ERROR known asset could not be found in global warehouse db!"), 0) - end - - else - - -- Debug info. - self:_DebugMessage(self.wid..string.format("Adding %d NEW assets of group %s to warehouse %s.", n, tostring(group:GetName()), self.alias), 5) - - -- This is a group that is not in the db yet. Add it n times. - local assets=self:_RegisterAsset(group, n, forceattribute) - - -- Add created assets to stock of this warehouse. - for _,asset in pairs(assets) do - table.insert(self.stock, asset) - end - end - ]] -- Destroy group if it is alive. - -- TODO: This causes a problem, when a completely new asset is added, i.e. not from a template group. - -- Need to create a "zombie" template group maybe? if group:IsAlive()==true then self:_DebugMessage(string.format("Destroying group %s.", group:GetName()), 5) - group:Destroy(true) + group:Destroy() end end @@ -2041,6 +2034,14 @@ function WAREHOUSE:_FindAssetInDB(group) return nil end +--- Find a warehouse in the global warehouse data base. +-- @param #WAREHOUSE self +-- @param #number uid The unique ID of the warehouse. +-- @return #WAREHOUSE The warehouse object or nil if no warehouse exists. +function WAREHOUSE:_FindWarehouseInDB(uid) + return WAREHOUSE.db.Warehouses[uid] +end + --- Register new asset in globase warehouse data base. -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group The group that will be added to the warehouse stock. @@ -2165,28 +2166,21 @@ function WAREHOUSE:_AssetItemInfo(asset) self:T3({Template=asset.template}) end ---- On after "AddAsset" event. Add a group to the warehouse stock. If the group is alive, it is destroyed. --- @param #WAREHOUSE self --- @param #string templategroupname Name of the late activated template group as defined in the mission editor. --- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1. -function WAREHOUSE:_AddAssetFromZombie(group, ngroups) - --TODO -end - --- Spawn a ground or naval asset in the corresponding spawn zone of the warehouse. -- @param #WAREHOUSE self +-- @param #string alias Alias name of the asset group. -- @param #WAREHOUSE.Assetitem asset Ground asset that will be spawned. -- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. -- @param Core.Zone#ZONE spawnzone Zone where the assets should be spawned. -- @param boolean aioff If true, AI of ground units are set to off. -- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned. -function WAREHOUSE:_SpawnAssetGroundNaval(asset, request, spawnzone, aioff) +function WAREHOUSE:_SpawnAssetGroundNaval(alias, asset, request, spawnzone, aioff) if asset and (asset.category==Group.Category.GROUND or asset.category==Group.Category.SHIP) then -- Prepare spawn template. - local template=self:_SpawnAssetPrepareTemplate(asset, request) + local template=self:_SpawnAssetPrepareTemplate(asset, alias) -- Initial spawn point. template.route.points[1]={} @@ -2241,18 +2235,19 @@ end --- Spawn an aircraft asset (plane or helo) at the airbase associated with the warehouse. -- @param #WAREHOUSE self +-- @param #string alias Alias name of the asset group. -- @param #WAREHOUSE.Assetitem asset Ground asset that will be spawned. -- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. -- @param #table parking Parking data for this asset. -- @param #boolean uncontrolled Spawn aircraft in uncontrolled state. -- @param #boolean hotstart Spawn aircraft with engines already on. Default is a cold start with engines off. -- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned. -function WAREHOUSE:_SpawnAssetAircraft(asset, request, parking, uncontrolled, hotstart) +function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrolled, hotstart) if asset and asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then -- Prepare the spawn template. - local template=self:_SpawnAssetPrepareTemplate(asset, request) + local template=self:_SpawnAssetPrepareTemplate(asset, alias) -- Set route points. if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then @@ -2364,15 +2359,15 @@ end --- Prepare a spawn template for the asset. Deep copy of asset template, adjusting template and unit names, nillifying group and unit ids. -- @param #WAREHOUSE self -- @param #WAREHOUSE.Assetitem asset Ground asset that will be spawned. --- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. +-- @param #string alias Alias name of the group. -- @return #table Prepared new spawn template. -function WAREHOUSE:_SpawnAssetPrepareTemplate(asset, request) +function WAREHOUSE:_SpawnAssetPrepareTemplate(asset, alias) -- Create an own copy of the template! local template=UTILS.DeepCopy(asset.template) -- Set unique name. - template.name=self:_Alias(asset, request) + template.name=alias -- Set current(!) coalition and country. template.CoalitionID=self.coalition @@ -2628,7 +2623,6 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Add groups to pending item. Pending.cargogroupset=_spawngroups - --Pending.cargogroupset:FilterStart() ------------------------------------------------------------------------------------------------------------------------------------ -- Self request: assets are spawned at warehouse but not transported anywhere. @@ -2730,7 +2724,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) end -- Empty cargo group set. - CargoGroups = SET_CARGO:New():FilterDeads() + CargoGroups = SET_CARGO:New() -- Add cargo groups to set. for _i,_group in pairs(_spawngroups:GetSetObjects()) do @@ -2784,12 +2778,12 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Get stock item. local _assetitem=_assetstock[i] --#WAREHOUSE.Assetitem - - -- Spawn with ALIAS here or DCS crashes! - local _alias=self:_Alias(_assetitem, Request) + + -- Create an alias name with the UIDs for the sending warehouse, asset and request. + local _alias=self:_alias(_assetitem.unittype, self.uid, _assetitem.uid, Request.uid) - -- Spawn plane at airport in uncontrolled state. - local spawngroup=self:_SpawnAssetAircraft(_assetitem, Pending, Parking[_assetitem.uid], true) + -- Spawn plane at airport in uncontrolled state. Will get activated when cargo is loaded. + local spawngroup=self:_SpawnAssetAircraft(_alias,_assetitem, Pending, Parking[_assetitem.uid], true) if spawngroup then -- Set state of warehouse so we can retrieve it later. @@ -2823,13 +2817,14 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Get stock item. local _assetitem=_assetstock[i] --#WAREHOUSE.Assetitem - -- Spawn with ALIAS here or DCS crashes! - local _alias=self:_Alias(_assetitem, Request) + -- Create an alias name with the UIDs for the sending warehouse, asset and request. + local _alias=self:_alias(_assetitem.unittype, self.uid, _assetitem.uid, Request.uid) -- Spawn plane at airport in controlled state. They need to fly to the spawn zone. - local spawngroup=self:_SpawnAssetAircraft(_assetitem, Pending, Parking[_assetitem.uid], false) + local spawngroup=self:_SpawnAssetAircraft(_alias,_assetitem, Pending, Parking[_assetitem.uid], false) if spawngroup then + -- Set state of warehouse so we can retrieve it later. spawngroup:SetState(spawngroup, "WAREHOUSE", self) @@ -2873,13 +2868,14 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Get stock item. local _assetitem=_assetstock[i] --#WAREHOUSE.Assetitem - -- Spawn with ALIAS here or DCS crashes! - local _alias=self:_Alias(_assetitem, Request) + -- Create an alias name with the UIDs for the sending warehouse, asset and request. + local _alias=self:_alias(_assetitem.unittype, self.uid, _assetitem.uid, Request.uid) -- Spawn ground asset. - local spawngroup=self:_SpawnAssetGroundNaval(_assetitem, Request, self.spawnzone) + local spawngroup=self:_SpawnAssetGroundNaval(_alias, _assetitem, Request, self.spawnzone) if spawngroup then + -- Set state of warehouse so we can retrieve it later. spawngroup:SetState(spawngroup, "WAREHOUSE", self) @@ -2941,9 +2937,15 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Load the cargo in the warehouse. --Cargo:Load(warehouse.warehouse) + + -- Get warehouse ID of the + local wid=warehouse:_GetIDsFromGroup(group) + + -- Get the receiving warehouse. + local warehouseReceiving=warehouse:_FindWarehouseInDB(wid) - -- Trigger Arrived event. - warehouse:__Arrived(1, group) + -- Trigger Arrived event at the receiving warehouse. + warehouseReceiving:__Arrived(1, group) end --- On after BackHome event. @@ -3013,7 +3015,7 @@ function WAREHOUSE:_SpawnAssetRequest(Request) end -- Create an empty group set. - local _groupset=SET_GROUP:New():FilterDeads() + local _groupset=SET_GROUP:New() -- Table for all spawned assets. local _assets={} @@ -3032,17 +3034,15 @@ function WAREHOUSE:_SpawnAssetRequest(Request) if _assetitem.category==Group.Category.GROUND then -- Spawn ground troops. - _group=self:_SpawnAssetGroundNaval(_assetitem, Request, self.spawnzone) + _group=self:_SpawnAssetGroundNaval(_alias,_assetitem, Request, self.spawnzone) elseif _assetitem.category==Group.Category.AIRPLANE or _assetitem.category==Group.Category.HELICOPTER then - --TODO: spawn only so many groups as there are parking spots. Adjust request and create a new one with the reduced number! - -- Spawn air units. if Parking[_assetitem.uid] then - _group=self:_SpawnAssetAircraft(_assetitem, Request, Parking[_assetitem.uid], UnControlled) + _group=self:_SpawnAssetAircraft(_alias,_assetitem, Request, Parking[_assetitem.uid], UnControlled) else - _group=self:_SpawnAssetAircraft(_assetitem, Request, nil, UnControlled) + _group=self:_SpawnAssetAircraft(_alias,_assetitem, Request, nil, UnControlled) end elseif _assetitem.category==Group.Category.TRAIN then @@ -3130,7 +3130,7 @@ function WAREHOUSE:onafterUnloaded(From, Event, To, group) end end ---- On after "Arrived" event. Triggered when a group has arrived at its destination. +--- On after "Arrived" event. Triggered when a group has arrived at its destination warehouse. -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. @@ -3139,13 +3139,38 @@ end function WAREHOUSE:onafterArrived(From, Event, To, group) -- Debug message and smoke. - self:_DebugMessage(string.format("Cargo %s arrived!", tostring(group:GetName())), 5) if self.Debug then group:SmokeOrange() end + -- Get request from group. + local request=self:_GetRequestOfGroup(group, self.pending) + + if request then + + -- Get the right warehouse to put the asset into + -- Transports go back to the warehouse which called this function while cargo goes into the receiving warehouse. + local warehouse=request.warehouse + if self:_GroupIsTransport(group,request) then + warehouse=self + end + + -- Debug message + self:_DebugMessage(string.format("Group %s arrived at warehouse %s!", tostring(group:GetName()), warehouse.alias), 5) + + -- Route mobile ground group to the warehouse. Group has 60 seconds to get there or it is despawned and added as asset to the new warehouse regardless. + if group:IsGround() and group:GetSpeedMax()>1 then + group:RouteGroundTo(warehouse.coordinate, group:GetSpeedMax()*0.3, "Off Road") + end + + -- Move asset from pending queue into new warehouse. + warehouse:__AddAsset(60, group) + + end + + --[[ -- Get request from group name. - local request=self:_GetRequestOfGroup(group, self.pending) + local request=self:_GetRequestOfGroup(group, self.pending) if request then @@ -3158,6 +3183,7 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) request.warehouse:__AddAsset(60, group) end + ]] end @@ -3696,9 +3722,6 @@ function WAREHOUSE:_OnEventArrived(EventData) -- Check if unit is alive and on the ground. Engine shutdown can also be triggered in other situations! if unit and unit:IsAlive()==true and unit:InAir()==false then - -- Smoke unit that arrived. - unit:SmokeBlue() - -- Get group. local group=EventData.IniGroup @@ -3793,7 +3816,7 @@ function WAREHOUSE:_OnEventLanding(EventData) self:T(self.wid..string.format("Warehouse %s captured event landing of its asset unit %s.", self.alias, EventData.IniUnitName)) -- Get request of this group - local request=self:_GetRequestOfGroup(group,self.pending) + local request=self:_GetRequestOfGroup(group, self.pending) -- If request is nil, the cargo has been delivered. -- TODO: I might need to add a delivered table, to be better able to get this right. @@ -4839,6 +4862,26 @@ function WAREHOUSE:_GetRequestOfGroup(group, queue) end +--- Is the group a used as transporter for a given request? +-- @param #WAREHOUSE self +-- @param Wrapper.Group#GROUP group The group from which the info is gathered. +-- @param #WAREHOUSE.Pendingitem request Request +-- @return #WAREHOUSE.Pendingitem The request belonging to this group. +function WAREHOUSE:_GroupIsTransport(group, request) + + local transporters=request.transportgroupset:GetSetObjects() + + local groupname=group:GetName() + for _,transport in pairs(transporters) do + if transport:GetName()==groupname then + return true + end + end + + return false +end + + --- Creates a unique name for spawned assets. From the group name the original warehouse, global asset and the request can be derived. -- @param #WAREHOUSE self -- @param #WAREHOUSE.Assetitem _assetitem Asset for which the name is created. From 5c29f48a887792d79070839584580297baa95bc4 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Thu, 6 Sep 2018 18:36:31 +0200 Subject: [PATCH 322/420] Home event handler implementation for APCs and Helicopters. airplanes is to be further investigated. --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 7 +- .../Moose/AI/AI_Cargo_Dispatcher.lua | 256 ++++++++++-------- .../Moose/AI/AI_Cargo_Helicopter.lua | 9 +- 3 files changed, 145 insertions(+), 127 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index fb16616ea..4df35bc4c 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -261,6 +261,7 @@ function AI_CARGO_APC:onafterMonitor( APC, From, Event, To ) if self:Is( "Unloaded" ) then self:Follow() end + self:F( "I am here" .. self:GetCurrentState() ) if self:Is( "Following" ) then for Cargo, APCUnit in pairs( self.Carrier_Cargo ) do local Cargo = Cargo -- Cargo.Cargo#CARGO @@ -422,15 +423,15 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate Home place. -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. -function AI_CARGO_APC:onafterHome( APC, From, Event, To, Coordinate, Speed ) +function AI_CARGO_APC:onafterHome( APC, From, Event, To, Coordinate, Speed, HomeZone ) if APC and APC:IsAlive() ~= nil then self.RouteHome = true - local _speed=Speed or APC:GetSpeedMax()*0.5 + Speed = Speed or APC:GetSpeedMax()*0.5 - local Waypoints = APC:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true ) + local Waypoints = APC:TaskGroundOnRoad( Coordinate, Speed, "Line abreast", true ) self:F({Waypoints = Waypoints}) local Waypoint = Waypoints[#Waypoints] diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index d8319f7cc..6ae9d3de5 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -99,13 +99,13 @@ -- You can use this event handler to post messages to players, or provide status updates etc. -- -- --- --- Pickup Handler OnAfter for CLASS. +-- --- Pickup event handler OnAfter for CLASS. -- -- Use this event handler to tailor the event when a CarrierGroup is routed towards a new pickup Coordinate and a specified Speed. -- -- You can use this event handler to post messages to players, or provide status updates etc. -- -- @param #CLASS self --- -- @param #string From A string that contains the "*from state name*" when the event was fired. --- -- @param #string Event A string that contains the "*event name*" when the event was fired. --- -- @param #string To A string that contains the "*to state name*" when the event was fired. +-- -- @param #string From A string that contains the "*from state name*" when the event was triggered. +-- -- @param #string Event A string that contains the "*event name*" when the event was triggered. +-- -- @param #string To A string that contains the "*to state name*" when the event was triggered. -- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- -- @param Core.Point#COORDINATE Coordinate The coordinate of the pickup location. -- -- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the pickup Coordinate. @@ -123,13 +123,13 @@ -- You can use this event handler to post messages to players, or provide status updates etc. -- -- --- --- Load Handler OnAfter for CLASS. +-- --- Load event handler OnAfter for CLASS. -- -- Use this event handler to tailor the event when a CarrierGroup has initiated the loading or boarding of cargo within reporting or near range. -- -- You can use this event handler to post messages to players, or provide status updates etc. -- -- @param #CLASS self --- -- @param #string From A string that contains the "*from state name*" when the event was fired. --- -- @param #string Event A string that contains the "*event name*" when the event was fired. --- -- @param #string To A string that contains the "*to state name*" when the event was fired. +-- -- @param #string From A string that contains the "*from state name*" when the event was triggered. +-- -- @param #string Event A string that contains the "*event name*" when the event was triggered. +-- -- @param #string To A string that contains the "*to state name*" when the event was triggered. -- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. -- function CLASS:OnAfterLoad( From, Event, To, CarrierGroup, PickupZone ) @@ -145,14 +145,14 @@ -- You can use this event handler to post messages to players, or provide status updates etc. -- -- --- --- Loading Handler OnAfter for CLASS. +-- --- Loading event handler OnAfter for CLASS. -- -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup is in the process of loading or boarding of a cargo object. -- -- You can use this event handler to post messages to players, or provide status updates etc. --- -- Note that this event is fired repeatedly until all cargo (units) have been boarded into the carrier. +-- -- Note that this event is triggered repeatedly until all cargo (units) have been boarded into the carrier. -- -- @param #CLASS self --- -- @param #string From A string that contains the "*from state name*" when the event was fired. --- -- @param #string Event A string that contains the "*event name*" when the event was fired. --- -- @param #string To A string that contains the "*to state name*" when the event was fired. +-- -- @param #string From A string that contains the "*from state name*" when the event was triggered. +-- -- @param #string Event A string that contains the "*event name*" when the event was triggered. +-- -- @param #string To A string that contains the "*to state name*" when the event was triggered. -- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- -- @param Cargo.Cargo#CARGO Cargo The cargo object. -- -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo loading operation. @@ -168,21 +168,21 @@ -- -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup has loaded a cargo object. -- You can use this event handler to post messages to players, or provide status updates etc. --- Note that if more cargo objects were loading or boarding into the CarrierUnit, then this event can be fired multiple times for each different Cargo/CarrierUnit. +-- Note that if more cargo objects were loading or boarding into the CarrierUnit, then this event can be triggered multiple times for each different Cargo/CarrierUnit. -- -- The function provides the CarrierGroup, which is the main group that was loading the Cargo into the CarrierUnit. -- A CarrierUnit is part of the larger CarrierGroup. -- -- --- --- Loaded Handler OnAfter for CLASS. +-- --- Loaded event handler OnAfter for CLASS. -- -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup has loaded a cargo object. -- -- You can use this event handler to post messages to players, or provide status updates etc. --- -- Note that if more cargo objects were loading or boarding into the CarrierUnit, then this event can be fired multiple times for each different Cargo/CarrierUnit. +-- -- Note that if more cargo objects were loading or boarding into the CarrierUnit, then this event can be triggered multiple times for each different Cargo/CarrierUnit. -- -- A CarrierUnit can be part of the larger CarrierGroup. -- -- @param #CLASS self --- -- @param #string From A string that contains the "*from state name*" when the event was fired. --- -- @param #string Event A string that contains the "*event name*" when the event was fired. --- -- @param #string To A string that contains the "*to state name*" when the event was fired. +-- -- @param #string From A string that contains the "*from state name*" when the event was triggered. +-- -- @param #string Event A string that contains the "*event name*" when the event was triggered. +-- -- @param #string To A string that contains the "*to state name*" when the event was triggered. -- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- -- @param Cargo.Cargo#CARGO Cargo The cargo object. -- -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo loading operation. @@ -200,13 +200,13 @@ -- You can use this event handler to post messages to players, or provide status updates etc. -- -- --- --- PickedUp Handler OnAfter for CLASS. +-- --- PickedUp event handler OnAfter for CLASS. -- -- Use this event handler to tailor the event when a carrier has picked up all cargo objects into the CarrierGroup. -- -- You can use this event handler to post messages to players, or provide status updates etc. -- -- @param #CLASS self --- -- @param #string From A string that contains the "*from state name*" when the event was fired. --- -- @param #string Event A string that contains the "*event name*" when the event was fired. --- -- @param #string To A string that contains the "*to state name*" when the event was fired. +-- -- @param #string From A string that contains the "*from state name*" when the event was triggered. +-- -- @param #string Event A string that contains the "*event name*" when the event was triggered. +-- -- @param #string To A string that contains the "*to state name*" when the event was triggered. -- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. -- function CLASS:OnAfterPickedUp( From, Event, To, CarrierGroup, PickupZone ) @@ -222,13 +222,13 @@ -- You can use this event handler to post messages to players, or provide status updates etc. -- -- --- --- Deploy Handler OnAfter for CLASS. +-- --- Deploy event handler OnAfter for CLASS. -- -- Use this event handler to tailor the event when a CarrierGroup is routed to a deploy coordinate, to Unload all cargo objects in each CarrierUnit. -- -- You can use this event handler to post messages to players, or provide status updates etc. -- -- @param #CLASS self --- -- @param #string From A string that contains the "*from state name*" when the event was fired. --- -- @param #string Event A string that contains the "*event name*" when the event was fired. --- -- @param #string To A string that contains the "*to state name*" when the event was fired. +-- -- @param #string From A string that contains the "*from state name*" when the event was triggered. +-- -- @param #string Event A string that contains the "*event name*" when the event was triggered. +-- -- @param #string To A string that contains the "*to state name*" when the event was triggered. -- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- -- @param Core.Point#COORDINATE Coordinate The deploy coordinate. -- -- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the deploy Coordinate. @@ -246,13 +246,13 @@ -- You can use this event handler to post messages to players, or provide status updates etc. -- -- --- --- Unload Handler OnAfter for CLASS. +-- --- Unload event handler OnAfter for CLASS. -- -- Use this event handler to tailor the event when a CarrierGroup has initiated the unloading or unboarding of cargo. -- -- You can use this event handler to post messages to players, or provide status updates etc. -- -- @param #CLASS self --- -- @param #string From A string that contains the "*from state name*" when the event was fired. --- -- @param #string Event A string that contains the "*event name*" when the event was fired. --- -- @param #string To A string that contains the "*to state name*" when the event was fired. +-- -- @param #string From A string that contains the "*from state name*" when the event was triggered. +-- -- @param #string Event A string that contains the "*event name*" when the event was triggered. +-- -- @param #string To A string that contains the "*to state name*" when the event was triggered. -- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. -- function CLASS:OnAfterUnload( From, Event, To, CarrierGroup, DeployZone ) @@ -265,14 +265,14 @@ -- ## 3.8) Tailor the **Unloading** event -- -- --- --- UnLoading Handler OnAfter for CLASS. +-- --- UnLoading event handler OnAfter for CLASS. -- -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup is in the process of unloading or unboarding of a cargo object. -- -- You can use this event handler to post messages to players, or provide status updates etc. --- -- Note that this event is fired repeatedly until all cargo (units) have been unboarded from the CarrierUnit. +-- -- Note that this event is triggered repeatedly until all cargo (units) have been unboarded from the CarrierUnit. -- -- @param #CLASS self --- -- @param #string From A string that contains the "*from state name*" when the event was fired. --- -- @param #string Event A string that contains the "*event name*" when the event was fired. --- -- @param #string To A string that contains the "*to state name*" when the event was fired. +-- -- @param #string From A string that contains the "*from state name*" when the event was triggered. +-- -- @param #string Event A string that contains the "*event name*" when the event was triggered. +-- -- @param #string To A string that contains the "*to state name*" when the event was triggered. -- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- -- @param Cargo.Cargo#CARGO Cargo The cargo object. -- -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo unloading operation. @@ -290,15 +290,15 @@ -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup has unloaded a cargo object. -- You can use this event handler to post messages to players, or provide status updates etc. -- --- --- Unloaded Handler OnAfter for CLASS. +-- --- Unloaded event handler OnAfter for CLASS. -- -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup has unloaded a cargo object. -- -- You can use this event handler to post messages to players, or provide status updates etc. --- -- Note that if more cargo objects were unloading or unboarding from the CarrierUnit, then this event can be fired multiple times for each different Cargo/CarrierUnit. +-- -- Note that if more cargo objects were unloading or unboarding from the CarrierUnit, then this event can be triggered multiple times for each different Cargo/CarrierUnit. -- -- A CarrierUnit can be part of the larger CarrierGroup. -- -- @param #CLASS self --- -- @param #string From A string that contains the "*from state name*" when the event was fired. --- -- @param #string Event A string that contains the "*event name*" when the event was fired. --- -- @param #string To A string that contains the "*to state name*" when the event was fired. +-- -- @param #string From A string that contains the "*from state name*" when the event was triggered. +-- -- @param #string Event A string that contains the "*event name*" when the event was triggered. +-- -- @param #string To A string that contains the "*to state name*" when the event was triggered. -- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- -- @param Cargo.Cargo#CARGO Cargo The cargo object. -- -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo unloading operation. @@ -316,14 +316,13 @@ -- You can use this event handler to post messages to players, or provide status updates etc. -- -- --- --- Deployed Handler OnAfter for AI_CARGO_DISPATCHER. +-- --- Deployed event handler OnAfter for CLASS. -- -- Use this event handler to tailor the event when a carrier has deployed all cargo objects from the CarrierGroup. -- -- You can use this event handler to post messages to players, or provide status updates etc. --- -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterDeployed --- -- @param #AI_CARGO_DISPATCHER self --- -- @param #string From A string that contains the "*from state name*" when the event was fired. --- -- @param #string Event A string that contains the "*event name*" when the event was fired. --- -- @param #string To A string that contains the "*to state name*" when the event was fired. +-- -- @param #CLASS self +-- -- @param #string From A string that contains the "*from state name*" when the event was triggered. +-- -- @param #string Event A string that contains the "*event name*" when the event was triggered. +-- -- @param #string To A string that contains the "*to state name*" when the event was triggered. -- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. -- function CLASS:OnAfterDeployed( From, Event, To, CarrierGroup, DeployZone ) @@ -331,7 +330,30 @@ -- -- Write here your own code. -- -- end +-- +-- ## 3.11) Tailor the **Home** event +-- +-- Use this event handler to tailor the event when a CarrierGroup is returning to the HomeZone, after it has deployed all cargo objects from the CarrierGroup. +-- You can use this event handler to post messages to players, or provide status updates etc. +-- +-- --- Home event handler OnAfter for CLASS. +-- -- Use this event handler to tailor the event when a CarrierGroup is returning to the HomeZone, after it has deployed all cargo objects from the CarrierGroup. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- If there is no HomeZone is specified, the CarrierGroup will stay at the current location after having deployed all cargo and this event won't be triggered. +-- -- @param #CLASS self +-- -- @param #string From A string that contains the "*from state name*" when the event was triggered. +-- -- @param #string Event A string that contains the "*event name*" when the event was triggered. +-- -- @param #string To A string that contains the "*to state name*" when the event was triggered. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Core.Point#COORDINATE Coordinate The home coordinate the Carrier will arrive and stop it's activities. +-- -- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the home Coordinate. +-- -- @param Core.Zone#ZONE HomeZone The zone wherein the carrier will return when all cargo has been transported. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +-- function CLASS:OnAfterHome( From, Event, To, CarrierGroup, Coordinate, Speed, HomeZone ) +-- +-- -- Write here your own code. -- +-- end +-- -- -- # 3) Set the pickup parameters. -- @@ -668,14 +690,14 @@ function AI_CARGO_DISPATCHER:onafterMonitor() self.AI_Cargo[Carrier] = self:AICargo( Carrier, self.SetCargo, self.CombatRadius ) AI_Cargo = self.AI_Cargo[Carrier] - --- Pickup Handler OnAfter for AI_CARGO_DISPATCHER. + --- Pickup event handler OnAfter for AI_CARGO_DISPATCHER. -- Use this event handler to tailor the event when a CarrierGroup is routed towards a new pickup Coordinate and a specified Speed. -- You can use this event handler to post messages to players, or provide status updates etc. -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterPickup -- @param #AI_CARGO_DISPATCHER self - -- @param #string From A string that contains the "*from state name*" when the event was fired. - -- @param #string Event A string that contains the "*event name*" when the event was fired. - -- @param #string To A string that contains the "*to state name*" when the event was fired. + -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- @param #string To A string that contains the "*to state name*" when the event was triggered. -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- @param Core.Point#COORDINATE Coordinate The coordinate of the pickup location. -- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the pickup Coordinate. @@ -684,14 +706,14 @@ function AI_CARGO_DISPATCHER:onafterMonitor() self:Pickup( CarrierGroup, Coordinate, Speed, PickupZone ) end - --- Load Handler OnAfter for AI_CARGO_DISPATCHER. + --- Load event handler OnAfter for AI_CARGO_DISPATCHER. -- Use this event handler to tailor the event when a CarrierGroup has initiated the loading or boarding of cargo within reporting or near range. -- You can use this event handler to post messages to players, or provide status updates etc. -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterLoad -- @param #AI_CARGO_DISPATCHER self - -- @param #string From A string that contains the "*from state name*" when the event was fired. - -- @param #string Event A string that contains the "*event name*" when the event was fired. - -- @param #string To A string that contains the "*to state name*" when the event was fired. + -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- @param #string To A string that contains the "*to state name*" when the event was triggered. -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. @@ -699,15 +721,15 @@ function AI_CARGO_DISPATCHER:onafterMonitor() self:Load( CarrierGroup, PickupZone ) end - --- Loading Handler OnAfter for AI_CARGO_DISPATCHER. + --- Loading event handler OnAfter for AI_CARGO_DISPATCHER. -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup is in the process of loading or boarding of a cargo object. -- You can use this event handler to post messages to players, or provide status updates etc. - -- Note that this event is fired repeatedly until all cargo (units) have been boarded into the carrier. + -- Note that this event is triggered repeatedly until all cargo (units) have been boarded into the carrier. -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterLoading -- @param #AI_CARGO_DISPATCHER self - -- @param #string From A string that contains the "*from state name*" when the event was fired. - -- @param #string Event A string that contains the "*event name*" when the event was fired. - -- @param #string To A string that contains the "*to state name*" when the event was fired. + -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- @param #string To A string that contains the "*to state name*" when the event was triggered. -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- @param Cargo.Cargo#CARGO Cargo The cargo object. -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo loading operation. @@ -717,16 +739,16 @@ function AI_CARGO_DISPATCHER:onafterMonitor() self:Loading( CarrierGroup, Cargo, CarrierUnit, PickupZone ) end - --- Loaded Handler OnAfter for AI_CARGO_DISPATCHER. + --- Loaded event handler OnAfter for AI_CARGO_DISPATCHER. -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup has loaded a cargo object. -- You can use this event handler to post messages to players, or provide status updates etc. - -- Note that if more cargo objects were loading or boarding into the CarrierUnit, then this event can be fired multiple times for each different Cargo/CarrierUnit. + -- Note that if more cargo objects were loading or boarding into the CarrierUnit, then this event can be triggered multiple times for each different Cargo/CarrierUnit. -- A CarrierUnit can be part of the larger CarrierGroup. -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterLoaded -- @param #AI_CARGO_DISPATCHER self - -- @param #string From A string that contains the "*from state name*" when the event was fired. - -- @param #string Event A string that contains the "*event name*" when the event was fired. - -- @param #string To A string that contains the "*to state name*" when the event was fired. + -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- @param #string To A string that contains the "*to state name*" when the event was triggered. -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- @param Cargo.Cargo#CARGO Cargo The cargo object. -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo loading operation. @@ -736,14 +758,14 @@ function AI_CARGO_DISPATCHER:onafterMonitor() self:Loaded( CarrierGroup, Cargo, CarrierUnit, PickupZone ) end - --- PickedUp Handler OnAfter for AI_CARGO_DISPATCHER. + --- PickedUp event handler OnAfter for AI_CARGO_DISPATCHER. -- Use this event handler to tailor the event when a carrier has picked up all cargo objects into the CarrierGroup. -- You can use this event handler to post messages to players, or provide status updates etc. -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterPickedUp -- @param #AI_CARGO_DISPATCHER self - -- @param #string From A string that contains the "*from state name*" when the event was fired. - -- @param #string Event A string that contains the "*event name*" when the event was fired. - -- @param #string To A string that contains the "*to state name*" when the event was fired. + -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- @param #string To A string that contains the "*to state name*" when the event was triggered. -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. @@ -752,14 +774,14 @@ function AI_CARGO_DISPATCHER:onafterMonitor() end - --- Deploy Handler OnAfter for AI_CARGO_DISPATCHER. + --- Deploy event handler OnAfter for AI_CARGO_DISPATCHER. -- Use this event handler to tailor the event when a CarrierGroup is routed to a deploy coordinate, to Unload all cargo objects in each CarrierUnit. -- You can use this event handler to post messages to players, or provide status updates etc. -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterDeploy -- @param #AI_CARGO_DISPATCHER self - -- @param #string From A string that contains the "*from state name*" when the event was fired. - -- @param #string Event A string that contains the "*event name*" when the event was fired. - -- @param #string To A string that contains the "*to state name*" when the event was fired. + -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- @param #string To A string that contains the "*to state name*" when the event was triggered. -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- @param Core.Point#COORDINATE Coordinate The deploy coordinate. -- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the deploy Coordinate. @@ -770,14 +792,14 @@ function AI_CARGO_DISPATCHER:onafterMonitor() end - --- Unload Handler OnAfter for AI_CARGO_DISPATCHER. + --- Unload event handler OnAfter for AI_CARGO_DISPATCHER. -- Use this event handler to tailor the event when a CarrierGroup has initiated the unloading or unboarding of cargo. -- You can use this event handler to post messages to players, or provide status updates etc. -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterUnload -- @param #AI_CARGO_DISPATCHER self - -- @param #string From A string that contains the "*from state name*" when the event was fired. - -- @param #string Event A string that contains the "*event name*" when the event was fired. - -- @param #string To A string that contains the "*to state name*" when the event was fired. + -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- @param #string To A string that contains the "*to state name*" when the event was triggered. -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. @@ -785,15 +807,15 @@ function AI_CARGO_DISPATCHER:onafterMonitor() self:Unloading( Carrier, Cargo, CarrierUnit, DeployZone ) end - --- UnLoading Handler OnAfter for AI_CARGO_DISPATCHER. + --- UnLoading event handler OnAfter for AI_CARGO_DISPATCHER. -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup is in the process of unloading or unboarding of a cargo object. -- You can use this event handler to post messages to players, or provide status updates etc. - -- Note that this event is fired repeatedly until all cargo (units) have been unboarded from the CarrierUnit. + -- Note that this event is triggered repeatedly until all cargo (units) have been unboarded from the CarrierUnit. -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterUnloading -- @param #AI_CARGO_DISPATCHER self - -- @param #string From A string that contains the "*from state name*" when the event was fired. - -- @param #string Event A string that contains the "*event name*" when the event was fired. - -- @param #string To A string that contains the "*to state name*" when the event was fired. + -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- @param #string To A string that contains the "*to state name*" when the event was triggered. -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- @param Cargo.Cargo#CARGO Cargo The cargo object. -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo unloading operation. @@ -804,16 +826,16 @@ function AI_CARGO_DISPATCHER:onafterMonitor() end - --- Unloaded Handler OnAfter for AI_CARGO_DISPATCHER. + --- Unloaded event handler OnAfter for AI_CARGO_DISPATCHER. -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup has unloaded a cargo object. -- You can use this event handler to post messages to players, or provide status updates etc. - -- Note that if more cargo objects were unloading or unboarding from the CarrierUnit, then this event can be fired multiple times for each different Cargo/CarrierUnit. + -- Note that if more cargo objects were unloading or unboarding from the CarrierUnit, then this event can be triggered multiple times for each different Cargo/CarrierUnit. -- A CarrierUnit can be part of the larger CarrierGroup. -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterUnloaded -- @param #AI_CARGO_DISPATCHER self - -- @param #string From A string that contains the "*from state name*" when the event was fired. - -- @param #string Event A string that contains the "*event name*" when the event was fired. - -- @param #string To A string that contains the "*to state name*" when the event was fired. + -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- @param #string To A string that contains the "*to state name*" when the event was triggered. -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- @param Cargo.Cargo#CARGO Cargo The cargo object. -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo unloading operation. @@ -823,20 +845,38 @@ function AI_CARGO_DISPATCHER:onafterMonitor() self:Unloaded( Carrier, Cargo, CarrierUnit, DeployZone ) end - --- Deployed Handler OnAfter for AI_CARGO_DISPATCHER. + --- Deployed event handler OnAfter for AI_CARGO_DISPATCHER. -- Use this event handler to tailor the event when a carrier has deployed all cargo objects from the CarrierGroup. -- You can use this event handler to post messages to players, or provide status updates etc. -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterDeployed -- @param #AI_CARGO_DISPATCHER self - -- @param #string From A string that contains the "*from state name*" when the event was fired. - -- @param #string Event A string that contains the "*event name*" when the event was fired. - -- @param #string To A string that contains the "*to state name*" when the event was fired. + -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- @param #string To A string that contains the "*to state name*" when the event was triggered. -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. function AI_Cargo.OnAfterDeployed( AI_Cargo, Carrier, From, Event, To, DeployZone ) self:Deployed( Carrier, DeployZone ) end + + --- Home event handler OnAfter for AI_CARGO_DISPATCHER. + -- Use this event handler to tailor the event when a CarrierGroup is returning to the HomeZone, after it has deployed all cargo objects from the CarrierGroup. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- If there is no HomeZone is specified, the CarrierGroup will stay at the current location after having deployed all cargo. + -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterHome + -- @param #AI_CARGO_DISPATCHER self + -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- @param #string To A string that contains the "*to state name*" when the event was triggered. + -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. + -- @param Core.Point#COORDINATE Coordinate The home coordinate the Carrier will arrive and stop it's activities. + -- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the home Coordinate. + -- @param Core.Zone#ZONE HomeZone The zone wherein the carrier will return when all cargo has been transported. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. + + function AI_Cargo.OnAfterHome( AI_Cargo, Carrier, From, Event, To, Coordinate, Speed, HomeZone ) + self:Home( Carrier, Coordinate, Speed, HomeZone ) + end end -- The Pickup sequence ... @@ -900,7 +940,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() if self.HomeZone then if not self.CarrierHome[Carrier] then self.CarrierHome[Carrier] = true - AI_Cargo:__Home( 60, self.HomeZone:GetRandomPointVec2() ) + AI_Cargo:__Home( 60, self.HomeZone:GetRandomPointVec2(), math.random( self.PickupMinSpeed, self.PickupMaxSpeed ), self.HomeZone ) end end end @@ -910,7 +950,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() self:__Monitor( self.MonitorTimeInterval ) end ---- Start Handler OnBefore for AI_CARGO_DISPATCHER +--- Start event handler OnBefore for AI_CARGO_DISPATCHER -- @function [parent=#AI_CARGO_DISPATCHER] OnBeforeStart -- @param #AI_CARGO_DISPATCHER self -- @param #string From @@ -918,7 +958,7 @@ end -- @param #string To -- @return #boolean ---- Start Handler OnAfter for AI_CARGO_DISPATCHER +--- Start event handler OnAfter for AI_CARGO_DISPATCHER -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterStart -- @param #AI_CARGO_DISPATCHER self -- @param #string From @@ -938,7 +978,7 @@ function AI_CARGO_DISPATCHER:onafterStart( From, Event, To ) self:__Monitor( -1 ) end ---- Stop Handler OnBefore for AI_CARGO_DISPATCHER +--- Stop event handler OnBefore for AI_CARGO_DISPATCHER -- @function [parent=#AI_CARGO_DISPATCHER] OnBeforeStop -- @param #AI_CARGO_DISPATCHER self -- @param #string From @@ -946,7 +986,7 @@ end -- @param #string To -- @return #boolean ---- Stop Handler OnAfter for AI_CARGO_DISPATCHER +--- Stop event handler OnAfter for AI_CARGO_DISPATCHER -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterStop -- @param #AI_CARGO_DISPATCHER self -- @param #string From @@ -963,30 +1003,6 @@ end -- @param #number Delay - ---- Loaded Handler OnAfter for AI_CARGO_DISPATCHER --- @function [parent=#AI_CARGO_DISPATCHER] OnAfterLoaded --- @param #AI_CARGO_DISPATCHER self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Wrapper.Group#GROUP Carrier Carrier object. --- @param Cargo.Cargo#CARGO Cargo Cargo object. - ---- Unloaded Handler OnAfter for AI_CARGO_DISPATCHER --- @function [parent=#AI_CARGO_DISPATCHER] OnAfterUnloaded --- @param #AI_CARGO_DISPATCHER self --- @param #string From --- @param #string Event --- @param #string To --- @param Wrapper.Group#GROUP Carrier --- @param Cargo.Cargo#CARGO Cargo - - - - - - --- Make a Carrier run for a cargo deploy action after the cargo has been loaded, by default. -- @param #AI_CARGO_DISPATCHER self -- @param From diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 2c82fb1e3..11d32053b 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -597,7 +597,8 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate Home place. -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. -function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinate, Speed ) +-- @param Core.Zone#ZONE HomeZone The zone wherein the carrier will return when all cargo has been transported. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinate, Speed, HomeZone ) if Helicopter and Helicopter:IsAlive() ~= nil then @@ -609,7 +610,7 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat Coordinate.y = math.random( 50, 200 ) - local _speed=Speed or Helicopter:GetSpeedMax()*0.5 + Speed = Speed or Helicopter:GetSpeedMax()*0.5 --- Create a route point of type air. local CoordinateFrom = Helicopter:GetCoordinate() @@ -617,7 +618,7 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, - _speed, + Speed , true ) Route[#Route+1] = WaypointFrom @@ -628,7 +629,7 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, - _speed, + Speed , true ) From 5bd6f4901f1fd796bc8fdfb9d3fa067b9a3b30f0 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Thu, 6 Sep 2018 19:23:13 +0200 Subject: [PATCH 323/420] Fixed issues with pickup zones. --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 8 ++++---- Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 4df35bc4c..8b2bd9eee 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -319,12 +319,12 @@ end --- @param #AI_CARGO_APC -- @param Wrapper.Group#GROUP APC -function AI_CARGO_APC._Pickup( APC, self, PickupZone ) +function AI_CARGO_APC._Pickup( APC, self, Coordinate, Speed, PickupZone ) APC:F( { "AI_CARGO_APC._Pickup:", APC:GetName() } ) if APC:IsAlive() then - self:Load( PickupZone) + self:Load( PickupZone ) self.Relocating = false self.Transporting = true end @@ -364,7 +364,7 @@ function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed, Pi local Waypoints = APC:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true ) - local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Pickup", self, PickupZone ) + local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Pickup", self, Coordinate, Speed, PickupZone ) self:F({Waypoints = Waypoints}) local Waypoint = Waypoints[#Waypoints] @@ -372,7 +372,7 @@ function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed, Pi APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. else - AI_CARGO_APC._Pickup( APC, self, PickupZone ) + AI_CARGO_APC._Pickup( APC, self, Coordinate, Speed, PickupZone ) end self.Relocating = true diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 6ae9d3de5..58ed1ce92 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -900,7 +900,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() if not self.PickupZoneSet or PickupZone then for CarrierPickup, Coordinate in pairs( self.PickupCargo ) do if CarrierPickup:IsAlive() == true then - if CargoCoordinate:Get2DDistance( Coordinate ) <= 25 then + if CargoCoordinate:Get2DDistance( Coordinate ) <= 100 then CoordinateFree = false break end From 703dac8251b3087514d8c1eae9e2288a221e43bc Mon Sep 17 00:00:00 2001 From: FlightControl Date: Thu, 6 Sep 2018 20:31:33 +0200 Subject: [PATCH 324/420] Optimization solving the overloading problem with Loaded event for cargo deployment. --- Moose Development/Moose/AI/AI_Cargo.lua | 2 +- Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua index 8e145a8c4..885d717de 100644 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -414,7 +414,7 @@ function AI_CARGO:onafterUnloaded( Carrier, From, Event, To, Cargo, CarrierUnit, end if AllUnloaded == true then - self:Deployed( DeployZone ) + self:__Deployed( 5, DeployZone ) end end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 58ed1ce92..eb448955f 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -428,6 +428,8 @@ function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo ) self:AddTransition( "Monitoring", "Loaded", "Monitoring" ) self:AddTransition( "Monitoring", "PickedUp", "Monitoring" ) + self:AddTransition( "Monitoring", "Transport", "Monitoring" ) + self:AddTransition( "Monitoring", "Deploy", "Monitoring" ) self:AddTransition( "Monitoring", "Unload", "Monitoring" ) self:AddTransition( "Monitoring", "Unloading", "Monitoring" ) @@ -771,6 +773,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() function AI_Cargo.OnAfterPickedUp( AI_Cargo, CarrierGroup, From, Event, To, PickupZone ) self:PickedUp( CarrierGroup, PickupZone ) + self:Transport( CarrierGroup ) end @@ -1011,7 +1014,7 @@ end -- @param Wrapper.Group#GROUP Carrier -- @param Cargo.Cargo#CARGO Cargo -- @return #AI_CARGO_DISPATCHER -function AI_CARGO_DISPATCHER:OnAfterLoaded( From, Event, To, Carrier, Cargo ) +function AI_CARGO_DISPATCHER:onafterTransport( From, Event, To, Carrier, Cargo ) if self.DeployZoneSet then if self.AI_Cargo[Carrier]:IsTransporting() == true then From 3f875ce27697c7f54bbee508f5edbc62db26b40f Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 6 Sep 2018 23:20:47 +0200 Subject: [PATCH 325/420] Warehouse v0.3.7 Added global warehouse table. Fixed bugs for prending queue --- .../Moose/Functional/Warehouse.lua | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index c1e1813f8..827033e28 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -931,7 +931,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.3.6w" +WAREHOUSE.version="0.3.7" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -3058,7 +3058,7 @@ function WAREHOUSE:_SpawnAssetRequest(Request) elseif _assetitem.category==Group.Category.SHIP then -- Spawn naval assets. - _group=self:_SpawnAssetGroundNaval(_assetitem, Request, self.portzone) + _group=self:_SpawnAssetGroundNaval(_alias,_assetitem, Request, self.portzone) else self:E(self.wid.."ERROR: Unknown asset category!") @@ -4868,16 +4868,19 @@ end -- @param #WAREHOUSE.Pendingitem request Request -- @return #WAREHOUSE.Pendingitem The request belonging to this group. function WAREHOUSE:_GroupIsTransport(group, request) + + if request.transportgroupset then - local transporters=request.transportgroupset:GetSetObjects() + local transporters=request.transportgroupset:GetSetObjects() - local groupname=group:GetName() - for _,transport in pairs(transporters) do - if transport:GetName()==groupname then - return true + local groupname=group:GetName() + for _,transport in pairs(transporters) do + if transport:GetName()==groupname then + return true + end end end - + return false end From c2064690f16ba69cf08d122b7f3d339d96a25e65 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 6 Sep 2018 23:25:19 +0200 Subject: [PATCH 326/420] AI CARGO Added new routines by Sven. Due to conflicts just overwriting the old ones. --- Moose Development/Moose/AI/AI_Cargo.lua | 435 +++++++++++ Moose Development/Moose/AI/AI_Cargo_APC.lua | 449 +---------- .../Moose/AI/AI_Cargo_Airplane.lua | 240 ++---- .../Moose/AI/AI_Cargo_Dispatcher.lua | 729 ++++++++++++++---- .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 40 +- .../Moose/AI/AI_Cargo_Dispatcher_Airplane.lua | 47 +- .../AI/AI_Cargo_Dispatcher_Helicopter.lua | 49 +- .../Moose/AI/AI_Cargo_Helicopter.lua | 432 ++--------- 8 files changed, 1273 insertions(+), 1148 deletions(-) create mode 100644 Moose Development/Moose/AI/AI_Cargo.lua diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua new file mode 100644 index 000000000..885d717de --- /dev/null +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -0,0 +1,435 @@ +--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo. +-- +-- === +-- +-- ### Author: **FlightControl** +-- +-- === +-- +-- @module AI.AI_Cargo +-- @image Cargo.JPG + +--- @type AI_CARGO +-- @extends Core.Fsm#FSM_CONTROLLABLE + + +--- Base class for the dynamic cargo handling capability for AI groups. +-- +-- Carriers can be mobilized to intelligently transport infantry and other cargo within the simulation. +-- The AI_CARGO module uses the @{Cargo.Cargo} capabilities within the MOOSE framework. +-- CARGO derived objects must be declared within the mission to make the AI_CARGO object recognize the cargo. +-- Please consult the @{Cargo.Cargo} module for more information. +-- +-- The derived classes from this module are: +-- +-- * @{AI.AI_Cargo_APC} - Cargo transportation using APCs and other vehicles between zones. +-- * @{AI.AI_Cargo_Helicopter} - Cargo transportation using helicopters between zones. +-- * @{AI.AI_Cargo_Airplane} - Cargo transportation using airplanes to and from airbases. +-- +-- @field #AI_CARGO +AI_CARGO = { + ClassName = "AI_CARGO", + Coordinate = nil, -- Core.Point#COORDINATE, + Carrier_Cargo = {}, +} + +--- Creates a new AI_CARGO object. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param Core.Set#SET_CARGO CargoSet +-- @param #number CombatRadius +-- @return #AI_CARGO +function AI_CARGO:New( Carrier, CargoSet ) + + local self = BASE:Inherit( self, FSM_CONTROLLABLE:New( Carrier ) ) -- #AI_CARGO + + self.CargoSet = CargoSet -- Core.Set#SET_CARGO + self.CargoCarrier = Carrier -- Wrapper.Group#GROUP + + self:SetStartState( "Unloaded" ) + + self:AddTransition( "Unloaded", "Pickup", "*" ) + self:AddTransition( "Loaded", "Deploy", "*" ) + + self:AddTransition( "*", "Load", "Boarding" ) + self:AddTransition( { "Boarding", "Loaded" }, "Board", "Boarding" ) + self:AddTransition( "Boarding", "Loaded", "Boarding" ) + self:AddTransition( "Boarding", "PickedUp", "Loaded" ) + + self:AddTransition( "Loaded", "Unload", "Unboarding" ) + self:AddTransition( "Unboarding", "Unboard", "Unboarding" ) + self:AddTransition( "Unboarding", "Unloaded", "Unboarding" ) + self:AddTransition( "Unboarding", "Deployed", "Unloaded" ) + + --- Pickup Handler OnBefore for AI_CARGO + -- @function [parent=#AI_CARGO] OnBeforePickup + -- @param #AI_CARGO self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. + -- @return #boolean + + --- Pickup Handler OnAfter for AI_CARGO + -- @function [parent=#AI_CARGO] OnAfterPickup + -- @param #AI_CARGO self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. + + --- Pickup Trigger for AI_CARGO + -- @function [parent=#AI_CARGO] Pickup + -- @param #AI_CARGO self + -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. + + --- Pickup Asynchronous Trigger for AI_CARGO + -- @function [parent=#AI_CARGO] __Pickup + -- @param #AI_CARGO self + -- @param #number Delay + -- @param Core.Point#COORDINATE Coordinate Pickup place. If not given, loading starts at the current location. + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. + + --- Deploy Handler OnBefore for AI_CARGO + -- @function [parent=#AI_CARGO] OnBeforeDeploy + -- @param #AI_CARGO self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. + -- @return #boolean + + --- Deploy Handler OnAfter for AI_CARGO + -- @function [parent=#AI_CARGO] OnAfterDeploy + -- @param #AI_CARGO self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. + + --- Deploy Trigger for AI_CARGO + -- @function [parent=#AI_CARGO] Deploy + -- @param #AI_CARGO self + -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. + + --- Deploy Asynchronous Trigger for AI_CARGO + -- @function [parent=#AI_CARGO] __Deploy + -- @param #AI_CARGO self + -- @param #number Delay + -- @param Core.Point#COORDINATE Coordinate + -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. + + + --- Loaded Handler OnAfter for AI_CARGO + -- @function [parent=#AI_CARGO] OnAfterLoaded + -- @param #AI_CARGO self + -- @param Wrapper.Group#GROUP Carrier + -- @param #string From + -- @param #string Event + -- @param #string To + + --- Unloaded Handler OnAfter for AI_CARGO + -- @function [parent=#AI_CARGO] OnAfterUnloaded + -- @param #AI_CARGO self + -- @param Wrapper.Group#GROUP Carrier + -- @param #string From + -- @param #string Event + -- @param #string To + + for _, CarrierUnit in pairs( Carrier:GetUnits() ) do + CarrierUnit:SetCargoBayWeightLimit() + end + + self.Transporting = false + self.Relocating = false + + return self +end + + + +function AI_CARGO:IsTransporting() + + return self.Transporting == true +end + +function AI_CARGO:IsRelocating() + + return self.Relocating == true +end + + + +--- On before Load event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO:onbeforeLoad( Carrier, From, Event, To, PickupZone ) + self:F( { Carrier, From, Event, To } ) + + local Boarding = false + + local LoadInterval = 10 + local LoadDelay = 10 + local Carrier_List = {} + local Carrier_Weight = {} + + if Carrier and Carrier:IsAlive() then + self.Carrier_Cargo = {} + for _, CarrierUnit in pairs( Carrier:GetUnits() ) do + local CarrierUnit = CarrierUnit -- Wrapper.Unit#UNIT + + local CargoBayFreeWeight = CarrierUnit:GetCargoBayFreeWeight() + self:F({CargoBayFreeWeight=CargoBayFreeWeight}) + + Carrier_List[#Carrier_List+1] = CarrierUnit + Carrier_Weight[CarrierUnit] = CargoBayFreeWeight + end + + local Carrier_Count = #Carrier_List + local Carrier_Index = 1 + + for _, Cargo in UTILS.spairs( self.CargoSet:GetSet(), function( t, a, b ) return t[a]:GetWeight() > t[b]:GetWeight() end ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + + self:F( { IsUnLoaded = Cargo:IsUnLoaded(), IsDeployed = Cargo:IsDeployed(), Cargo:GetName(), Carrier:GetName() } ) + + local Loaded = false + + -- Try all Carriers, but start from the one according the Carrier_Index + for Carrier_Loop = 1, #Carrier_List do + + local CarrierUnit = Carrier_List[Carrier_Index] -- Wrapper.Unit#UNIT + + -- This counters loop through the available Carriers. + Carrier_Index = Carrier_Index + 1 + if Carrier_Index > Carrier_Count then + Carrier_Index = 1 + end + + if Cargo:IsUnLoaded() then -- and not Cargo:IsDeployed() then + if Cargo:IsInLoadRadius( CarrierUnit:GetCoordinate() ) then + self:F( { "In radius", CarrierUnit:GetName() } ) + + local CargoWeight = Cargo:GetWeight() + + -- Only when there is space within the bay to load the next cargo item! + if Carrier_Weight[CarrierUnit] > CargoWeight then --and CargoBayFreeVolume > CargoVolume then + Carrier:RouteStop() + --Cargo:Ungroup() + Cargo:__Board( LoadDelay, CarrierUnit, 25 ) + LoadDelay = LoadDelay + LoadInterval + self:__Board( LoadDelay, Cargo, CarrierUnit, PickupZone ) + + -- So now this CarrierUnit has Cargo that is being loaded. + -- This will be used further in the logic to follow and to check cargo status. + self.Carrier_Cargo[Cargo] = CarrierUnit + Boarding = true + Carrier_Weight[CarrierUnit] = Carrier_Weight[CarrierUnit] - CargoWeight + Loaded = true + + -- Ok, we loaded a cargo, now we can stop the loop. + break + end + end + end + + end + + if not Loaded then + -- If the cargo wasn't loaded in one of the carriers, then we need to stop the loading. + end + + end + end + + return Boarding + +end + +--- On after Board event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Cargo.Cargo#CARGO Cargo Cargo object. +-- @param Wrapper.Unit#UNIT CarrierUnit +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO:onafterBoard( Carrier, From, Event, To, Cargo, CarrierUnit, PickupZone ) + self:F( { Carrier, From, Event, To, Cargo, CarrierUnit:GetName() } ) + + if Carrier and Carrier:IsAlive() then + self:F({ IsLoaded = Cargo:IsLoaded(), Cargo:GetName(), Carrier:GetName() } ) + if not Cargo:IsLoaded() then + self:__Board( 10, Cargo, CarrierUnit, PickupZone ) + return + end + end + + self:__Loaded( 10, Cargo, CarrierUnit, PickupZone ) + +end + +--- On after Loaded event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @return #boolean Cargo loaded. +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO:onafterLoaded( Carrier, From, Event, To, Cargo, PickupZone ) + self:F( { Carrier, From, Event, To } ) + + local Loaded = true + + if Carrier and Carrier:IsAlive() then + for Cargo, CarrierUnit in pairs( self.Carrier_Cargo ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + self:F( { IsLoaded = Cargo:IsLoaded(), IsDestroyed = Cargo:IsDestroyed(), Cargo:GetName(), Carrier:GetName() } ) + if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then + Loaded = false + end + end + end + + if Loaded then + self:PickedUp( PickupZone ) + end + +end + +--- On after PickedUp event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO:onafterPickedUp( Carrier, From, Event, To, PickupZone ) + self:F( { Carrier, From, Event, To } ) + + Carrier:RouteResume() + +end + + + + +--- On after Unload event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +function AI_CARGO:onafterUnload( Carrier, From, Event, To, DeployZone ) + self:F( { Carrier, From, Event, To, DeployZone } ) + + local UnboardInterval = 10 + local UnboardDelay = 10 + + if Carrier and Carrier:IsAlive() then + for _, CarrierUnit in pairs( Carrier:GetUnits() ) do + local CarrierUnit = CarrierUnit -- Wrapper.Unit#UNIT + Carrier:RouteStop() + for _, Cargo in pairs( CarrierUnit:GetCargo() ) do + if Cargo:IsLoaded() then + Cargo:__UnBoard( UnboardDelay ) + UnboardDelay = UnboardDelay + UnboardInterval + Cargo:SetDeployed( true ) + self:__Unboard( UnboardDelay, Cargo, CarrierUnit, DeployZone ) + end + end + end + end + +end + +--- On after Unboard event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #string Cargo.Cargo#CARGO Cargo Cargo object. +-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +function AI_CARGO:onafterUnboard( Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone ) + self:F( { Carrier, From, Event, To, Cargo:GetName() } ) + + if Carrier and Carrier:IsAlive() then + if not Cargo:IsUnLoaded() then + self:__Unboard( 10, Cargo, CarrierUnit, DeployZone ) + return + end + end + + self:Unloaded( Cargo, CarrierUnit, DeployZone ) + +end + +--- On after Unloaded event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #string Cargo.Cargo#CARGO Cargo Cargo object. +-- @param #boolean Deployed Cargo is deployed. +-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +function AI_CARGO:onafterUnloaded( Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone ) + self:F( { Carrier, From, Event, To, Cargo:GetName(), DeployZone = DeployZone } ) + + local AllUnloaded = true + + --Cargo:Regroup() + + if Carrier and Carrier:IsAlive() then + for _, CarrierUnit in pairs( Carrier:GetUnits() ) do + local CarrierUnit = CarrierUnit -- Wrapper.Unit#UNIT + local IsEmpty = CarrierUnit:IsCargoEmpty() + self:I({ IsEmpty = IsEmpty }) + if not IsEmpty then + AllUnloaded = false + break + end + end + + if AllUnloaded == true then + if DeployZone == true then + self.Carrier_Cargo = {} + end + self.CargoCarrier = Carrier + end + end + + if AllUnloaded == true then + self:__Deployed( 5, DeployZone ) + end + +end + +--- On after Deployed event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP Carrier +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +function AI_CARGO:onafterDeployed( Carrier, From, Event, To, DeployZone ) + self:F( { Carrier, From, Event, To, DeployZone = DeployZone } ) + + self:__Guard( 0.1 ) + +end + diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 71434f10d..8b2bd9eee 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -1,4 +1,4 @@ ---- **AI** -- (R2.3) - Models the intelligent transportation of infantry and other cargo. +--- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo. -- -- === -- @@ -10,15 +10,16 @@ -- @image AI_Cargo_Dispatching_For_APC.JPG --- @type AI_CARGO_APC --- @extends Core.Fsm#FSM_CONTROLLABLE +-- @extends AI.AI_Cargo#AI_CARGO ---- Brings a dynamic cargo handling capability for AI groups. +--- Brings a dynamic cargo handling capability for an AI vehicle group. -- -- Armoured Personnel Carriers (APC), Trucks, Jeeps and other ground based carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. --- The AI\_CARGO\APC module uses the @{Cargo} capabilities within the MOOSE framework. --- CARGO derived objects must be declared within the mission to make the AI\_CARGO\APC object recognize the cargo. --- Please consult the @{Cargo} module for more information. +-- +-- The AI_CARGO_APC class uses the @{Cargo.Cargo} capabilities within the MOOSE framework. +-- @{Cargo.Cargo} must be declared within the mission to make the AI_CARGO_APC object recognize the cargo. +-- Please consult the @{Cargo.Cargo} module for more information. -- -- ## Cargo loading. -- @@ -74,7 +75,6 @@ AI_CARGO_APC = { ClassName = "AI_CARGO_APC", Coordinate = nil, -- Core.Point#COORDINATE, - APC_Cargo = {}, } --- Creates a new AI_CARGO_APC object. @@ -85,125 +85,21 @@ AI_CARGO_APC = { -- @return #AI_CARGO_APC function AI_CARGO_APC:New( APC, CargoSet, CombatRadius ) - local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_CARGO_APC + local self = BASE:Inherit( self, AI_CARGO:New( APC, CargoSet ) ) -- #AI_CARGO_APC - self.CargoSet = CargoSet -- Core.Set#SET_CARGO self.CombatRadius = CombatRadius - self:SetStartState( "Unloaded" ) - - self:AddTransition( "Unloaded", "Pickup", "*" ) - self:AddTransition( "Loaded", "Deploy", "*" ) - - self:AddTransition( "*", "Load", "Boarding" ) - self:AddTransition( { "Boarding", "Loaded" }, "Board", "Boarding" ) - self:AddTransition( "Boarding", "Loaded", "Loaded" ) - self:AddTransition( "Loaded", "Unload", "Unboarding" ) - self:AddTransition( "Unboarding", "Unboard", "Unboarding" ) - self:AddTransition( { "Unboarding", "Unloaded" }, "Unloaded", "Unloaded" ) - self:AddTransition( "*", "Monitor", "*" ) self:AddTransition( "*", "Follow", "Following" ) self:AddTransition( "*", "Guard", "Unloaded" ) self:AddTransition( "*", "Home", "*" ) - self:AddTransition( "*", "BackHome" , "*" ) self:AddTransition( "*", "Destroyed", "Destroyed" ) - - --- Pickup Handler OnBefore for AI_CARGO_APC - -- @function [parent=#AI_CARGO_APC] OnBeforePickup - -- @param #AI_CARGO_APC self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param Core.Point#COORDINATE Coordinate - -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. - -- @return #boolean - - --- Pickup Handler OnAfter for AI_CARGO_APC - -- @function [parent=#AI_CARGO_APC] OnAfterPickup - -- @param #AI_CARGO_APC self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param Core.Point#COORDINATE Coordinate - -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. - - --- Pickup Trigger for AI_CARGO_APC - -- @function [parent=#AI_CARGO_APC] Pickup - -- @param #AI_CARGO_APC self - -- @param Core.Point#COORDINATE Coordinate - -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. - - --- Pickup Asynchronous Trigger for AI_CARGO_APC - -- @function [parent=#AI_CARGO_APC] __Pickup - -- @param #AI_CARGO_APC self - -- @param #number Delay - -- @param Core.Point#COORDINATE Coordinate Pickup place. If not given, loading starts at the current location. - -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. - - --- Deploy Handler OnBefore for AI_CARGO_APC - -- @function [parent=#AI_CARGO_APC] OnBeforeDeploy - -- @param #AI_CARGO_APC self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param Core.Point#COORDINATE Coordinate - -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. - -- @return #boolean - - --- Deploy Handler OnAfter for AI_CARGO_APC - -- @function [parent=#AI_CARGO_APC] OnAfterDeploy - -- @param #AI_CARGO_APC self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param Core.Point#COORDINATE Coordinate - -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. - - --- Deploy Trigger for AI_CARGO_APC - -- @function [parent=#AI_CARGO_APC] Deploy - -- @param #AI_CARGO_APC self - -- @param Core.Point#COORDINATE Coordinate - -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. - - --- Deploy Asynchronous Trigger for AI_CARGO_APC - -- @function [parent=#AI_CARGO_APC] __Deploy - -- @param #AI_CARGO_APC self - -- @param #number Delay - -- @param Core.Point#COORDINATE Coordinate - -- @param #number Speed Speed in km/h. Default is 50% of max possible speed the group can do. - - - --- Loaded Handler OnAfter for AI_CARGO_APC - -- @function [parent=#AI_CARGO_APC] OnAfterLoaded - -- @param #AI_CARGO_APC self - -- @param Wrapper.Group#GROUP APC - -- @param #string From - -- @param #string Event - -- @param #string To - - --- Unloaded Handler OnAfter for AI_CARGO_APC - -- @function [parent=#AI_CARGO_APC] OnAfterUnloaded - -- @param #AI_CARGO_APC self - -- @param Wrapper.Group#GROUP APC - -- @param #string From - -- @param #string Event - -- @param #string To - - self:__Monitor( 1 ) self:SetCarrier( APC ) - for _, APCUnit in pairs( APC:GetUnits() ) do - APCUnit:SetCargoBayWeightLimit() - end - - self.Transporting = false - self.Relocating = false - return self end @@ -256,16 +152,6 @@ function AI_CARGO_APC:SetCarrier( CargoCarrier ) end -function AI_CARGO_APC:IsTransporting() - - return self.Transporting == true -end - -function AI_CARGO_APC:IsRelocating() - - return self.Relocating == true -end - --- Find a free Carrier within a range. -- @param #AI_CARGO_APC self -- @param Core.Point#COORDINATE Coordinate @@ -359,7 +245,7 @@ function AI_CARGO_APC:onafterMonitor( APC, From, Event, To ) if APC and APC:IsAlive() then if self.CarrierCoordinate then - if self:IsRelocating() == true then + if self:IsTransporting() == true then local Coordinate = APC:GetCoordinate() self.Zone:Scan( { Object.Category.UNIT } ) if self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then @@ -370,14 +256,16 @@ function AI_CARGO_APC:onafterMonitor( APC, From, Event, To ) else if self:Is( "Loaded" ) then -- There are enemies within combat range. Unload the CargoCarrier. - self:__Unload( 1, false ) + self:__Unload( 1 ) else if self:Is( "Unloaded" ) then self:Follow() end + self:F( "I am here" .. self:GetCurrentState() ) if self:Is( "Following" ) then - for APCUnit, Cargo in pairs( self.APC_Cargo ) do + for Cargo, APCUnit in pairs( self.Carrier_Cargo ) do local Cargo = Cargo -- Cargo.Cargo#CARGO + local APCUnit = APCUnit -- Wrapper.Unit#UNIT if Cargo:IsAlive() then if not Cargo:IsNear( APCUnit, 40 ) then APCUnit:RouteStop() @@ -406,249 +294,6 @@ function AI_CARGO_APC:onafterMonitor( APC, From, Event, To ) end ---- On before Load event. --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP APC --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function AI_CARGO_APC:onbeforeLoad( APC, From, Event, To ) - self:F( { APC, From, Event, To } ) - - local Boarding = false - - if APC and APC:IsAlive() then - self.APC_Cargo = {} - for _, APCUnit in pairs( APC:GetUnits() ) do - local APCUnit = APCUnit -- Wrapper.Unit#UNIT - - local CargoBayFreeWeight = APCUnit:GetCargoBayFreeWeight() - self:F({CargoBayFreeWeight=CargoBayFreeWeight}) - - --for _, Cargo in pairs( self.CargoSet:GetSet() ) do - for _, Cargo in UTILS.spairs( self.CargoSet:GetSet(), function( t, a, b ) return t[a]:GetWeight() > t[b]:GetWeight() end ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - self:F( { IsUnLoaded = Cargo:IsUnLoaded(), IsDeployed = Cargo:IsDeployed(), Cargo:GetName(), APC:GetName() } ) - if Cargo:IsUnLoaded() then -- and not Cargo:IsDeployed() then - if Cargo:IsInLoadRadius( APCUnit:GetCoordinate() ) then - self:F( { "In radius", APCUnit:GetName() } ) - - local CargoWeight = Cargo:GetWeight() - - -- Only when there is space within the bay to load the next cargo item! - if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then - APC:RouteStop() - --Cargo:Ungroup() - Cargo:Board( APCUnit, 25 ) - self:__Board( 1, Cargo, APCUnit ) - - -- So now this APCUnit has Cargo that is being loaded. - -- This will be used further in the logic to follow and to check cargo status. - self.APC_Cargo[APCUnit] = Cargo - Boarding = true - break - end - end - end - end - end - end - - return Boarding - -end - ---- On after Board event. --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP APC --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Cargo.Cargo#CARGO Cargo Cargo object. --- @param Wrapper.Unit#UNIT APCUnit -function AI_CARGO_APC:onafterBoard( APC, From, Event, To, Cargo, APCUnit ) - self:F( { APC, From, Event, To, Cargo, APCUnit:GetName() } ) - - if APC and APC:IsAlive() then - self:F({ IsLoaded = Cargo:IsLoaded(), Cargo:GetName(), APC:GetName() } ) - if not Cargo:IsLoaded() then - self:__Board( 10, Cargo, APCUnit ) - else - local CargoBayFreeWeight = APCUnit:GetCargoBayFreeWeight() - self:F({CargoBayFreeWeight=CargoBayFreeWeight}) - for _, Cargo in UTILS.spairs( self.CargoSet:GetSet(), function( t, a, b ) return t[a]:GetWeight() > t[b]:GetWeight() end ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - if Cargo:IsUnLoaded() then - if Cargo:IsInLoadRadius( APCUnit:GetCoordinate() ) then - local CargoWeight = Cargo:GetWeight() - - -- Only when there is space within the bay to load the next cargo item! - if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then - Cargo:Board( APCUnit, 25 ) - self:__Board( 10, Cargo, APCUnit ) - -- So now this APCUnit has Cargo that is being loaded. - -- This will be used further in the logic to follow and to check cargo status. - self.APC_Cargo[APCUnit] = Cargo - return - end - end - end - end - self:__Loaded( 5, Cargo ) - end - end - -end - ---- On before Loaded event. --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP APC --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @return #boolean Cargo loaded. -function AI_CARGO_APC:onbeforeLoaded( APC, From, Event, To, Cargo ) - self:F( { APC, From, Event, To } ) - - local Loaded = true - - if APC and APC:IsAlive() then - for APCUnit, Cargo in pairs( self.APC_Cargo ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - self:F( { IsLoaded = Cargo:IsLoaded(), IsDestroyed = Cargo:IsDestroyed(), Cargo:GetName(), APC:GetName() } ) - if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then - Loaded = false - end - end - end - - if Loaded == true then - APC:RouteResume() - end - - return Loaded - -end - - - - - ---- On after Unload event. --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP APC --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param #boolean Deployed Cargo is deployed. -function AI_CARGO_APC:onafterUnload( APC, From, Event, To, Deployed ) - self:F( { APC, From, Event, To, Deployed } ) - - if APC and APC:IsAlive() then - for _, APCUnit in pairs( APC:GetUnits() ) do - local APCUnit = APCUnit -- Wrapper.Unit#UNIT - APC:RouteStop() - for _, Cargo in pairs( APCUnit:GetCargo() ) do - if Cargo:IsLoaded() then - Cargo:UnBoard() - Cargo:SetDeployed( true ) - self:__Unboard( 10, Cargo, Deployed ) - end - end - end - end - -end - ---- On after Unboard event. --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP APC --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param #string Cargo.Cargo#CARGO Cargo Cargo object. --- @param #boolean Deployed Cargo is deployed. -function AI_CARGO_APC:onafterUnboard( APC, From, Event, To, Cargo, Deployed ) - self:F( { APC, From, Event, To, Cargo:GetName() } ) - - if APC and APC:IsAlive() then - if not Cargo:IsUnLoaded() then - self:__Unboard( 10, Cargo, Deployed ) - else - for _, APCUnit in pairs( APC:GetUnits() ) do - local APCUnit = APCUnit -- Wrapper.Unit#UNIT - for _, Cargo in pairs( APCUnit:GetCargo() ) do - if Cargo:IsLoaded() then - Cargo:UnBoard() - Cargo:SetDeployed( true ) - self:__Unboard( 10, Cargo, Deployed ) - return - end - end - end - self:__Unloaded( 1, Cargo, Deployed ) - end - end - -end - ---- On before Unloaded event. --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP APC --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param #string Cargo.Cargo#CARGO Cargo Cargo object. --- @param #boolean Deployed Cargo is deployed. --- @return #boolean All cargo unloaded. -function AI_CARGO_APC:onbeforeUnloaded( APC, From, Event, To, Cargo, Deployed ) - self:F( { APC, From, Event, To, Cargo:GetName(), Deployed = Deployed } ) - - local AllUnloaded = true - - --Cargo:Regroup() - - if APC and APC:IsAlive() then - for _, APCUnit in pairs( APC:GetUnits() ) do - local APCUnit = APCUnit -- Wrapper.Unit#UNIT - local IsEmpty = APCUnit:IsCargoEmpty() - self:I({ IsEmpty = IsEmpty }) - if not IsEmpty then - AllUnloaded = false - break - end - end - - if AllUnloaded == true then - if Deployed == true then - self.APC_Cargo = {} - end - self:Guard() - self.CargoCarrier = APC - end - end - - self:F( { AllUnloaded = AllUnloaded } ) - return AllUnloaded - -end - ---- On after Unloaded event. --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP APC --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param #string Cargo.Cargo#CARGO Cargo Cargo object. --- @param #boolean Deployed Cargo is deployed. --- @return #boolean All cargo unloaded. -function AI_CARGO_APC:onafterUnloaded( APC, From, Event, To, Cargo, Deployed ) - self:F( { APC, From, Event, To, Cargo:GetName(), Deployed = Deployed } ) - - self.Transporting = false - -end - --- On after Follow event. -- @param #AI_CARGO_APC self -- @param Wrapper.Group#GROUP APC @@ -660,7 +305,7 @@ function AI_CARGO_APC:onafterFollow( APC, From, Event, To ) self:F( "Follow" ) if APC and APC:IsAlive() then - for APCUnit, Cargo in pairs( self.APC_Cargo ) do + for Cargo, APCUnit in pairs( self.Carrier_Cargo ) do local Cargo = Cargo -- Cargo.Cargo#CARGO if Cargo:IsUnLoaded() then self:FollowToCarrier( self, APCUnit, Cargo ) @@ -674,25 +319,25 @@ end --- @param #AI_CARGO_APC -- @param Wrapper.Group#GROUP APC -function AI_CARGO_APC._Pickup( APC, self ) +function AI_CARGO_APC._Pickup( APC, self, Coordinate, Speed, PickupZone ) APC:F( { "AI_CARGO_APC._Pickup:", APC:GetName() } ) if APC:IsAlive() then - self:Load() - self.Relocating = true + self:Load( PickupZone ) + self.Relocating = false + self.Transporting = true end end ---- @param #AI_CARGO_APC --- @param Wrapper.Group#GROUP APC -function AI_CARGO_APC._Deploy( APC, self ) +function AI_CARGO_APC._Deploy( APC, self, Coordinate, DeployZone ) APC:F( { "AI_CARGO_APC._Deploy:", APC } ) if APC:IsAlive() then - self:Unload( true ) + self:Unload( DeployZone ) + self.Transporting = false self.Relocating = false end end @@ -707,7 +352,8 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate of the pickup point. -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. -function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed ) +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed, PickupZone ) if APC and APC:IsAlive() then @@ -718,7 +364,7 @@ function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed ) local Waypoints = APC:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true ) - local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Pickup", self ) + local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Pickup", self, Coordinate, Speed, PickupZone ) self:F({Waypoints = Waypoints}) local Waypoint = Waypoints[#Waypoints] @@ -726,10 +372,11 @@ function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed ) APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. else - AI_CARGO_APC._Pickup( APC, self ) + AI_CARGO_APC._Pickup( APC, self, Coordinate, Speed, PickupZone ) end - - self.Transporting = true + + self.Relocating = true + self.Transporting = false end end @@ -743,7 +390,7 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate Deploy place. -- @param #number Speed Speed in km/h to drive to the depoly coordinate. Default is 50% of max possible speed the unit can go. -function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed ) +function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed, DeployZone ) if APC and APC:IsAlive() then @@ -753,13 +400,16 @@ function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed ) local Waypoints = APC:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true ) - local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Deploy", self ) + local TaskFunction = APC:TaskFunction( "AI_CARGO_APC._Deploy", self, Coordinate, DeployZone ) self:F({Waypoints = Waypoints}) local Waypoint = Waypoints[#Waypoints] APC:SetTaskWaypoint( Waypoint, TaskFunction ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone. APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. + + self.Relocating = false + self.Transporting = true end end @@ -773,48 +423,21 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate Home place. -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. -function AI_CARGO_APC:onafterHome( APC, From, Event, To, Coordinate, Speed ) +function AI_CARGO_APC:onafterHome( APC, From, Event, To, Coordinate, Speed, HomeZone ) if APC and APC:IsAlive() ~= nil then self.RouteHome = true - local _speed=Speed or APC:GetSpeedMax()*0.5 + Speed = Speed or APC:GetSpeedMax()*0.5 - local Waypoints = APC:TaskGroundOnRoad( Coordinate, _speed, "Line abreast", true ) + local Waypoints = APC:TaskGroundOnRoad( Coordinate, Speed, "Line abreast", true ) self:F({Waypoints = Waypoints}) local Waypoint = Waypoints[#Waypoints] - - -- Task function triggering the arrived event. - local TaskFunction = APC:TaskFunction("AI_CARGO_APC._BackHome", self) - - -- Put task function on last waypoint. - APC:SetTaskWaypoint( Waypoint, TaskFunction ) APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. end end - ---- Function called when transport is back home and nothing more to do. Triggering the event BackHome. --- @param Wrapper.Group#GROUP APC Cargo carrier. --- @param #AI_CARGO_APC self -function AI_CARGO_APC._BackHome(APC, self) - --Trigger BackHome event. - env.info(string.format("FF APC %s is back home task function!",APC:GetName())) - APC:SmokeGreen() - self:__BackHome(1) -end - ---- On after BackHome event. --- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP APC --- @param From --- @param Event --- @param To -function AI_CARGO_APC:onafterBackHome( APC, From, Event, To ) - env.info(string.format("FF APC %s is back home event!",APC:GetName())) - APC:SmokeRed() -end \ No newline at end of file diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index be200d882..0b4be9907 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -1,4 +1,4 @@ ---- **AI** -- (R2.3) - Models the intelligent transportation of infantry (cargo). +--- **AI** -- (R2.4) - Models the intelligent transportation of infantry (cargo). -- -- === -- @@ -13,12 +13,39 @@ -- @extends Core.Fsm#FSM_CONTROLLABLE ---- Implements the transportation of cargo by airplanes. +--- Brings a dynamic cargo handling capability for an AI airplane group. +-- +-- Airplane carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation between airbases. +-- +-- The AI_CARGO_AIRPLANE module uses the @{Cargo.Cargo} capabilities within the MOOSE framework. +-- @{Cargo.Cargo} must be declared within the mission to make AI_CARGO_AIRPLANE recognize the cargo. +-- Please consult the @{Cargo.Cargo} module for more information. +-- +-- ## Cargo pickup. +-- +-- Using the @{#AI_CARGO_AIRPLANE.Pickup}() method, you are able to direct the helicopters towards a point on the battlefield to board/load the cargo at the specific coordinate. +-- Ensure that the landing zone is horizontally flat, and that trees cannot be found in the landing vicinity, or the helicopters won't land or will even crash! +-- +-- ## Cargo deployment. +-- +-- Using the @{#AI_CARGO_AIRPLANE.Deploy}() method, you are able to direct the helicopters towards a point on the battlefield to unboard/unload the cargo at the specific coordinate. +-- Ensure that the landing zone is horizontally flat, and that trees cannot be found in the landing vicinity, or the helicopters won't land or will even crash! +-- +-- ## Infantry health. +-- +-- When infantry is unboarded from the APCs, the infantry is actually respawned into the battlefield. +-- As a result, the unboarding infantry is very _healthy_ every time it unboards. +-- This is due to the limitation of the DCS simulator, which is not able to specify the health of new spawned units as a parameter. +-- However, infantry that was destroyed when unboarded, won't be respawned again. Destroyed is destroyed. +-- As a result, there is some additional strength that is gained when an unboarding action happens, but in terms of simulation balance this has +-- marginal impact on the overall battlefield simulation. Fortunately, the firing strength of infantry is limited, and thus, respacing healthy infantry every +-- time is not so much of an issue ... +-- -- -- @field #AI_CARGO_AIRPLANE AI_CARGO_AIRPLANE = { ClassName = "AI_CARGO_AIRPLANE", - Coordinate = nil -- Core.Point#COORDINATE, + Coordinate = nil, -- Core.Point#COORDINATE } --- Creates a new AI_CARGO_AIRPLANE object. @@ -28,21 +55,7 @@ AI_CARGO_AIRPLANE = { -- @return #AI_CARGO_AIRPLANE function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) - local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_CARGO_AIRPLANE - - self.CargoSet = CargoSet -- Cargo.CargoGroup#CARGO_GROUP - - self:SetStartState( "Unloaded" ) - - self:AddTransition( { "Unloaded", "Loaded" }, "Pickup", "*" ) - self:AddTransition( "Loaded", "Deploy", "*" ) - - self:AddTransition( { "Unloaded", "Boarding" }, "Load", "Boarding" ) - self:AddTransition( "Boarding", "Board", "Boarding" ) - self:AddTransition( "Boarding", "Loaded", "Loaded" ) - self:AddTransition( "Loaded", "Unload", "Unboarding" ) - self:AddTransition( "Unboarding", "Unboard", "Unboarding" ) - self:AddTransition( "Unboarding" , "Unloaded", "Unloaded" ) + local self = BASE:Inherit( self, AI_CARGO:New( Airplane, CargoSet ) ) -- #AI_CARGO_AIRPLANE self:AddTransition( "*", "Landed", "*" ) self:AddTransition( "*", "Home" , "*" ) @@ -132,7 +145,7 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) AirplaneUnit:SetCargoBayWeightLimit() end - self.Relocating = false --FF should be false or set according to state of airplane! + self.Relocating = true return self end @@ -245,7 +258,7 @@ function AI_CARGO_AIRPLANE:onafterLanded( Airplane, From, Event, To ) -- Aircraft was sent to this airbase to pickup troops. Initiate loadling. if self.RoutePickup == true then env.info("FF load airplane "..Airplane:GetName()) - self:Load( Airplane:GetCoordinate() ) + self:Load( self.PickupZone ) self.RoutePickup = false self.Relocating = true end @@ -269,12 +282,15 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param Wrapper.Airbase#AIRBASE Airbase Airbase where the troops as picked up. +-- @param Core.Point#COORDINATE Coordinate -- @param #number Speed in km/h for travelling to pickup base. -function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Airbase, Speed ) +-- @param Core.Zone#ZONE_AIRBASE PickupZone +function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate, Speed, PickupZone ) if Airplane and Airplane:IsAlive()~=nil then env.info("FF onafterpick aircraft alive") + + self.PickupZone = PickupZone -- Get closest airbase of current position. local ClosestAirbase, DistToAirbase=Airplane:GetCoordinate():GetClosestAirbase() @@ -288,8 +304,10 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Airbase, Sp self.Airbase=ClosestAirbase end + local Airbase = PickupZone:GetAirbase() + -- Distance from closest to pickup airbase ==> we need to know if we are already at the pickup airbase. - local Dist=Airbase:GetCoordinate():Get2DDistance(ClosestAirbase:GetCoordinate()) + local Dist = Airbase:GetCoordinate():Get2DDistance(ClosestAirbase:GetCoordinate()) env.info("Distance closest to pickup airbase = "..Dist) if Airplane:InAir() or Dist>500 then @@ -305,9 +323,6 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Airbase, Sp -- Aircraft is on a pickup mission. self.RoutePickup = true - -- Unclear!? - self.Transporting = true - self.Relocating = false else env.info("FF onafterpick calling landed") @@ -316,9 +331,13 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Airbase, Sp self:Landed() end + + self.Transporting = false + self.Relocating = true else env.info("FF onafterpick aircraft not alive") end + end @@ -328,12 +347,15 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param Wrapper.Airbase#AIRBASE Airbase Airbase where troups should be deployed. --- @param #number Speed Speed in km/h for travelling to deploy base. -function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Airbase, Speed ) +-- @param Core.Point#COORDINATE Coordinate +-- @param #number Speed in km/h for travelling to pickup base. +-- @param Core.Zone#ZONE_AIRBASE DeployZone +function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Coordinate, Speed, DeployZone ) if Airplane and Airplane:IsAlive()~=nil then + local Airbase = DeployZone:GetAirbase() + -- Activate uncontrolled airplane. if Airplane:IsAlive()==false then Airplane:SetCommand({id = 'Start', params = {}}) @@ -355,116 +377,18 @@ function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Airbase, Sp end ---- On before Load event. Checks if cargo is inside the load radius and if so starts the boarding process. --- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane Transport plane. --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Wrapper.Point#COORDINATE Coordinate Place where the cargo is guided to if it is inside the load radius. -function AI_CARGO_AIRPLANE:onbeforeLoad( Airplane, From, Event, To, Coordinate ) - - local Boarding = false - - if Airplane and Airplane:IsAlive() ~= nil then - - for _, AirplaneUnit in pairs( Airplane:GetUnits() ) do - local AirplaneUnit = AirplaneUnit -- Wrapper.Unit#UNIT - for _,_Cargo in pairs( self.CargoSet:GetSet() ) do - self:F({_Cargo:GetName()}) - local Cargo=_Cargo --Cargo.Cargo#CARGO - local InRadius = Cargo:IsInLoadRadius( Coordinate ) - if InRadius then - - -- Is there a cargo still unloaded? - if Cargo:IsUnLoaded() == true then - - Cargo:Board( AirplaneUnit, 25 ) - self:__Board( 5, Cargo, AirplaneUnit ) - Boarding = true - break - end - end - - end - end - end - - return Boarding - -end - ---- On after Board event. Cargo is inside the load radius and boarding is performed. +--- On after PickedUp event. All cargo is inside the carrier and ready to be transported. -- @param #AI_CARGO_AIRPLANE self -- @param Wrapper.Group#GROUP Airplane Cargo transport plane. -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param Cargo.Cargo#CARGO Cargo Cargo object. --- @param Wrapper.Unit#UNIT AirplaneUnit -function AI_CARGO_AIRPLANE:onafterBoard( Airplane, From, Event, To, Cargo, AirplaneUnit ) +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO_AIRPLANE:onafterPickedUp( Airplane, From, Event, To, PickupZone ) + self:F( { AirplaneGroup, From, Event, To } ) if Airplane and Airplane:IsAlive() then - - self:F({ IsLoaded = Cargo:IsLoaded() } ) - - if not Cargo:IsLoaded() then - self:__Board( 10, Cargo, AirplaneUnit ) - else - local CargoBayFreeWeight = AirplaneUnit:GetCargoBayFreeWeight() - self:F({CargoBayFreeWeight=CargoBayFreeWeight}) - - -- Check if another cargo can be loaded into the airplane. - for _,_Cargo in UTILS.spairs( self.CargoSet:GetSet(), function( t, a, b ) return t[a]:GetWeight() > t[b]:GetWeight() end ) do - - self:F({_Cargo:GetName()}) - local Cargo =_Cargo --Cargo.Cargo#CARGO - - -- Is there a cargo still unloaded? - if Cargo:IsUnLoaded() == true then - - -- Only when the cargo is within load radius. - local InRadius = Cargo:IsInLoadRadius( Airplane:GetCoordinate() ) - if InRadius then - - local CargoBayFreeWeight = AirplaneUnit:GetCargoBayFreeWeight() - --local CargoBayFreeVolume = Airplane:GetCargoBayFreeVolume() - - local CargoWeight = Cargo:GetWeight() - --local CargoVolume = Cargo:GetVolume() - - -- Only when there is space within the bay to load the next cargo item! - if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then - - -- ok, board. - self:__Load( 5, Airplane:GetCoordinate() ) - - -- And start the boarding loop for the AI_CARGO_AIRPLANE object until the cargo is boarded. - --Cargo:Board( Airplane, 25 ) - return - end - end - end - end - self:__Loaded( 1, Cargo ) - end - end - -end - ---- On after Loaded event. Cargo is inside the carrier and ready to be transported. --- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane Cargo transport plane. --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function AI_CARGO_AIRPLANE:onafterLoaded( Airplane, From, Event, To, Cargo ) - - env.info("FF troops loaded into cargo plane") - - if Airplane and Airplane:IsAlive() then - self:F( { "Transporting" } ) self.Transporting = true -- This will only be executed when there is no cargo boarded anymore. The dispatcher will then kick-off the deploy cycle! end end @@ -476,7 +400,10 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To ) +function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To, DeployZone ) + + local UnboardInterval = 10 + local UnboardDelay = 10 if Airplane and Airplane:IsAlive() then for _, AirplaneUnit in pairs( Airplane:GetUnits() ) do @@ -489,68 +416,35 @@ function AI_CARGO_AIRPLANE:onafterUnload( Airplane, From, Event, To ) self:T( { CargoCarrierHeading, CargoDeployHeading } ) local CargoDeployCoordinate = Airplane:GetPointVec2():Translate( 150, CargoDeployHeading ) - Cargo:UnBoard( CargoDeployCoordinate ) + Cargo:__UnBoard( UnboardDelay, CargoDeployCoordinate ) + UnboardDelay = UnboardDelay + UnboardInterval Cargo:SetDeployed( true ) - self:__Unboard( 10, Cargo ) + self:__Unboard( UnboardDelay, Cargo, AirplaneUnit, DeployZone ) end end end end ---- On after Unboard event. Checks if unboarding process is finished. --- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane Cargo transport plane. --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. -function AI_CARGO_AIRPLANE:onafterUnboard( Airplane, From, Event, To, Cargo ) - self:E( { "Unboard", Cargo } ) - if Airplane and Airplane:IsAlive() then - if not Cargo:IsUnLoaded() then - self:__Unboard( 10, Cargo ) - else - for _, AirplaneUnit in pairs( Airplane:GetUnits() ) do - local Cargos = AirplaneUnit:GetCargo() - for CargoID, Cargo in pairs( Cargos ) do - if Cargo:IsLoaded() then - local Angle = 180 - local CargoCarrierHeading = Airplane:GetHeading() -- Get Heading of object in degrees. - local CargoDeployHeading = ( ( CargoCarrierHeading + Angle ) >= 360 ) and ( CargoCarrierHeading + Angle - 360 ) or ( CargoCarrierHeading + Angle ) - self:T( { CargoCarrierHeading, CargoDeployHeading } ) - local CargoDeployCoordinate = Airplane:GetPointVec2():Translate( 150, CargoDeployHeading ) - Cargo:UnBoard( CargoDeployCoordinate ) - Cargo:SetDeployed( true ) - - self:__Unboard( 10, Cargo ) - return - end - end - self:__Unloaded( 1, Cargo ) - end - end - end -end - ---- On after Unloaded event. Cargo has been unloaded, i.e. the unboarding process is finished. +--- On after Deployed event. -- @param #AI_CARGO_AIRPLANE self -- @param Wrapper.Group#GROUP Airplane Cargo transport plane. -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param Cargo.Cargo#CARGO Cargo -function AI_CARGO_AIRPLANE:onafterUnloaded( Airplane, From, Event, To, Cargo ) - - self:E( { "Unloaded", Cargo } ) +function AI_CARGO_AIRPLANE:onafterDeployed( Airplane, From, Event, To, DeployZone ) if Airplane and Airplane:IsAlive() then - self.Airplane = Airplane self.Transporting = false -- This will only be executed when there is no cargo onboard anymore. The dispatcher will then kick-off the pickup cycle! end end + + + --- Route the airplane from one airport or it's current position to another airbase. -- @param #AI_CARGO_AIRPLANE self -- @param Wrapper.Group#GROUP Airplane Airplane group to be routed. diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 777c3cff8..eb448955f 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -16,52 +16,360 @@ --- A dynamic cargo handling capability for AI groups. -- -- Carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. --- The AI\_CARGO\_DISPATCHER module uses the @{Cargo} capabilities within the MOOSE framework, to enable Carrier GROUP objects --- to transport @{Cargo} towards several deploy zones. --- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER object recognize the cargo. --- Please consult the @{Cargo} module for more information. +-- The AI_CARGO_DISPATCHER module uses the @{Cargo.Cargo} capabilities within the MOOSE framework, to enable Carrier GROUP objects +-- to transport @{Cargo.Cargo} towards several deploy zones. +-- @{Cargo.Cargo} must be declared within the mission to make the AI_CARGO_DISPATCHER object recognize the cargo. +-- Please consult the @{Cargo.Cargo} module for more information. -- --- ## 1. AI\_CARGO\_DISPATCHER constructor +-- # 1) AI_CARGO_DISPATCHER constructor -- -- * @{#AI_CARGO_DISPATCHER.New}(): Creates a new AI\_CARGO\_DISPATCHER object. -- --- ## 2. AI\_CARGO\_DISPATCHER is a FSM +-- # 2) AI_CARGO_DISPATCHER is a FSM -- --- ![Process](..\Presentations\AI_PATROL\Dia2.JPG) +-- This section must be read as follows. Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed. +-- The first column is the **From** state, the second column the **Event**, and the third column the **To** state. -- --- ### 2.1. AI\_CARGO\_DISPATCHER States +-- So, each of the rows have the following structure. +-- +-- * **From** => **Event** => **To** +-- +-- Important to know is that an event can only be executed if the **current state** is the **From** state. +-- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed, +-- and the resulting state will be the **To** state. +-- +-- These are the different possible state transitions of this state machine implementation: +-- +-- * Idle => Start => Monitoring +-- * Monitoring => Monitor => Monitoring +-- * Monitoring => Stop => Idle +-- +-- * Monitoring => Pickup => Monitoring +-- * Monitoring => Load => Monitoring +-- * Monitoring => Loading => Monitoring +-- * Monitoring => Loaded => Monitoring +-- * Monitoring => PickedUp => Monitoring +-- * Monitoring => Deploy => Monitoring +-- * Monitoring => Unload => Monitoring +-- * Monitoring => Unloaded => Monitoring +-- * Monitoring => Deployed => Monitoring +-- * Monitoring => Home => Monitoring +-- +-- +-- ## 2.1) AI_CARGO_DISPATCHER States -- -- * **Monitoring**: The process is dispatching. -- * **Idle**: The process is idle. -- --- ### 2.2. AI\_CARGO\_DISPATCHER Events +-- ## 2.2) AI_CARGO_DISPATCHER Events -- --- * **Monitor**: Monitor and take action. -- * **Start**: Start the transport process. -- * **Stop**: Stop the transport process. +-- * **Monitor**: Monitor and take action. +-- -- * **Pickup**: Pickup cargo. -- * **Load**: Load the cargo. +-- * **Loading**: The dispatcher is coordinating the loading of a cargo. -- * **Loaded**: Flag that the cargo is loaded. +-- * **PickedUp**: The dispatcher has loaded all requested cargo into the CarrierGroup. -- * **Deploy**: Deploy cargo to a location. -- * **Unload**: Unload the cargo. -- * **Unloaded**: Flag that the cargo is unloaded. +-- * **Deployed**: All cargo is unloaded from the carriers in the group. -- * **Home**: A Carrier is going home. -- --- ## 3. Set the pickup parameters. +-- # 3) Enhance your mission scripts with **Tailored** Event Handling! +-- +-- Use these methods to capture the events and tailor the events with your own code! +-- All classes derived from AI_CARGO_DISPATCHER can capture these events, and you can write your own code. +-- +-- In order to properly capture the events, it is mandatory that you execute the following actions using your script: +-- +-- * Copy / Paste the code section into your script. +-- * Change the CLASS literal to the object name you have in your script. +-- * Within the function, you can now write your own code! +-- * IntelliSense will recognize the type of the variables provided by the function. Note: the From, Event and To variables can be safely ignored, +-- but you need to declare them as they are automatically provided by the event handling system of MOOSE. +-- +-- You can send messages or fire off any other events within the code section. The sky is the limit! +-- +-- ## 3.1) Tailor the **Pickup** event +-- +-- Use this event handler to tailor the event when a CarrierGroup is routed towards a new pickup Coordinate and a specified Speed. +-- You can use this event handler to post messages to players, or provide status updates etc. +-- +-- +-- --- Pickup event handler OnAfter for CLASS. +-- -- Use this event handler to tailor the event when a CarrierGroup is routed towards a new pickup Coordinate and a specified Speed. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- @param #CLASS self +-- -- @param #string From A string that contains the "*from state name*" when the event was triggered. +-- -- @param #string Event A string that contains the "*event name*" when the event was triggered. +-- -- @param #string To A string that contains the "*to state name*" when the event was triggered. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Core.Point#COORDINATE Coordinate The coordinate of the pickup location. +-- -- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the pickup Coordinate. +-- -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. +-- function CLASS:OnAfterPickup( From, Event, To, CarrierGroup, Coordinate, Speed, PickupZone ) +-- +-- -- Write here your own code. +-- +-- end +-- +-- +-- ## 3.2) Tailor the **Load** event +-- +-- Use this event handler to tailor the event when a CarrierGroup has initiated the loading or boarding of cargo within reporting or near range. +-- You can use this event handler to post messages to players, or provide status updates etc. +-- +-- +-- --- Load event handler OnAfter for CLASS. +-- -- Use this event handler to tailor the event when a CarrierGroup has initiated the loading or boarding of cargo within reporting or near range. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- @param #CLASS self +-- -- @param #string From A string that contains the "*from state name*" when the event was triggered. +-- -- @param #string Event A string that contains the "*event name*" when the event was triggered. +-- -- @param #string To A string that contains the "*to state name*" when the event was triggered. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. +-- function CLASS:OnAfterLoad( From, Event, To, CarrierGroup, PickupZone ) +-- +-- -- Write here your own code. +-- +-- end +-- +-- +-- ## 3.3) Tailor the **Loading** event +-- +-- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup is in the process of loading or boarding of a cargo object. +-- You can use this event handler to post messages to players, or provide status updates etc. +-- +-- +-- --- Loading event handler OnAfter for CLASS. +-- -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup is in the process of loading or boarding of a cargo object. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- Note that this event is triggered repeatedly until all cargo (units) have been boarded into the carrier. +-- -- @param #CLASS self +-- -- @param #string From A string that contains the "*from state name*" when the event was triggered. +-- -- @param #string Event A string that contains the "*event name*" when the event was triggered. +-- -- @param #string To A string that contains the "*to state name*" when the event was triggered. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Cargo.Cargo#CARGO Cargo The cargo object. +-- -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo loading operation. +-- -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. +-- function CLASS:OnAfterLoading( From, Event, To, CarrierGroup, Cargo, CarrierUnit, PickupZone ) +-- +-- -- Write here your own code. +-- +-- end +-- +-- +-- ## 3.4) Tailor the **Loaded** event +-- +-- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup has loaded a cargo object. +-- You can use this event handler to post messages to players, or provide status updates etc. +-- Note that if more cargo objects were loading or boarding into the CarrierUnit, then this event can be triggered multiple times for each different Cargo/CarrierUnit. +-- +-- The function provides the CarrierGroup, which is the main group that was loading the Cargo into the CarrierUnit. +-- A CarrierUnit is part of the larger CarrierGroup. +-- +-- +-- --- Loaded event handler OnAfter for CLASS. +-- -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup has loaded a cargo object. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- Note that if more cargo objects were loading or boarding into the CarrierUnit, then this event can be triggered multiple times for each different Cargo/CarrierUnit. +-- -- A CarrierUnit can be part of the larger CarrierGroup. +-- -- @param #CLASS self +-- -- @param #string From A string that contains the "*from state name*" when the event was triggered. +-- -- @param #string Event A string that contains the "*event name*" when the event was triggered. +-- -- @param #string To A string that contains the "*to state name*" when the event was triggered. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Cargo.Cargo#CARGO Cargo The cargo object. +-- -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo loading operation. +-- -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. +-- function CLASS:OnAfterLoaded( From, Event, To, CarrierGroup, Cargo, CarrierUnit, PickupZone ) +-- +-- -- Write here your own code. +-- +-- end +-- +-- +-- ## 3.5) Tailor the **PickedUp** event +-- +-- Use this event handler to tailor the event when a carrier has picked up all cargo objects into the CarrierGroup. +-- You can use this event handler to post messages to players, or provide status updates etc. +-- +-- +-- --- PickedUp event handler OnAfter for CLASS. +-- -- Use this event handler to tailor the event when a carrier has picked up all cargo objects into the CarrierGroup. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- @param #CLASS self +-- -- @param #string From A string that contains the "*from state name*" when the event was triggered. +-- -- @param #string Event A string that contains the "*event name*" when the event was triggered. +-- -- @param #string To A string that contains the "*to state name*" when the event was triggered. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. +-- function CLASS:OnAfterPickedUp( From, Event, To, CarrierGroup, PickupZone ) +-- +-- -- Write here your own code. +-- +-- end +-- +-- +-- ## 3.6) Tailor the **Deploy** event +-- +-- Use this event handler to tailor the event when a CarrierGroup is routed to a deploy coordinate, to Unload all cargo objects in each CarrierUnit. +-- You can use this event handler to post messages to players, or provide status updates etc. +-- +-- +-- --- Deploy event handler OnAfter for CLASS. +-- -- Use this event handler to tailor the event when a CarrierGroup is routed to a deploy coordinate, to Unload all cargo objects in each CarrierUnit. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- @param #CLASS self +-- -- @param #string From A string that contains the "*from state name*" when the event was triggered. +-- -- @param #string Event A string that contains the "*event name*" when the event was triggered. +-- -- @param #string To A string that contains the "*to state name*" when the event was triggered. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Core.Point#COORDINATE Coordinate The deploy coordinate. +-- -- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the deploy Coordinate. +-- -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +-- function CLASS:OnAfterDeploy( From, Event, To, CarrierGroup, Coordinate, Speed, DeployZone ) +-- +-- -- Write here your own code. +-- +-- end +-- +-- +-- ## 3.7) Tailor the **Unload** event +-- +-- Use this event handler to tailor the event when a CarrierGroup has initiated the unloading or unboarding of cargo. +-- You can use this event handler to post messages to players, or provide status updates etc. +-- +-- +-- --- Unload event handler OnAfter for CLASS. +-- -- Use this event handler to tailor the event when a CarrierGroup has initiated the unloading or unboarding of cargo. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- @param #CLASS self +-- -- @param #string From A string that contains the "*from state name*" when the event was triggered. +-- -- @param #string Event A string that contains the "*event name*" when the event was triggered. +-- -- @param #string To A string that contains the "*to state name*" when the event was triggered. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +-- function CLASS:OnAfterUnload( From, Event, To, CarrierGroup, DeployZone ) +-- +-- -- Write here your own code. +-- +-- end +-- +-- +-- ## 3.8) Tailor the **Unloading** event +-- +-- +-- --- UnLoading event handler OnAfter for CLASS. +-- -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup is in the process of unloading or unboarding of a cargo object. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- Note that this event is triggered repeatedly until all cargo (units) have been unboarded from the CarrierUnit. +-- -- @param #CLASS self +-- -- @param #string From A string that contains the "*from state name*" when the event was triggered. +-- -- @param #string Event A string that contains the "*event name*" when the event was triggered. +-- -- @param #string To A string that contains the "*to state name*" when the event was triggered. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Cargo.Cargo#CARGO Cargo The cargo object. +-- -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo unloading operation. +-- -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +-- function CLASS:OnAfterUnload( From, Event, To, CarrierGroup, Cargo, CarrierUnit, DeployZone ) +-- +-- -- Write here your own code. +-- +-- end +-- +-- +-- ## 3.9) Tailor the **Unloaded** event +-- +-- +-- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup has unloaded a cargo object. +-- You can use this event handler to post messages to players, or provide status updates etc. +-- +-- --- Unloaded event handler OnAfter for CLASS. +-- -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup has unloaded a cargo object. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- Note that if more cargo objects were unloading or unboarding from the CarrierUnit, then this event can be triggered multiple times for each different Cargo/CarrierUnit. +-- -- A CarrierUnit can be part of the larger CarrierGroup. +-- -- @param #CLASS self +-- -- @param #string From A string that contains the "*from state name*" when the event was triggered. +-- -- @param #string Event A string that contains the "*event name*" when the event was triggered. +-- -- @param #string To A string that contains the "*to state name*" when the event was triggered. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Cargo.Cargo#CARGO Cargo The cargo object. +-- -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo unloading operation. +-- -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +-- function CLASS:OnAfterUnloaded( From, Event, To, CarrierGroup, Cargo, CarrierUnit, DeployZone ) +-- +-- -- Write here your own code. +-- +-- end +-- +-- +-- ## 3.10) Tailor the **Deployed** event +-- +-- Use this event handler to tailor the event when a carrier has deployed all cargo objects from the CarrierGroup. +-- You can use this event handler to post messages to players, or provide status updates etc. +-- +-- +-- --- Deployed event handler OnAfter for CLASS. +-- -- Use this event handler to tailor the event when a carrier has deployed all cargo objects from the CarrierGroup. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- @param #CLASS self +-- -- @param #string From A string that contains the "*from state name*" when the event was triggered. +-- -- @param #string Event A string that contains the "*event name*" when the event was triggered. +-- -- @param #string To A string that contains the "*to state name*" when the event was triggered. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +-- function CLASS:OnAfterDeployed( From, Event, To, CarrierGroup, DeployZone ) +-- +-- -- Write here your own code. +-- +-- end +-- +-- ## 3.11) Tailor the **Home** event +-- +-- Use this event handler to tailor the event when a CarrierGroup is returning to the HomeZone, after it has deployed all cargo objects from the CarrierGroup. +-- You can use this event handler to post messages to players, or provide status updates etc. +-- +-- --- Home event handler OnAfter for CLASS. +-- -- Use this event handler to tailor the event when a CarrierGroup is returning to the HomeZone, after it has deployed all cargo objects from the CarrierGroup. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- If there is no HomeZone is specified, the CarrierGroup will stay at the current location after having deployed all cargo and this event won't be triggered. +-- -- @param #CLASS self +-- -- @param #string From A string that contains the "*from state name*" when the event was triggered. +-- -- @param #string Event A string that contains the "*event name*" when the event was triggered. +-- -- @param #string To A string that contains the "*to state name*" when the event was triggered. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Core.Point#COORDINATE Coordinate The home coordinate the Carrier will arrive and stop it's activities. +-- -- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the home Coordinate. +-- -- @param Core.Zone#ZONE HomeZone The zone wherein the carrier will return when all cargo has been transported. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +-- function CLASS:OnAfterHome( From, Event, To, CarrierGroup, Coordinate, Speed, HomeZone ) +-- +-- -- Write here your own code. +-- +-- end +-- +-- +-- # 3) Set the pickup parameters. -- -- Several parameters can be set to pickup cargo: -- -- * @{#AI_CARGO_DISPATCHER.SetPickupRadius}(): Sets or randomizes the pickup location for the carrier around the cargo coordinate in a radius defined an outer and optional inner radius. -- * @{#AI_CARGO_DISPATCHER.SetPickupSpeed}(): Set the speed or randomizes the speed in km/h to pickup the cargo. -- --- ## 4. Set the deploy parameters. +-- # 4) Set the deploy parameters. -- -- Several parameters can be set to deploy cargo: -- -- * @{#AI_CARGO_DISPATCHER.SetDeployRadius}(): Sets or randomizes the deploy location for the carrier around the cargo coordinate in a radius defined an outer and an optional inner radius. -- * @{#AI_CARGO_DISPATCHER.SetDeploySpeed}(): Set the speed or randomizes the speed in km/h to deploy the cargo. -- --- ## 5. Set the home zone when there isn't any more cargo to pickup. +-- # 5) Set the home zone when there isn't any more cargo to pickup. -- -- A home zone can be specified to where the Carriers will move when there isn't any cargo left for pickup. -- Use @{#AI_CARGO_DISPATCHER.SetHomeZone}() to specify the home zone. @@ -74,7 +382,7 @@ AI_CARGO_DISPATCHER = { ClassName = "AI_CARGO_DISPATCHER", SetCarrier = nil, - DeployZonesSet = nil, + DeployZoneSet = nil, AI_Cargo = {}, PickupCargo = {} } @@ -114,21 +422,25 @@ function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo ) self:AddTransition( "Monitoring", "Stop", "Idle" ) - self:AddTransition( "*", "Pickup", "*" ) - self:AddTransition( "*", "Loading", "*" ) - self:AddTransition( "*", "Loaded", "*" ) + self:AddTransition( "Monitoring", "Pickup", "Monitoring" ) + self:AddTransition( "Monitoring", "Load", "Monitoring" ) + self:AddTransition( "Monitoring", "Loading", "Monitoring" ) + self:AddTransition( "Monitoring", "Loaded", "Monitoring" ) + self:AddTransition( "Monitoring", "PickedUp", "Monitoring" ) - self:AddTransition( "*", "Deploy", "*" ) - self:AddTransition( "*", "Unloading", "*" ) - self:AddTransition( "*", "Unloaded", "*" ) + self:AddTransition( "Monitoring", "Transport", "Monitoring" ) + + self:AddTransition( "Monitoring", "Deploy", "Monitoring" ) + self:AddTransition( "Monitoring", "Unload", "Monitoring" ) + self:AddTransition( "Monitoring", "Unloading", "Monitoring" ) + self:AddTransition( "Monitoring", "Unloaded", "Monitoring" ) + self:AddTransition( "Monitoring", "Deployed", "Monitoring" ) - self:AddTransition( "*", "Home", "*" ) - self:AddTransition( "*", "RTB", "*" ) --FF - self:AddTransition( "*", "BackHome", "*" ) --FF + self:AddTransition( "Monitoring", "Home", "Monitoring" ) self.MonitorTimeInterval = 30 - self.DeployInnerRadius = 200 - self.DeployOuterRadius = 500 + self.DeployRadiusInner = 200 + self.DeployRadiusOuter = 500 self.PickupCargo = {} self.CarrierHome = {} @@ -146,23 +458,25 @@ end --- Creates a new AI_CARGO_DISPATCHER object. -- @param #AI_CARGO_DISPATCHER self --- @param Core.Set#SET_GROUP SetCarriers --- @param Core.Set#SET_CARGO SetCargos --- @param Core.Set#SET_ZONE DeployZonesSet +-- @param Core.Set#SET_GROUP SetCarrier +-- @param Core.Set#SET_CARGO SetCargo +-- @param Core.Set#SET_ZONE PickupZoneSet +-- @param Core.Set#SET_ZONE DeployZoneSet -- @return #AI_CARGO_DISPATCHER -- @usage -- -- -- Create a new cargo dispatcher -- SetCarriers = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() -- SetCargos = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() --- DeployZonesSet = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() +-- DeployZoneSet = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() -- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZone ) -- -function AI_CARGO_DISPATCHER:NewWithZones( SetCarriers, SetCargos, DeployZonesSet ) +function AI_CARGO_DISPATCHER:NewWithZones( SetCarriers, SetCargos, PickupZoneSet, DeployZoneSet ) local self = AI_CARGO_DISPATCHER:New( SetCarriers, SetCargos ) -- #AI_CARGO_DISPATCHER - self.DeployZonesSet = DeployZonesSet + self.PickupZoneSet = PickupZoneSet + self.DeployZoneSet = DeployZoneSet return self end @@ -231,6 +545,19 @@ function AI_CARGO_DISPATCHER:SetHomeBase( HomeBase ) end +--- Set the home base. +-- When there is nothing anymore to pickup, the carriers will return to their home airbase. There they will await new orders. +-- @param #AI_CARGO_DISPATCHER self +-- @param Wrapper.Airbase#AIRBASE HomeBase The airbase where the carrier will go to, once they completed all pending assignments. +-- @return #AI_CARGO_DISPATCHER self +function AI_CARGO_DISPATCHER:SetHomeBase( HomeBase ) + + self.HomeBase = HomeBase + + return self +end + + --- Sets or randomizes the pickup location for the carrier around the cargo coordinate in a radius defined an outer and optional inner radius. -- This radius is influencing the location where the carrier will land to pickup the cargo. -- There are two aspects that are very important to remember and take into account: @@ -356,51 +683,203 @@ end function AI_CARGO_DISPATCHER:onafterMonitor() for CarrierGroupName, Carrier in pairs( self.SetCarrier:GetSet() ) do - env.info("FF cargo dispatcher carrier group "..CarrierGroupName) - local Carrier = Carrier -- Wrapper.Group#GROUP local AI_Cargo = self.AI_Cargo[Carrier] if not AI_Cargo then - env.info("FF not AI CARGO") -- ok, so this Carrier does not have yet an AI_CARGO handling object... -- let's create one and also declare the Loaded and UnLoaded handlers. self.AI_Cargo[Carrier] = self:AICargo( Carrier, self.SetCargo, self.CombatRadius ) AI_Cargo = self.AI_Cargo[Carrier] - function AI_Cargo.OnAfterPickup( AI_Cargo, Carrier, From, Event, To, Cargo ) - self:Pickup( Carrier, Cargo ) + --- Pickup event handler OnAfter for AI_CARGO_DISPATCHER. + -- Use this event handler to tailor the event when a CarrierGroup is routed towards a new pickup Coordinate and a specified Speed. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterPickup + -- @param #AI_CARGO_DISPATCHER self + -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- @param #string To A string that contains the "*to state name*" when the event was triggered. + -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. + -- @param Core.Point#COORDINATE Coordinate The coordinate of the pickup location. + -- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the pickup Coordinate. + -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. + function AI_Cargo.OnAfterPickup( AI_Cargo, CarrierGroup, From, Event, To, Coordinate, Speed, PickupZone ) + self:Pickup( CarrierGroup, Coordinate, Speed, PickupZone ) end - function AI_Cargo.OnAfterLoad( AI_Cargo, Carrier, From, Event, To, Cargo ) - self:Loading( Carrier ) + --- Load event handler OnAfter for AI_CARGO_DISPATCHER. + -- Use this event handler to tailor the event when a CarrierGroup has initiated the loading or boarding of cargo within reporting or near range. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterLoad + -- @param #AI_CARGO_DISPATCHER self + -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- @param #string To A string that contains the "*to state name*" when the event was triggered. + -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. + -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. + + function AI_Cargo.OnAfterLoad( AI_Cargo, CarrierGroup, From, Event, To, PickupZone ) + self:Load( CarrierGroup, PickupZone ) end - function AI_Cargo.OnAfterLoaded( AI_Cargo, Carrier, From, Event, To, Cargo ) - self:Loaded( Carrier, Cargo ) + --- Loading event handler OnAfter for AI_CARGO_DISPATCHER. + -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup is in the process of loading or boarding of a cargo object. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- Note that this event is triggered repeatedly until all cargo (units) have been boarded into the carrier. + -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterLoading + -- @param #AI_CARGO_DISPATCHER self + -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- @param #string To A string that contains the "*to state name*" when the event was triggered. + -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. + -- @param Cargo.Cargo#CARGO Cargo The cargo object. + -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo loading operation. + -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. + + function AI_Cargo.OnAfterBoard( AI_Cargo, CarrierGroup, From, Event, To, Cargo, CarrierUnit, PickupZone ) + self:Loading( CarrierGroup, Cargo, CarrierUnit, PickupZone ) end - function AI_Cargo.OnAfterDeploy( AI_Cargo, Carrier, From, Event, To, Cargo ) - self:Deploy( Carrier, Cargo ) + --- Loaded event handler OnAfter for AI_CARGO_DISPATCHER. + -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup has loaded a cargo object. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- Note that if more cargo objects were loading or boarding into the CarrierUnit, then this event can be triggered multiple times for each different Cargo/CarrierUnit. + -- A CarrierUnit can be part of the larger CarrierGroup. + -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterLoaded + -- @param #AI_CARGO_DISPATCHER self + -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- @param #string To A string that contains the "*to state name*" when the event was triggered. + -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. + -- @param Cargo.Cargo#CARGO Cargo The cargo object. + -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo loading operation. + -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. + + function AI_Cargo.OnAfterLoaded( AI_Cargo, CarrierGroup, From, Event, To, Cargo, CarrierUnit, PickupZone ) + self:Loaded( CarrierGroup, Cargo, CarrierUnit, PickupZone ) + end + + --- PickedUp event handler OnAfter for AI_CARGO_DISPATCHER. + -- Use this event handler to tailor the event when a carrier has picked up all cargo objects into the CarrierGroup. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterPickedUp + -- @param #AI_CARGO_DISPATCHER self + -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- @param #string To A string that contains the "*to state name*" when the event was triggered. + -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. + -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. + + function AI_Cargo.OnAfterPickedUp( AI_Cargo, CarrierGroup, From, Event, To, PickupZone ) + self:PickedUp( CarrierGroup, PickupZone ) + self:Transport( CarrierGroup ) + end + + + --- Deploy event handler OnAfter for AI_CARGO_DISPATCHER. + -- Use this event handler to tailor the event when a CarrierGroup is routed to a deploy coordinate, to Unload all cargo objects in each CarrierUnit. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterDeploy + -- @param #AI_CARGO_DISPATCHER self + -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- @param #string To A string that contains the "*to state name*" when the event was triggered. + -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. + -- @param Core.Point#COORDINATE Coordinate The deploy coordinate. + -- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the deploy Coordinate. + -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. + + function AI_Cargo.OnAfterDeploy( AI_Cargo, CarrierGroup, From, Event, To, Coordinate, Speed, DeployZone ) + self:Deploy( CarrierGroup, Coordinate, Speed, DeployZone ) end - function AI_Cargo.OnAfterUnload( AI_Cargo, Carrier, From, Event, To, Cargo ) - self:Unloading( Carrier, Cargo ) + + --- Unload event handler OnAfter for AI_CARGO_DISPATCHER. + -- Use this event handler to tailor the event when a CarrierGroup has initiated the unloading or unboarding of cargo. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterUnload + -- @param #AI_CARGO_DISPATCHER self + -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- @param #string To A string that contains the "*to state name*" when the event was triggered. + -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. + -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. + + function AI_Cargo.OnAfterUnload( AI_Cargo, Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone ) + self:Unloading( Carrier, Cargo, CarrierUnit, DeployZone ) end - function AI_Cargo.OnAfterUnloaded( AI_Cargo, Carrier, From, Event, To, Cargo ) - self:Unloaded( Carrier, Cargo ) - end + --- UnLoading event handler OnAfter for AI_CARGO_DISPATCHER. + -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup is in the process of unloading or unboarding of a cargo object. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- Note that this event is triggered repeatedly until all cargo (units) have been unboarded from the CarrierUnit. + -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterUnloading + -- @param #AI_CARGO_DISPATCHER self + -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- @param #string To A string that contains the "*to state name*" when the event was triggered. + -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. + -- @param Cargo.Cargo#CARGO Cargo The cargo object. + -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo unloading operation. + -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. - -- FF added BackHome event. - function AI_Cargo.OnAfterBackHome( AI_Cargo, Carrier, From, Event, To) - self:BackHome( Carrier ) + function AI_Cargo.OnAfterUnboard( AI_Cargo, CarrierGroup, From, Event, To, Cargo, CarrierUnit, DeployZone ) + self:Unloading( CarrierGroup, Cargo, CarrierUnit, DeployZone ) end + + + --- Unloaded event handler OnAfter for AI_CARGO_DISPATCHER. + -- Use this event handler to tailor the event when a CarrierUnit of a CarrierGroup has unloaded a cargo object. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- Note that if more cargo objects were unloading or unboarding from the CarrierUnit, then this event can be triggered multiple times for each different Cargo/CarrierUnit. + -- A CarrierUnit can be part of the larger CarrierGroup. + -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterUnloaded + -- @param #AI_CARGO_DISPATCHER self + -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- @param #string To A string that contains the "*to state name*" when the event was triggered. + -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. + -- @param Cargo.Cargo#CARGO Cargo The cargo object. + -- @param Wrapper.Unit#UNIT CarrierUnit The carrier unit that is executing the cargo unloading operation. + -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. - -- FF added RTB event. - function AI_Cargo.OnAfterRTB( AI_Cargo, Carrier, From, Event, To, Airbase) - self:RTB( Carrier, Airbase ) - end + function AI_Cargo.OnAfterUnloaded( AI_Cargo, Carrier, From, Event, To, Cargo, CarrierUnit, DeployZone ) + self:Unloaded( Carrier, Cargo, CarrierUnit, DeployZone ) + end + + --- Deployed event handler OnAfter for AI_CARGO_DISPATCHER. + -- Use this event handler to tailor the event when a carrier has deployed all cargo objects from the CarrierGroup. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterDeployed + -- @param #AI_CARGO_DISPATCHER self + -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- @param #string To A string that contains the "*to state name*" when the event was triggered. + -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. + -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. + + function AI_Cargo.OnAfterDeployed( AI_Cargo, Carrier, From, Event, To, DeployZone ) + self:Deployed( Carrier, DeployZone ) + end + + --- Home event handler OnAfter for AI_CARGO_DISPATCHER. + -- Use this event handler to tailor the event when a CarrierGroup is returning to the HomeZone, after it has deployed all cargo objects from the CarrierGroup. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- If there is no HomeZone is specified, the CarrierGroup will stay at the current location after having deployed all cargo. + -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterHome + -- @param #AI_CARGO_DISPATCHER self + -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- @param #string To A string that contains the "*to state name*" when the event was triggered. + -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. + -- @param Core.Point#COORDINATE Coordinate The home coordinate the Carrier will arrive and stop it's activities. + -- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the home Coordinate. + -- @param Core.Zone#ZONE HomeZone The zone wherein the carrier will return when all cargo has been transported. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. + + function AI_Cargo.OnAfterHome( AI_Cargo, Carrier, From, Event, To, Coordinate, Speed, HomeZone ) + self:Home( Carrier, Coordinate, Speed, HomeZone ) + end end -- The Pickup sequence ... @@ -412,6 +891,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() -- now find the first cargo that is Unloaded local PickupCargo = nil + local PickupZone = nil for CargoName, Cargo in UTILS.spairs( self.SetCargo:GetSet(), function( t, a, b ) return t[a]:GetWeight() < t[b]:GetWeight() end ) do local Cargo = Cargo -- Cargo.Cargo#CARGO @@ -419,81 +899,53 @@ function AI_CARGO_DISPATCHER:onafterMonitor() if Cargo:IsUnLoaded() == true and Cargo:IsDeployed() == false then local CargoCoordinate = Cargo:GetCoordinate() local CoordinateFree = true - for CarrierPickup, Coordinate in pairs( self.PickupCargo ) do - if CarrierPickup:IsAlive() == true then - if CargoCoordinate:Get2DDistance( Coordinate ) <= 25 then - CoordinateFree = false + PickupZone = self.PickupZoneSet and self.PickupZoneSet:IsCoordinateInZone( CargoCoordinate ) + if not self.PickupZoneSet or PickupZone then + for CarrierPickup, Coordinate in pairs( self.PickupCargo ) do + if CarrierPickup:IsAlive() == true then + if CargoCoordinate:Get2DDistance( Coordinate ) <= 100 then + CoordinateFree = false + break + end + else + self.PickupCargo[CarrierPickup] = nil + end + end + if CoordinateFree == true then + -- Check if this cargo can be picked-up by at least one carrier unit of AI_Cargo. + local LargestLoadCapacity = 0 + for _, Carrier in pairs( Carrier:GetUnits() ) do + local LoadCapacity = Carrier:GetCargoBayFreeWeight() + if LargestLoadCapacity < LoadCapacity then + LargestLoadCapacity = LoadCapacity + end + end + -- So if there is aa carrier that has the required load capacity to load the total weight of the cargo, dispatch the carrier. + -- Otherwise break and go to the next carrier. + -- This will skip cargo which is too large to be able to be loaded by carriers + -- and will secure an efficient dispatching scheme. + if LargestLoadCapacity >= Cargo:GetWeight() then + self.PickupCargo[Carrier] = CargoCoordinate + PickupCargo = Cargo break end - else - self.PickupCargo[CarrierPickup] = nil - end - end - if CoordinateFree == true then - -- Check if this cargo can be picked-up by at least one carrier unit of AI_Cargo. - local LargestLoadCapacity = 0 - for _, Carrier in pairs( Carrier:GetUnits() ) do - local LoadCapacity = Carrier:GetCargoBayFreeWeight() - if LargestLoadCapacity < LoadCapacity then - LargestLoadCapacity = LoadCapacity - end - end - -- So if there is aa carrier that has the required load capacity to load the total weight of the cargo, dispatch the carrier. - -- Otherwise break and go to the next carrier. - -- This will skip cargo which is too large to be able to be loaded by carriers - -- and will secure an efficient dispatching scheme. - if LargestLoadCapacity >= Cargo:GetWeight() then - self.PickupCargo[Carrier] = CargoCoordinate - PickupCargo = Cargo - break end end end end if PickupCargo then - self.CarrierHome[Carrier] = nil local PickupCoordinate = PickupCargo:GetCoordinate():GetRandomCoordinateInRadius( self.PickupOuterRadius, self.PickupInnerRadius ) - - if self.PickupAirbasesSet then - -- Find airbase within 2km from the cargo with the set. - local PickupAirbase = self.PickupAirbasesSet:FindAirbaseInRange( PickupCoordinate, 4000 ) - if PickupAirbase then - AI_Cargo:Pickup( PickupAirbase, math.random( self.PickupMinSpeed, self.PickupMaxSpeed ) ) - end - else - AI_Cargo:Pickup( PickupCoordinate, math.random( self.PickupMinSpeed, self.PickupMaxSpeed ) ) - end + AI_Cargo:Pickup( PickupCoordinate, math.random( self.PickupMinSpeed, self.PickupMaxSpeed ), PickupZone ) break - else - - env.info("FF HomeZone or HomeBase?") if self.HomeZone then - - env.info("FF HomeZone! Really?") if not self.CarrierHome[Carrier] then - env.info("FF Yes!") self.CarrierHome[Carrier] = true - AI_Cargo:__Home( 60, self.HomeZone:GetRandomPointVec2() ) - else - env.info("FF Nope!") + AI_Cargo:__Home( 60, self.HomeZone:GetRandomPointVec2(), math.random( self.PickupMinSpeed, self.PickupMaxSpeed ), self.HomeZone ) end - - elseif self.HomeBase2 then - - env.info("FF HomeBase! Really?") - if not self.CarrierHome[Carrier] then - env.info("FF Yes!") - self.CarrierHome[Carrier] = true - AI_Cargo:__RTB( 1, self.HomeBase ) - else - env.info("FF Nope!") - end - end - end end end @@ -501,7 +953,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() self:__Monitor( self.MonitorTimeInterval ) end ---- Start Handler OnBefore for AI_CARGO_DISPATCHER +--- Start event handler OnBefore for AI_CARGO_DISPATCHER -- @function [parent=#AI_CARGO_DISPATCHER] OnBeforeStart -- @param #AI_CARGO_DISPATCHER self -- @param #string From @@ -509,7 +961,7 @@ end -- @param #string To -- @return #boolean ---- Start Handler OnAfter for AI_CARGO_DISPATCHER +--- Start event handler OnAfter for AI_CARGO_DISPATCHER -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterStart -- @param #AI_CARGO_DISPATCHER self -- @param #string From @@ -529,7 +981,7 @@ function AI_CARGO_DISPATCHER:onafterStart( From, Event, To ) self:__Monitor( -1 ) end ---- Stop Handler OnBefore for AI_CARGO_DISPATCHER +--- Stop event handler OnBefore for AI_CARGO_DISPATCHER -- @function [parent=#AI_CARGO_DISPATCHER] OnBeforeStop -- @param #AI_CARGO_DISPATCHER self -- @param #string From @@ -537,7 +989,7 @@ end -- @param #string To -- @return #boolean ---- Stop Handler OnAfter for AI_CARGO_DISPATCHER +--- Stop event handler OnAfter for AI_CARGO_DISPATCHER -- @function [parent=#AI_CARGO_DISPATCHER] OnAfterStop -- @param #AI_CARGO_DISPATCHER self -- @param #string From @@ -554,30 +1006,6 @@ end -- @param #number Delay - ---- Loaded Handler OnAfter for AI_CARGO_DISPATCHER --- @function [parent=#AI_CARGO_DISPATCHER] OnAfterLoaded --- @param #AI_CARGO_DISPATCHER self --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Wrapper.Group#GROUP Carrier Carrier object. --- @param Cargo.Cargo#CARGO Cargo Cargo object. - ---- Unloaded Handler OnAfter for AI_CARGO_DISPATCHER --- @function [parent=#AI_CARGO_DISPATCHER] OnAfterUnloaded --- @param #AI_CARGO_DISPATCHER self --- @param #string From --- @param #string Event --- @param #string To --- @param Wrapper.Group#GROUP Carrier --- @param Cargo.Cargo#CARGO Cargo - - - - - - --- Make a Carrier run for a cargo deploy action after the cargo has been loaded, by default. -- @param #AI_CARGO_DISPATCHER self -- @param From @@ -586,23 +1014,14 @@ end -- @param Wrapper.Group#GROUP Carrier -- @param Cargo.Cargo#CARGO Cargo -- @return #AI_CARGO_DISPATCHER -function AI_CARGO_DISPATCHER:OnAfterLoaded( From, Event, To, Carrier, Cargo ) - +function AI_CARGO_DISPATCHER:onafterTransport( From, Event, To, Carrier, Cargo ) - if self.DeployZonesSet then - - local DeployZone = self.DeployZonesSet:GetRandomZone() - - local DeployCoordinate = DeployZone:GetCoordinate():GetRandomCoordinateInRadius( self.DeployOuterRadius, self.DeployInnerRadius ) - self.AI_Cargo[Carrier]:Deploy( DeployCoordinate, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ) ) - - end - - if self.DeployAirbasesSet then - + if self.DeployZoneSet then if self.AI_Cargo[Carrier]:IsTransporting() == true then - local DeployAirbase = self.DeployAirbasesSet:GetRandomAirbase() - self.AI_Cargo[Carrier]:Deploy( DeployAirbase, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ) ) + local DeployZone = self.DeployZoneSet:GetRandomZone() + + local DeployCoordinate = DeployZone:GetCoordinate():GetRandomCoordinateInRadius( self.DeployOuterRadius, self.DeployInnerRadius ) + self.AI_Cargo[Carrier]:Deploy( DeployCoordinate, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ), DeployZone ) end end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index 1612ce2c7..fffaaf90f 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -1,4 +1,4 @@ ---- **AI** -- Models the intelligent transportation of infantry and other cargo using APCs. +--- **AI** -- (2.4) - Models the intelligent transportation of infantry and other cargo using APCs. -- -- **Features:** -- @@ -28,9 +28,18 @@ --- A dynamic cargo transportation capability for AI groups. -- -- Armoured Personnel APCs (APC), Trucks, Jeeps and other carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. --- The AI\_CARGO\_DISPATCHER\_APC module uses the @{Cargo} capabilities within the MOOSE framework. --- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER\_APC object recognize the cargo. --- Please consult the @{Cargo} module for more information. +-- +-- The AI_CARGO_DISPATCHER_APC module is derived from the AI_CARGO_DISPATCHER module. +-- +-- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_APC class, it is recommended that you +-- **first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!!** +-- +-- Especially to learn how to **Tailor the different cargo handling events**, this will be very useful! +-- +-- On top, the AI_CARGO_DISPATCHER_APC class uses the @{Cargo.Cargo} capabilities within the MOOSE framework. +-- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class. +-- CARGO derived objects must be declared within the mission to make the AI_CARGO_DISPATCHER_HELICOPTER object recognize the cargo. +-- -- -- ## 1. AI\_CARGO\_DISPATCHER\_APC constructor -- @@ -88,22 +97,23 @@ AI_CARGO_DISPATCHER_APC = { --- Creates a new AI_CARGO_DISPATCHER_APC object. -- @param #AI_CARGO_DISPATCHER_APC self --- @param Core.Set#SET_GROUP SetAPC The collection of APC @{Wrapper.Group}s. --- @param Core.Set#SET_CARGO SetCargo The collection of @{Cargo} derived objects. --- @param Core.Set#SET_ZONE SetDeployZone The collection of deploy @{Zone}s, which are used to where the cargo will be deployed by the APCs. +-- @param Core.Set#SET_GROUP APCSet The collection of APC @{Wrapper.Group}s. +-- @param Core.Set#SET_CARGO CargoSet The collection of @{Cargo.Cargo} derived objects. +-- @param Core.Set#SET_ZONE PickupZoneSet (optional) The collection of pickup @{Zone}s, which are used to where the cargo can be picked up by the APCs. If nil, then cargo can be picked up everywhere. +-- @param Core.Set#SET_ZONE DeployZoneSet The collection of deploy @{Zone}s, which are used to where the cargo will be deployed by the APCs. -- @param DCS#Distance CombatRadius The cargo will be unloaded from the APC and engage the enemy if the enemy is within CombatRadius range. The radius is in meters, the default value is 500 meters. -- @return #AI_CARGO_DISPATCHER_APC -- @usage -- -- -- Create a new cargo dispatcher for the set of APCs, with a combatradius of 500. --- SetAPC = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() --- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() --- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() --- AICargoDispatcher = AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZone, 500 ) +-- APCSet = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() +-- CargoSet = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- DeployZoneSet = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() +-- AICargoDispatcher = AI_CARGO_DISPATCHER_APC:New( APCSet, SCargoSet, nil, DeployZoneSet, 500 ) -- -function AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZone, CombatRadius ) +function AI_CARGO_DISPATCHER_APC:New( APCSet, CargoSet, PickupZoneSet, DeployZoneSet, CombatRadius ) - local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithZones( SetAPC, SetCargo, SetDeployZone ) ) -- #AI_CARGO_DISPATCHER_APC + local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithZones( APCSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) -- #AI_CARGO_DISPATCHER_APC self.CombatRadius = CombatRadius or 500 @@ -116,7 +126,7 @@ function AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargo, SetDeployZone, CombatRad end -function AI_CARGO_DISPATCHER_APC:AICargo( APC, SetCargo ) +function AI_CARGO_DISPATCHER_APC:AICargo( APC, CargoSet ) - return AI_CARGO_APC:New( APC, SetCargo, self.CombatRadius ) + return AI_CARGO_APC:New( APC, CargoSet, self.CombatRadius ) end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua index 300fef9ae..1df08d56f 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua @@ -1,5 +1,10 @@ --- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo using Planes. -- +-- **Features:** +-- +-- * The airplanes will fly towards the pickup airbases to pickup the cargo. +-- * The airplanes will fly towards the deploy airbases to deploy the cargo. +-- -- === -- -- ### Author: **FlightControl** @@ -16,9 +21,17 @@ --- Brings a dynamic cargo handling capability for AI groups. -- -- Airplanes can be mobilized to intelligently transport infantry and other cargo within the simulation. --- The AI_CARGO_DISPATCHER_AIRPLANE module uses the @{Cargo} capabilities within the MOOSE framework. --- CARGO derived objects must be declared within the mission to make the AI_CARGO_DISPATCHER_AIRPLANE object recognize the cargo. --- Please consult the @{Cargo} module for more information. +-- +-- The AI_CARGO_DISPATCHER_AIRPLANE module is derived from the AI_CARGO_DISPATCHER module. +-- +-- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_AIRPLANE class, it is recommended that you +-- **first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!!** +-- +-- Especially to learn how to **Tailor the different cargo handling events**, this will be very useful! +-- +-- On top, the AI_CARGO_DISPATCHER_AIRPLANE class uses the @{Cargo.Cargo} capabilities within the MOOSE framework. +-- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class. +-- CARGO derived objects must be declared within the mission to make the AI_CARGO_DISPATCHER_HELICOPTER object recognize the cargo. -- -- -- @@ -29,23 +42,25 @@ AI_CARGO_DISPATCHER_AIRPLANE = { --- Creates a new AI_CARGO_DISPATCHER_AIRPLANE object. -- @param #AI_CARGO_DISPATCHER_AIRPLANE self --- @param Core.Set#SET_GROUP SetAirplanes Set of cargo transport airplanes. --- @param Core.Set#SET_CARGO SetCargos Set of cargo, which is supposed to be transported. --- @param Core.Set#SET_AIRBASE PickupAirbasesSet Set of airbases where the cargo has to be picked up. --- @param Core.Set#SET_AIRBASE DeployAirbasesSet Set of airbases where the cargo is deployed. Choice for each cargo is random. +-- @param Core.Set#SET_GROUP AirplaneSet Set of cargo transport airplanes. +-- @param Core.Set#SET_CARGO CargoSet Set of cargo, which is supposed to be transported. +-- @param Core.Zone#SET_ZONE PickupZoneSet Set of zone airbases where the cargo has to be picked up. +-- @param Core.Zone#SET_ZONE DeployZoneSet Set of zone airbases where the cargo is deployed. Choice for each cargo is random. -- @return #AI_CARGO_DISPATCHER_AIRPLANE self -- @usage -- -- -- Create a new cargo dispatcher --- SetAirplanes = SET_GROUP:New():FilterPrefixes( "Airplane" ):FilterStart() --- SetCargos = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() --- PickupAirbasesSet = SET_AIRBASE:New() --- DeployAirbasesSet = SET_AIRBASE:New() --- AICargoDispatcher = AI_CARGO_DISPATCHER_AIRPLANE:New( SetAirplanes, SetCargos, PickupAirbasesSet, DeployAirbasesSet ) +-- AirplaneSet = SET_GROUP:New():FilterPrefixes( "Airplane" ):FilterStart() +-- CargoSet = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- PickupZoneSet = SET_AIRBASE:New() +-- DeployZoneSet = SET_AIRBASE:New() +-- PickupZoneSet:AddZone( ZONE_AIRBASE:New( "Gudauta", AIRBASE:FindByName( AIRBASE.Caucasus.Gudauta ), 3000 ) ) +-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( "Sochi", AIRBASE:FindByName( AIRBASE.Caucasus.Sochi_Adler ), 3000 ) ) +-- AICargoDispatcher = AI_CARGO_DISPATCHER_AIRPLANE:New( AirplaneSet, CargoSet, PickupZoneSet, DeployZoneSet ) -- -function AI_CARGO_DISPATCHER_AIRPLANE:New( SetAirplanes, SetCargos, PickupAirbasesSet, DeployAirbasesSet ) +function AI_CARGO_DISPATCHER_AIRPLANE:New( AirplaneSet, CargoSet, PickupZoneSet, DeployZoneSet ) - local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithAirbases( SetAirplanes, SetCargos, PickupAirbasesSet, DeployAirbasesSet ) ) -- #AI_CARGO_DISPATCHER_AIRPLANE + local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithZones( AirplaneSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) -- #AI_CARGO_DISPATCHER_AIRPLANE self:SetDeploySpeed( 200, 150 ) self:SetPickupSpeed( 200, 150 ) @@ -55,7 +70,7 @@ function AI_CARGO_DISPATCHER_AIRPLANE:New( SetAirplanes, SetCargos, PickupAirbas return self end -function AI_CARGO_DISPATCHER_AIRPLANE:AICargo( Airplane, SetCargo ) +function AI_CARGO_DISPATCHER_AIRPLANE:AICargo( Airplane, CargoSet ) - return AI_CARGO_AIRPLANE:New( Airplane, SetCargo ) + return AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index 26b5d9aab..69beb31f4 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -1,7 +1,12 @@ ---- **AI** -- Models the intelligent transportation of infantry and other cargo using Helicopters. --- --- The @{#AI_CARGO_DISPATCHER_HELICOPTER} classes implements the dynamic dispatching of cargo transportation tasks for helicopters. +--- **AI** -- (2.4) - Models the intelligent transportation of infantry and other cargo using Helicopters. -- +-- **Features:** +-- +-- * The helicopters will fly towards the pickup locations to pickup the cargo. +-- * The helicopters will fly towards the deploy zones to deploy the cargo. +-- * Precision deployment as well as randomized deployment within the deploy zones are possible. +-- * Helicopters will orbit the deploy zones when there is no space for landing until the deploy zone is free. +-- -- === -- -- ### Author: **FlightControl** @@ -18,9 +23,18 @@ --- A dynamic cargo handling capability for AI helicopter groups. -- -- Helicopters can be mobilized to intelligently transport infantry and other cargo within the simulation. --- The AI\_CARGO\_DISPATCHER\_HELICOPTER module uses the @{Cargo} capabilities within the MOOSE framework. --- CARGO derived objects must be declared within the mission to make the AI\_CARGO\_DISPATCHER\_HELICOPTER object recognize the cargo. --- Please consult the @{Cargo} module for more information. +-- +-- +-- The AI_CARGO_DISPATCHER_HELICOPTER module is derived from the AI_CARGO_DISPATCHER module. +-- +-- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_HELICOPTER class, it is recommended that you +-- **first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!!** +-- +-- Especially to learn how to **Tailor the different cargo handling events**, this will be very useful! +-- +-- On top, the AI_CARGO_DISPATCHER_HELICOPTER class uses the @{Cargo.Cargo} capabilities within the MOOSE framework. +-- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class. +-- CARGO derived objects must be declared within the mission to make the AI_CARGO_DISPATCHER_HELICOPTER object recognize the cargo. -- -- --- -- @@ -88,21 +102,22 @@ AI_CARGO_DISPATCHER_HELICOPTER = { --- Creates a new AI_CARGO_DISPATCHER_HELICOPTER object. -- @param #AI_CARGO_DISPATCHER_HELICOPTER self --- @param Core.Set#SET_GROUP SetHelicopter The collection of Helicopter @{Wrapper.Group}s. --- @param Core.Set#SET_CARGO SetCargo The collection of @{Cargo} derived objects. --- @param Core.Set#SET_ZONE SetDeployZones The collection of deploy @{Zone}s, which are used to where the cargo will be deployed by the Helicopters. +-- @param Core.Set#SET_GROUP HelicopterSet The collection of Helicopter @{Wrapper.Group}s. +-- @param Core.Set#SET_CARGO CargoSet The collection of @{Cargo.Cargo} derived objects. +-- @param Core.Set#SET_ZONE PickupZoneSet (optional) The collection of pickup @{Zone}s, which are used to where the cargo can be picked up by the APCs. If nil, then cargo can be picked up everywhere. +-- @param Core.Set#SET_ZONE DeployZoneSet The collection of deploy @{Zone}s, which are used to where the cargo will be deployed by the Helicopters. -- @return #AI_CARGO_DISPATCHER_HELICOPTER -- @usage -- -- -- Create a new cargo dispatcher --- SetHelicopter = SET_GROUP:New():FilterPrefixes( "Helicopter" ):FilterStart() --- SetCargo = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() --- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() --- AICargoDispatcher = AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargo ) +-- HelicopterSet = SET_GROUP:New():FilterPrefixes( "Helicopter" ):FilterStart() +-- CargoSet = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- DeployZoneSet = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() +-- AICargoDispatcher = AI_CARGO_DISPATCHER_HELICOPTER:New( HelicopterSet, SetCargo, nil, DeployZoneSet ) -- -function AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargo, SetDeployZones ) +function AI_CARGO_DISPATCHER_HELICOPTER:New( HelicopterSet, CargoSet, PickupZoneSet, DeployZoneSet ) - local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithZones( SetHelicopter, SetCargo, SetDeployZones ) ) -- #AI_CARGO_DISPATCHER_HELICOPTER + local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithZones( HelicopterSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) -- #AI_CARGO_DISPATCHER_HELICOPTER self:SetDeploySpeed( 200, 150 ) self:SetPickupSpeed( 200, 150 ) @@ -112,8 +127,8 @@ function AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargo, SetDeployZ return self end -function AI_CARGO_DISPATCHER_HELICOPTER:AICargo( Helicopter, SetCargo ) +function AI_CARGO_DISPATCHER_HELICOPTER:AICargo( Helicopter, CargoSet ) - return AI_CARGO_HELICOPTER:New( Helicopter, SetCargo ) + return AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) end diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 46cf934a5..11d32053b 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -1,4 +1,4 @@ ---- **AI** -- (R2.3) - Models the intelligent transportation of infantry (cargo). +--- **AI** -- (R2.4) - Models the intelligent transportation of infantry (cargo). -- -- === -- @@ -13,7 +13,34 @@ -- @extends Core.Fsm#FSM_CONTROLLABLE ---- # AI\_CARGO\_TROOPS class, extends @{Core.Fsm#FSM_CONTROLLABLE} +--- Brings a dynamic cargo handling capability for an AI helicopter group. +-- +-- Helicopter carriers can be mobilized to intelligently transport infantry and other cargo within the simulation. +-- +-- The AI_CARGO_HELICOPTER class uses the @{Cargo.Cargo} capabilities within the MOOSE framework. +-- @{Cargo.Cargo} must be declared within the mission to make the AI_CARGO_HELICOPTER object recognize the cargo. +-- Please consult the @{Cargo.Cargo} module for more information. +-- +-- ## Cargo pickup. +-- +-- Using the @{#AI_CARGO_HELICOPTER.Pickup}() method, you are able to direct the helicopters towards a point on the battlefield to board/load the cargo at the specific coordinate. +-- Ensure that the landing zone is horizontally flat, and that trees cannot be found in the landing vicinity, or the helicopters won't land or will even crash! +-- +-- ## Cargo deployment. +-- +-- Using the @{#AI_CARGO_HELICOPTER.Deploy}() method, you are able to direct the helicopters towards a point on the battlefield to unboard/unload the cargo at the specific coordinate. +-- Ensure that the landing zone is horizontally flat, and that trees cannot be found in the landing vicinity, or the helicopters won't land or will even crash! +-- +-- ## Infantry health. +-- +-- When infantry is unboarded from the APCs, the infantry is actually respawned into the battlefield. +-- As a result, the unboarding infantry is very _healthy_ every time it unboards. +-- This is due to the limitation of the DCS simulator, which is not able to specify the health of new spawned units as a parameter. +-- However, infantry that was destroyed when unboarded, won't be respawned again. Destroyed is destroyed. +-- As a result, there is some additional strength that is gained when an unboarding action happens, but in terms of simulation balance this has +-- marginal impact on the overall battlefield simulation. Fortunately, the firing strength of infantry is limited, and thus, respacing healthy infantry every +-- time is not so much of an issue ... +-- -- -- === -- @@ -21,7 +48,6 @@ AI_CARGO_HELICOPTER = { ClassName = "AI_CARGO_HELICOPTER", Coordinate = nil, -- Core.Point#COORDINATE, - Helicopter_Cargo = {}, } AI_CARGO_QUEUE = {} @@ -33,10 +59,8 @@ AI_CARGO_QUEUE = {} -- @return #AI_CARGO_HELICOPTER function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) - local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- #AI_CARGO_HELICOPTER + local self = BASE:Inherit( self, AI_CARGO:New( Helicopter, CargoSet ) ) -- #AI_CARGO_HELICOPTER - self.CargoSet = CargoSet -- Cargo.CargoGroup#CARGO_GROUP - self.Zone = ZONE_GROUP:New( Helicopter:GetName(), Helicopter, 300 ) self:SetStartState( "Unloaded" ) @@ -46,17 +70,17 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) self:AddTransition( { "Unloaded", "Loading" }, "Load", "Boarding" ) self:AddTransition( "Boarding", "Board", "Boarding" ) - self:AddTransition( "Boarding", "Loaded", "Loaded" ) + self:AddTransition( "Boarding", "Loaded", "Boarding" ) + self:AddTransition( "Boarding", "PickedUp", "Loaded" ) self:AddTransition( "Loaded", "Unload", "Unboarding" ) self:AddTransition( "Unboarding", "Unboard", "Unboarding" ) - self:AddTransition( "Unboarding", "Unloaded", "Unloaded" ) + self:AddTransition( "Unboarding", "Unloaded", "Unboarding" ) + self:AddTransition( "Unboarding", "Deployed", "Unloaded" ) self:AddTransition( "*", "Landed", "*" ) self:AddTransition( "*", "Queue", "*" ) self:AddTransition( "*", "Orbit" , "*" ) - self:AddTransition( "*", "Home" , "*" ) - self:AddTransition( "*", "RTB" , "*" ) - self:AddTransition( "*", "BackHome" , "*" ) + self:AddTransition( "*", "Home" , "*" ) self:AddTransition( "*", "Destroyed", "Destroyed" ) @@ -235,18 +259,15 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To ) if self.RoutePickup == true then if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 10 then --self:Load( Helicopter:GetPointVec2() ) - self:Load() + self:Load( self.PickupZone ) self.RoutePickup = false self.Relocating = true end end - if self.RouteDeploy == true then - local height=Helicopter:GetHeight( true ) - local velocity=Helicopter:GetVelocityKMH() - env.info(string.format("FF helo in air %s, height = %d m, velocity = %d km/h", tostring(Helicopter:InAir()), height, velocity)) - if height <= 10 and velocity < 10 then - self:Unload( true ) + if self.RouteDeploy == true then + if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 10 then + self:Unload( self.DeployZone ) self.RouteDeploy = false self.Transporting = false self.Relocating = false @@ -264,7 +285,7 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate -- @param #number Speed -function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordinate, Speed ) +function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordinate, Speed, DeployZone ) local HelicopterInZone = false @@ -273,7 +294,7 @@ function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordina local Distance = Coordinate:DistanceFromPointVec2( Helicopter:GetCoordinate() ) if Distance > 2000 then - self:__Queue( -10, Coordinate ) + self:__Queue( -10, Coordinate, Speed, DeployZone ) else local ZoneFree = true @@ -322,8 +343,12 @@ function AI_CARGO_HELICOPTER:onafterQueue( Helicopter, From, Event, To, Coordina -- Now route the helicopter Helicopter:Route( Route, 0 ) + + -- Keep the DeployZone, because when the helo has landed, we want to provide the DeployZone to the mission designer as part of the Unloaded event. + self.DeployZone = DeployZone + else - self:__Queue( -10, Coordinate ) + self:__Queue( -10, Coordinate, Speed, DeployZone ) end end else @@ -378,127 +403,8 @@ function AI_CARGO_HELICOPTER:onafterOrbit( Helicopter, From, Event, To, Coordina end ---- On Before event Load. --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Group#GROUP Helicopter --- @param #string From From state. --- @param #string Event Event --- @param #string To To state. -function AI_CARGO_HELICOPTER:onbeforeLoad( Helicopter, From, Event, To) - local Boarding = false - - if Helicopter and Helicopter:IsAlive() then - - self.BoardingCount = 0 - - if Helicopter and Helicopter:IsAlive() then - for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do - local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT - local CargoBayFreeWeight = HelicopterUnit:GetCargoBayFreeWeight() - self:F({CargoBayFreeWeight=CargoBayFreeWeight}) - - for _, Cargo in pairs( self.CargoSet:GetSet() ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - self:F( { IsUnLoaded = Cargo:IsUnLoaded() } ) - if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then - if Cargo:IsInLoadRadius( HelicopterUnit:GetCoordinate() ) then - self:F( { "In radius", HelicopterUnit:GetName() } ) - - local CargoWeight = Cargo:GetWeight() - - -- Only when there is space within the bay to load the next cargo item! - if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then - - --Cargo:Ungroup() - Cargo:Board( HelicopterUnit, 25 ) - self:__Board( 1, Cargo, HelicopterUnit ) - self.Helicopter_Cargo[HelicopterUnit] = Cargo - Boarding = true - break - end - end - end - end - end - end - end - - return Boarding - -end - ---- On after Board event. --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Group#GROUP Helicopter --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Cargo.Cargo#CARGO Cargo Cargo object. --- @param Wrapper.Unit#UNIT HelicopterUnit -function AI_CARGO_HELICOPTER:onafterBoard( Helicopter, From, Event, To, Cargo, HelicopterUnit ) - self:F( { Helicopter, From, Event, To, Cargo, HelicopterUnit } ) - - if Helicopter and Helicopter:IsAlive() then - self:F({ IsLoaded = Cargo:IsLoaded() } ) - if not Cargo:IsLoaded() then - self:__Board( 10, Cargo, HelicopterUnit ) - else - local CargoBayFreeWeight = HelicopterUnit:GetCargoBayFreeWeight() - self:F({CargoBayFreeWeight=CargoBayFreeWeight}) - for _, Cargo in pairs( self.CargoSet:GetSet() ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - if Cargo:IsUnLoaded() then - if Cargo:IsInLoadRadius( HelicopterUnit:GetCoordinate() ) then - local CargoWeight = Cargo:GetWeight() - - -- Only when there is space within the bay to load the next cargo item! - if CargoBayFreeWeight > CargoWeight then --and CargoBayFreeVolume > CargoVolume then - Cargo:Board( HelicopterUnit, 25 ) - self:__Board( 10, Cargo, HelicopterUnit ) - self.Helicopter_Cargo[HelicopterUnit] = Cargo - return - end - end - end - end - self:__Loaded( 1, Cargo ) -- Will only be executed when no more cargo is boarded. - end - end - -end - - ---- On before Loaded event. --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Group#GROUP Helicopter --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @return #boolean Cargo loaded. -function AI_CARGO_HELICOPTER:onbeforeLoaded( Helicopter, From, Event, To, Cargo ) - self:F( { Helicopter, From, Event, To } ) - - local Loaded = true - - if Helicopter and Helicopter:IsAlive() then - for HelicopterUnit, Cargo in pairs( self.Helicopter_Cargo ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - self:F( { IsLoaded = Cargo:IsLoaded(), IsDestroyed = Cargo:IsDestroyed(), Cargo:GetName(), Helicopter:GetName() } ) - if not Cargo:IsLoaded() and not Cargo:IsDestroyed() then - Loaded = false - end - end - end - - return Loaded - -end - - - - ---- On after Loaded event. Check if cargo is loaded. +--- On after PickedUp event, raised when all cargo has been loaded into the CarrierGroup. -- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -- @param #string From From state. @@ -506,8 +412,9 @@ end -- @param #string To To state. -- @param Cargo.Cargo#CARGO Cargo Cargo object. -- @return #boolean Cargo is loaded. -function AI_CARGO_HELICOPTER:onafterLoaded( Helicopter, From, Event, To, Cargo ) - self:F( { Helicopter, From, Event, To, Cargo } ) +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO_HELICOPTER:onafterPickedUp( Helicopter, From, Event, To, PickupZone ) + self:F( { Helicopter, From, Event, To } ) if Helicopter and Helicopter:IsAlive() then self.Transporting = true @@ -515,62 +422,8 @@ function AI_CARGO_HELICOPTER:onafterLoaded( Helicopter, From, Event, To, Cargo ) end ---- On after Unload event. --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Group#GROUP Helicopter --- @param #string From From state. --- @param #string Event Event. --- @param #boolean Deployed Cargo is deployed. -function AI_CARGO_HELICOPTER:onafterUnload( Helicopter, From, Event, To, Deployed ) - if Helicopter and Helicopter:IsAlive() then - for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do - local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT - for _, Cargo in pairs( HelicopterUnit:GetCargo() ) do - if Cargo:IsLoaded() then - Cargo:UnBoard() - Cargo:SetDeployed( true ) - self:__Unboard( 10, Cargo, Deployed ) - end - end - end - end - - -end - ---- On after Unboard event. --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Group#GROUP Helicopter --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Cargo.Cargo#CARGO Cargo Cargo object. --- @param #boolean Deployed Cargo is deployed. -function AI_CARGO_HELICOPTER:onafterUnboard( Helicopter, From, Event, To, Cargo, Deployed ) - - if Helicopter and Helicopter:IsAlive() then - if not Cargo:IsUnLoaded() then - self:__Unboard( 10, Cargo, Deployed ) - else - for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do - local HelicopterUnit = HelicopterUnit -- Wrapper.Unit#UNIT - for _, Cargo in pairs( HelicopterUnit:GetCargo() ) do - if Cargo:IsLoaded() then - Cargo:UnBoard() - Cargo:SetDeployed( true ) - self:__Unboard( 10, Cargo, Deployed ) - return - end - end - end - self:__Unloaded( 1, Cargo, Deployed ) - end - end - -end - ---- On before Unloaded event. +--- On after Deployed event. -- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter -- @param #string From From state. @@ -579,45 +432,8 @@ end -- @param Cargo.Cargo#CARGO Cargo Cargo object. -- @param #boolean Deployed Cargo is deployed. -- @return #boolean True if all cargo has been unloaded. -function AI_CARGO_HELICOPTER:onbeforeUnloaded( Helicopter, From, Event, To, Cargo, Deployed ) - self:F( { APC, From, Event, To, Cargo:GetName(), Deployed = Deployed } ) - - local AllUnloaded = true - - --Cargo:Regroup() - - if Helicopter and Helicopter:IsAlive() then - for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do - local IsEmpty = HelicopterUnit:IsCargoEmpty() - self:I({ IsEmpty = IsEmpty }) - if not IsEmpty then - AllUnloaded = false - break - end - end - - if AllUnloaded == true then - if Deployed == true then - self.Helicopter_Cargo = {} - end - self.Helicopter = Helicopter - end - end - - self:F( { AllUnloaded = AllUnloaded } ) - return AllUnloaded - -end - ---- On after Unloaded event. --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Group#GROUP Helicopter --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Cargo.Cargo#CARGO Cargo Cargo object. --- @param #boolean Deployed Cargo is deployed. -function AI_CARGO_HELICOPTER:onafterUnloaded( Helicopter, From, Event, To, Cargo, Deployed ) +function AI_CARGO_HELICOPTER:onafterDeployed( Helicopter, From, Event, To, DeployZone ) + self:F( { Helicopter, From, Event, To, DeployZone = DeployZone } ) self:Orbit( Helicopter:GetCoordinate(), 50 ) @@ -627,7 +443,7 @@ function AI_CARGO_HELICOPTER:onafterUnloaded( Helicopter, From, Event, To, Cargo AI_CARGO_QUEUE[Helicopter] = nil end, Helicopter ) - + end --- On after Pickup event. @@ -638,15 +454,12 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate Pickup place. -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. -function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordinate, Speed ) +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordinate, Speed, PickupZone ) if Helicopter and Helicopter:IsAlive() ~= nil then - --Helicopter:Activate() - - env.info("FF route pickup") - - Coordinate:MarkToAll("helo pickupcoord") + Helicopter:Activate() self.RoutePickup = true Coordinate.y = math.random( 50, 500 ) @@ -692,7 +505,8 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin -- Now route the helicopter Helicopter:Route( Route, 1 ) - + + self.PickupZone = PickupZone self.Transporting = true end @@ -702,8 +516,8 @@ end -- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP AICargoHelicopter -- @param Core.Point#COORDINATE Coordinate Coordinate -function AI_CARGO_HELICOPTER:_Deploy( AICargoHelicopter, Coordinate ) - AICargoHelicopter:__Queue( -10, Coordinate, 100 ) +function AI_CARGO_HELICOPTER:_Deploy( AICargoHelicopter, Coordinate, DeployZone ) + AICargoHelicopter:__Queue( -10, Coordinate, 100, DeployZone ) end --- On after Deploy event. @@ -714,7 +528,7 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate Place at which the cargo is deployed. -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. -function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordinate, Speed ) +function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordinate, Speed, DeployZone ) if Helicopter and Helicopter:IsAlive() ~= nil then @@ -759,7 +573,7 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin local Tasks = {} - Tasks[#Tasks+1] = Helicopter:TaskFunction( "AI_CARGO_HELICOPTER._Deploy", self, Coordinate ) + Tasks[#Tasks+1] = Helicopter:TaskFunction( "AI_CARGO_HELICOPTER._Deploy", self, Coordinate, DeployZone ) Tasks[#Tasks+1] = Helicopter:TaskOrbitCircle( math.random( 30, 100 ), _speed, CoordinateTo:GetRandomCoordinateInRadius( 800, 500 ) ) --Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() ) @@ -782,8 +596,9 @@ end -- @param Event -- @param To -- @param Core.Point#COORDINATE Coordinate Home place. --- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 80% of max possible speed the unit can go. -function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinate, Speed ) +-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. +-- @param Core.Zone#ZONE HomeZone The zone wherein the carrier will return when all cargo has been transported. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinate, Speed, HomeZone ) if Helicopter and Helicopter:IsAlive() ~= nil then @@ -793,9 +608,9 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat --- Calculate the target route point. - Coordinate.y = math.random( 100, 500 ) + Coordinate.y = math.random( 50, 200 ) - local _speed=Speed or Helicopter:GetSpeedMax()*0.8 + Speed = Speed or Helicopter:GetSpeedMax()*0.5 --- Create a route point of type air. local CoordinateFrom = Helicopter:GetCoordinate() @@ -803,7 +618,7 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, - _speed, + Speed , true ) Route[#Route+1] = WaypointFrom @@ -814,7 +629,7 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat "RADIO", POINT_VEC3.RoutePointType.TurningPoint, POINT_VEC3.RoutePointAction.TurningPoint, - _speed, + Speed , true ) @@ -823,10 +638,11 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat --- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable... Helicopter:WayPointInitialize( Route ) - local Tasks = {} + local Tasks = {} + Tasks[#Tasks+1] = Helicopter:TaskLandAtVec2( CoordinateTo:GetVec2() ) Route[#Route].task = Helicopter:TaskCombo( Tasks ) - + Route[#Route+1] = WaypointTo -- Now route the helicopter @@ -836,105 +652,3 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat end - ---- On after RTB event. Route the helicopter from one airport or it's current position to another airbase. --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Group#GROUP Helicopter Cargo helicopter. --- @param From --- @param Event --- @param To --- @param Wrapper.Airbase#AIRBASE Airbase Destination airbase. --- @param #number Speed Speed in km/h. Default is 80% of max possible speed the group can do. -function AI_CARGO_HELICOPTER:onafterRTB( Helicopter, From, Event, To, Airbase, Speed) - - if Helicopter and Helicopter:IsAlive() then - - -- Set takeoff type. - local Takeoff = SPAWN.Takeoff.Hot - - -- Get template of group. - local Template = Helicopter:GetTemplate() - - -- Nil check - if Template==nil then - return - end - - -- Waypoints of the route. - local Points={} - - -- To point. - local AirbasePointVec2 = Airbase:GetPointVec2() - local ToWaypoint = AirbasePointVec2:WaypointAir( - POINT_VEC3.RoutePointAltType.BARO, - "Land", - "Landing", - Speed or Helicopter:GetSpeedMax()*0.8 - ) - ToWaypoint["airdromeId"] = Airbase:GetID() - ToWaypoint["speed_locked"] = true - - -- Task function triggering the arrived event. - local TaskFunction = Helicopter:TaskFunction("AI_CARGO_HELICOPTER._BackHome", self) - - -- Put task function on last waypoint. - Helicopter:SetTaskWaypoint( ToWaypoint, TaskFunction ) - - - -- If self.Airbase~=nil then group is currently at an airbase, where it should be respawned. - if self.Airbase then - - -- Second point of the route. First point is done in RespawnAtCurrentAirbase() routine. - Template.route.points[2] = ToWaypoint - - -- Respawn group at the current airbase. - Helicopter:RespawnAtCurrentAirbase(Template, Takeoff, false) - - else - - -- From point. - local GroupPoint = Helicopter:GetVec2() - local FromWaypoint = {} - FromWaypoint.x = GroupPoint.x - FromWaypoint.y = GroupPoint.y - FromWaypoint.type = "Turning Point" - FromWaypoint.action = "Turning Point" - FromWaypoint.speed = Helicopter:GetSpeedMax()*0.8 - - -- The two route points. - Points[1] = FromWaypoint - Points[2] = ToWaypoint - - local PointVec3 = Helicopter:GetPointVec3() - Template.x = PointVec3.x - Template.y = PointVec3.z - - Template.route.points = Points - - local GroupSpawned = Helicopter:Respawn(Template) - - end - end -end - ---- Function called when transport is back home and nothing more to do. Triggering the event BackHome. --- @param Wrapper.Group#GROUP Helicopter Cargo helicopter. --- @param #AI_CARGO_HELICOPTER self -function AI_CARGO_HELICOPTER._BackHome(Group, self) - env.info("FF ai cargo helicopter back home task function") - Group:SmokeRed() - --Trigger BackHome event. - self:__BackHome(1) -end - - ---- On after BackHome event. --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Group#GROUP Helicopter Cargo helo. --- @param From --- @param Event --- @param To -function AI_CARGO_HELICOPTER:onafterBackHome( Helicopter, From, Event, To ) - env.info("FF ai cargo helicopter back home event") - Helicopter:SmokeRed() -end \ No newline at end of file From 718679b5dd9ddc1468bf200ef8c88edbe39ad4ac Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Fri, 7 Sep 2018 16:41:23 +0200 Subject: [PATCH 327/420] Warehouse v0.3.7w --- .../Moose/AI/AI_Cargo_Dispatcher.lua | 4 +- .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 6 +- .../Moose/Functional/Warehouse.lua | 323 +++++++++++++----- 3 files changed, 240 insertions(+), 93 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index eb448955f..3f1cb6a9f 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -471,9 +471,9 @@ end -- DeployZoneSet = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() -- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZone ) -- -function AI_CARGO_DISPATCHER:NewWithZones( SetCarriers, SetCargos, PickupZoneSet, DeployZoneSet ) +function AI_CARGO_DISPATCHER:NewWithZones( SetCarrier, SetCargo, PickupZoneSet, DeployZoneSet ) - local self = AI_CARGO_DISPATCHER:New( SetCarriers, SetCargos ) -- #AI_CARGO_DISPATCHER + local self = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo ) -- #AI_CARGO_DISPATCHER self.PickupZoneSet = PickupZoneSet self.DeployZoneSet = DeployZoneSet diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index fffaaf90f..ef4708bda 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -109,7 +109,7 @@ AI_CARGO_DISPATCHER_APC = { -- APCSet = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() -- CargoSet = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() -- DeployZoneSet = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() --- AICargoDispatcher = AI_CARGO_DISPATCHER_APC:New( APCSet, SCargoSet, nil, DeployZoneSet, 500 ) +-- AICargoDispatcher = AI_CARGO_DISPATCHER_APC:New( APCSet, CargoSet, nil, DeployZoneSet, 500 ) -- function AI_CARGO_DISPATCHER_APC:New( APCSet, CargoSet, PickupZoneSet, DeployZoneSet, CombatRadius ) @@ -117,8 +117,8 @@ function AI_CARGO_DISPATCHER_APC:New( APCSet, CargoSet, PickupZoneSet, DeployZon self.CombatRadius = CombatRadius or 500 - self:SetDeploySpeed( 70, 120 ) - self:SetPickupSpeed( 70, 120 ) + self:SetDeploySpeed( 120, 70 ) + self:SetPickupSpeed( 120, 70 ) self:SetPickupRadius( 0, 0 ) self:SetDeployRadius( 0, 0 ) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 827033e28..bf0d52a17 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -49,7 +49,8 @@ -- @field #table delivered Table holding all delivered requests. Table elements are #boolean. If true, all cargo has been delivered. -- @field #table defending Table holding all defending requests, i.e. self requests that were if the warehouse is under attack. Table elements are of type @{#WAREHOUSE.Pendingitem}. -- @field Core.Zone#ZONE portzone Zone defining the port of a warehouse. This is where naval assets are spawned. --- @field #table shippinglanes Table holding the user defined shipping between warehouses. +-- @field #table shippinglanes Table holding the user defined shipping between warehouses. +-- @field #table offroadpaths Table holding user defined paths from one warehouse to another. -- @field #boolean autodefence When the warehouse is under attack, automatically spawn assets to defend the warehouse. -- @extends Core.Fsm#FSM @@ -257,6 +258,18 @@ -- -- The user can set the road connection manually with the @{#WAREHOUSE.SetRoadConnection} function. -- +-- ## Off Road Connections +-- +-- For ground troops it is also possible to define off road paths from between warehouses if no proper road connection is available or should not be used. +-- +-- An off road path can be defined via the @{#WAREHOUSE.AddOffRoadPath}(*remotewarehouse*, *group*, *oneway*) function, where +-- *remotewarehouse* is the warehouse to which the path leads. +-- The parameter *group* is a late activated template group. The waypoints of this group are used to define the path between the two warehouses. +-- By default, the reverse paths is automatically added to get *from* the remote warehouse to this warehouse unless the parameter *oneway* is set to true. +-- +-- **Note** that if an off road connection is defined between two warehouses this becomes the default path, i.e. even if there is a path *on road* possible +-- this will not be used. +-- -- ## Rail Connections -- -- A rail connection is automatically defined as the closest point on a railway measured from the center of the spawn zone. But only, if the distance is less than 3 km. @@ -267,7 +280,7 @@ -- -- ## Air Connections -- --- In order to use airborne assets, a warehouse needs to have an associated airbase. This can be an airdrome or a FARP/HELOPAD. +-- In order to use airborne assets, a warehouse needs to have an associated airbase. This can be an airdrome, a FARP/HELOPAD or a ship. -- -- If there is an airbase within 3 km range of the warehouse it is automatically set as the associated airbase. A user can set an airbase manually -- with the @{#WAREHOUSE.SetAirbase} function. Keep in mind, that sometimes, ground units need to walk/drive from the spawn zone to the airport @@ -287,11 +300,13 @@ -- -- ### Defining Shipping Lanes -- --- A shipping lane between to warehouses can be defined by the @{#WAREHOUSE.AddShippingLane}(*remotewarehouse*, *group*) function. The first parameter *remotewarehouse* +-- A shipping lane between to warehouses can be defined by the @{#WAREHOUSE.AddShippingLane}(*remotewarehouse*, *group*, *oneway*) function. The first parameter *remotewarehouse* -- is the warehouse which should be connected to the present warehouse. -- -- The parameter *group* should be a late activated group defined in the mission editor. The waypoints of this group are used as waypoints of the shipping lane. -- +-- By default, the reverse lane is automatically added to the remote warehouse. This can be disabled by setting the *oneway* parameter to *false*. +-- -- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_ShippingLane.png) -- -- === @@ -624,12 +639,12 @@ -- warehouse.Batumi:AddAsset("Huey", 5, WAREHOUSE.Attribute.AIR_TRANSPORTHELO) -- warehouse.Berlin:AddAsset("Huey", 5, WAREHOUSE.Attribute.AIR_TRANSPORTHELO) -- --- -- Big explosion at the warehose. It has a very nice damage model by the way :) +-- -- Big explosion at the warehouse. It has a very nice damage model by the way :) -- local function DestroyWarehouse() -- warehouse.Batumi.warehouse:GetCoordinate():Explosion(9999) -- end -- --- -- Create and explosion after 30 sec. +-- -- Create an explosion at the warehouse after 30 sec. -- SCHEDULER:New(nil, DestroyWarehouse, {}, 30) -- -- -- These requests should not be processed any more since the warehouse is destroyed. @@ -788,6 +803,7 @@ WAREHOUSE = { defending = {}, portzone = nil, shippinglanes = {}, + offroadpaths = {}, autodefence = false, } @@ -931,7 +947,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.3.7" +WAREHOUSE.version="0.3.7w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -941,6 +957,7 @@ WAREHOUSE.version="0.3.7" -- TODO: Add transport units from dispatchers back to warehouse stock once they completed their mission. -- TODO: Added habours as interface for transport to from warehouses? -- TODO: Set ROE for spawned groups. +-- TODO: Add offroad lanes between warehouses if road connection is not available. -- DONE: Add possibility to add active groups. Need to create a pseudo template before destroy. <== Does not seem to be necessary any more. -- TODO: Write documentation. -- TODO: Handle the case when units of a group die during the transfer. Adjust template?! See Grouping in SPAWN. @@ -1037,7 +1054,7 @@ function WAREHOUSE:New(warehouse, alias) self:AddTransition("*", "AddRequest", "*") -- New request from other warehouse. self:AddTransition("Running", "Request", "*") -- Process a request. Only in running mode. self:AddTransition("Attacked", "Request", "*") -- Process a request. Only in running mode. - self:AddTransition("*", "Unloaded", "*") -- Cargo has been unloaded from the carrier. + self:AddTransition("*", "Unloaded", "*") -- Cargo has been unloaded from the carrier (unused ==> unnecessary?). self:AddTransition("*", "Arrived", "*") -- Cargo or transport group has arrived. self:AddTransition("*", "Delivered", "*") -- All cargo groups of a request have been delivered to the requesting warehouse. self:AddTransition("Running", "SelfRequest", "*") -- Request to warehouse itself. Requested assets are only spawned but not delivered anywhere. @@ -1051,7 +1068,7 @@ function WAREHOUSE:New(warehouse, alias) self:AddTransition("Attacked", "Captured", "Running") -- DONE Warehouse was captured by another coalition. It must have been attacked first. self:AddTransition("*", "AirbaseCaptured", "*") -- DONE Airbase was captured by other coalition. self:AddTransition("*", "AirbaseRecaptured", "*") -- DONE Airbase was re-captured from other coalition. - self:AddTransition("*", "Destroyed", "*") -- DONE Warehouse was destoryed. All assets in stock are gone and warehouse is stopped. + self:AddTransition("*", "Destroyed", "*") -- DONE Warehouse was destroyed. All assets in stock are gone and warehouse is stopped. ------------------------ --- Pseudo Functions --- @@ -1158,8 +1175,11 @@ function WAREHOUSE:New(warehouse, alias) -- @param #WAREHOUSE.Queueitem Request Information table of the request. - --- Triggers the FSM event "Arrived", i.e. when a group has arrived at the destination warehosue. - -- This function should always be called from the receiving and not the sending warehouse because assets are added back to the + --- Triggers the FSM event "Arrived", i.e. when a group has arrived at the destination warehouse. + -- This function should always be called from the sending and not the receiving warehouse. + -- If the group is a cargo asset, it is added to the receiving warehouse. If the group is a transporter it + -- is added to the sending warehouse since carriers are supposed to return to their home warehouse once + -- all cargo was delivered. -- @function [parent=#WAREHOUSE] Arrived -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group Group that has arrived. @@ -1170,7 +1190,7 @@ function WAREHOUSE:New(warehouse, alias) -- @param #number delay Delay in seconds. -- @param Wrapper.Group#GROUP group Group that has arrived. - --- On after "Arrived" event user function. Called when a groups has arrived. + --- On after "Arrived" event user function. Called when a groups has arrived at its destination. -- @function [parent=#WAREHOUSE] OnAfterArrived -- @param #WAREHOUSE self -- @param #string From From state. @@ -1179,7 +1199,7 @@ function WAREHOUSE:New(warehouse, alias) -- @param Wrapper.Group#GROUP group Group that has arrived. - --- Triggers the FSM event "Delivered". A group has been delivered from the warehouse to another warehouse. + --- Triggers the FSM event "Delivered". All (cargo) assets of a request have been delivered to the receiving warehouse. -- @function [parent=#WAREHOUSE] Delivered -- @param #WAREHOUSE self -- @param #WAREHOUSE.Pendingitem request Pending request that was now delivered. @@ -1485,22 +1505,94 @@ function WAREHOUSE:SetPortZone(zone) return self end ---- Add a shipping lane to another warehouse. --- Note that both warehouses must have a port zone defined before a shipping lane can be added. +--- Add a shipping lane from this warehouse to another remote warehouse. +-- Note that both warehouses must have a port zone defined before a shipping lane can be added! -- Shipping lane is taken from the waypoints of a (late activated) template group. So set up a group, e.g. a ship or a helicopter, and place its -- waypoints along the shipping lane you want to add. -- @param #WAREHOUSE self -- @param #WAREHOUSE remotewarehouse The remote warehouse to where the shipping lane is added -- @param Wrapper.Group#GROUP group Waypoints of this group will define the shipping lane between to warehouses. +-- @param #boolean oneway (Optional) If true, the lane can only be used from this warehouse to the other but not other way around. Default false. -- @return #WAREHOUSE self -function WAREHOUSE:AddShippingLane(remotewarehouse, group) +function WAREHOUSE:AddShippingLane(remotewarehouse, group, oneway) -- Check that port zones are defined. if self.portzone==nil or remotewarehouse.portzone==nil then - self:E(self.wid..string.format("ERROR: Sending or receiving warehouse does not have a port zone defined. Adding shipping lane not possible!")) - return + local text=string.format("ERROR: Sending or receiving warehouse does not have a port zone defined. Adding shipping lane not possible!") + self:_ErrorMessage(text, 5) + return self end + local startcoord=self.portzone:GetRandomCoordinate() + local finalcoord=remotewarehouse.portzone:GetRandomCoordinate() + + local lane=self:_NewLane(group,startcoord,finalcoord) + + -- Debug info. Marks along shipping lane. + if self.Debug then + for i=1,#lane do + local coord=lane[i] --Core.Point#COORDINATE + local text=string.format("Shipping lane %s to %s. Point %d.", self.alias, remotewarehouse.alias, i) + coord:MarkToCoalition(text, self.coalition) + end + end + + -- Add shipping lane. + -- TODO: Maybe add multiple lanes as a table and later randomly select one for the actual route. + self.shippinglanes[remotewarehouse.warehouse:GetName()]=lane + + -- Add shipping lane in the opposite direction. + if not oneway then + remotewarehouse:AddShippingLane(self, group, false) + end + + return self +end + + +--- Add an off-road path from this warehouse to another and back. +-- The start and end points are automatically set to one random point in the respective spawn zones of the two warehouses. +-- By default, the reverse path is also added as path from the remote warehouse to this warehouse. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE remotewarehouse The remote warehouse to which the path leads. +-- @param Wrapper.Group#GROUP group Waypoints of this group will define the path between to warehouses. +-- @param #boolean oneway (Optional) If true, the path can only be used from this warehouse to the other but not other way around. Default false. +-- @return #WAREHOUSE self +function WAREHOUSE:AddOffRoadPath(remotewarehouse, group, oneway) + + local startcoord=self.spawnzone:GetRandomCoordinate() + local finalcoord=remotewarehouse.spawnzone:GetRandomCoordinate() + + local lane=self:_NewLane(group,startcoord,finalcoord) + + -- Debug info. Marks along shipping lane. + if self.Debug then + for i=1,#lane do + local coord=lane[i] --Core.Point#COORDINATE + local text=string.format("Off road path from %s to %s. Point %d.", self.alias, remotewarehouse.alias, i) + coord:MarkToCoalition(text, self.coalition) + end + end + + -- Add shipping lane. + self.offroadpaths[remotewarehouse.warehouse:GetName()]=lane + + -- Add shipping lane in the opposite direction. + if not oneway then + remotewarehouse:AddOffRoadPath(self, group, false) + end + + return self +end + +--- Create a new path from a template group. +-- @param #WAREHOUSE self +-- @param Wrapper.Group#GROUP group Group used for extracting the waypoints. +-- @param Core.Point#COORDINATE startcoord First coordinate. +-- @param Core.Point#COORDINATE finalcoord Final coordinate. +-- @return #table Table with route points. +function WAREHOUSE:_NewLane(group, startcoord, finalcoord) + -- Get route from template. local lanepoints=group:GetTemplateRoutePoints() @@ -1513,8 +1605,8 @@ function WAREHOUSE:AddShippingLane(remotewarehouse, group) local coordL=COORDINATE:New(laneL.x, 0, laneL.y) -- Figure out which point is closer to the port of this warehouse. - local distF=self.portzone:GetCoordinate():Get2DDistance(coordF) - local distL=self.portzone:GetCoordinate():Get2DDistance(coordL) + local distF=startcoord:Get2DDistance(coordF) + local distL=startcoord:GetCoordinate():Get2DDistance(coordL) -- Add the shipping lane. Need to take care of the wrong "direction". local lane={} @@ -1532,21 +1624,14 @@ function WAREHOUSE:AddShippingLane(remotewarehouse, group) end end - -- Debug info. Marks along shipping lane. - if self.Debug then - for i=1,#lane do - local coord=lane[i] --Core.Point#COORDINATE - local text=string.format("Shipping lane %s to %s. Point %d.", self.alias, remotewarehouse.alias, i) - coord:MarkToCoalition(text, self.coalition) - end - end - - -- Add shipping lane. - self.shippinglanes[remotewarehouse.warehouse:GetName()]=lane + -- Add beginning and end. + table.insert(lane, 1, startcoord) + table.insert(lane, #lane, finalcoord) - return self + return lane end + --- Check if the warehouse is running. -- @param #WAREHOUSE self -- @return #boolean If true, the warehouse is running and requests are processed. @@ -1577,7 +1662,7 @@ end --- Check if the warehouse has a road connection to another warehouse. Both warehouses need to be started! -- @param #WAREHOUSE self --- @param #WAREHOUSE warehouse The remote warehose to where the connection is checked. +-- @param #WAREHOUSE warehouse The remote warehouse to where the connection is checked. -- @param #boolean markpath If true, place markers of path segments on the F10 map. -- @param #boolean smokepath If true, put green smoke on path segments. -- @return #boolean If true, the two warehouses are connected by road. @@ -1597,7 +1682,7 @@ end --- Check if the warehouse has a railroad connection to another warehouse. Both warehouses need to be started! -- @param #WAREHOUSE self --- @param #WAREHOUSE warehouse The remote warehose to where the connection is checked. +-- @param #WAREHOUSE warehouse The remote warehouse to where the connection is checked. -- @param #boolean markpath If true, place markers of path segments on the F10 map. -- @param #boolean smokepath If true, put green smoke on path segments. -- @return #boolean If true, the two warehouses are connected by road. @@ -1617,7 +1702,7 @@ end --- Check if the warehouse has a shipping lane defined to another warehouse. -- @param #WAREHOUSE self --- @param #WAREHOUSE warehouse The remote warehose to where the connection is checked. +-- @param #WAREHOUSE warehouse The remote warehouse to where the connection is checked. -- @param #boolean markpath If true, place markers of path segments on the F10 map. -- @param #boolean smokepath If true, put green smoke on path segments. -- @return #boolean If true, the two warehouses are connected by road. @@ -1637,7 +1722,7 @@ function WAREHOUSE:HasConnectionNaval(warehouse, markpath, smokepath) if shippinglane then return true,1 else - self:_ErrorMessage("No shipping lane!") + self:T2(string.format("No shipping lane defined between warehouse %s and %s!", self.alias, warehouse.alias)) end end @@ -1645,6 +1730,37 @@ function WAREHOUSE:HasConnectionNaval(warehouse, markpath, smokepath) return nil, -1 end +--- Check if the warehouse has an off road path defined to another warehouse. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE warehouse The remote warehouse to where the connection is checked. +-- @param #boolean markpath If true, place markers of path segments on the F10 map. +-- @param #boolean smokepath If true, put green smoke on path segments. +-- @return #boolean If true, the two warehouses are connected by road. +-- @return #number Path length in meters. Negative distance -1 meter indicates no connection. +function WAREHOUSE:HasConnectionOffRoad(warehouse, markpath, smokepath) + + if warehouse then + + -- Self request + if warehouse.warehouse:GetName()==self.warehouse:GetName() then + return true,1 + end + + -- Get shipping lane. + local offroadpath=self.offroadpaths[warehouse.warehouse:GetName()] + + if offroadpath~=nil then + return true,1 + else + self:T2(string.format("No off-road path defined between warehouse %s and %s!", self.alias, warehouse.alias)) + end + + end + + return nil, -1 +end + + --- Get number of assets in warehouse stock. -- @param #WAREHOUSE self -- @param #string Descriptor (Optional) Descriptor return the number of a specifc asset type. See @{#WAREHOUSE.Descriptor} for possible values. @@ -2740,13 +2856,16 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) ------------------------------------------------------------------------------------------------------------------------------------ -- Set of cargo carriers. - local TransportSet = SET_GROUP:New():FilterDeads() + local TransportSet = SET_GROUP:New() -- Pickup and deploy zones/bases. local PickupAirbaseSet = SET_AIRBASE:New():AddAirbase(self.airbase) local DeployAirbaseSet = SET_AIRBASE:New():AddAirbase(Request.airbase) local DeployZoneSet = SET_ZONE:New():AddZone(Request.warehouse.spawnzone) + --local PickupAirbaseSet = SET_AIRBASE:New() + --ZONE_AIRBASE:New(ZoneName,ZoneAirbase,Radius,AirbaseName) + -- Cargo dispatcher. local CargoTransport --AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER @@ -3131,6 +3250,9 @@ function WAREHOUSE:onafterUnloaded(From, Event, To, group) end --- On after "Arrived" event. Triggered when a group has arrived at its destination warehouse. +-- The routine should be called by the warehouse sending this asset and not by the receiving warehouse. +-- It is checked if this asset is cargo (or self propelled) or transport. If it is cargo it is put into the stock of receiving warehouse. +-- If it is a transporter it is put back into the sending warehouse since transports are supposed to return their home warehouse. -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. @@ -3143,7 +3265,7 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) group:SmokeOrange() end - -- Get request from group. + -- Get pending request this group belongs to. local request=self:_GetRequestOfGroup(group, self.pending) if request then @@ -3155,7 +3277,7 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) warehouse=self end - -- Debug message + -- Debug message. self:_DebugMessage(string.format("Group %s arrived at warehouse %s!", tostring(group:GetName()), warehouse.alias), 5) -- Route mobile ground group to the warehouse. Group has 60 seconds to get there or it is despawned and added as asset to the new warehouse regardless. @@ -3167,23 +3289,6 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) warehouse:__AddAsset(60, group) end - - --[[ - -- Get request from group name. - local request=self:_GetRequestOfGroup(group, self.pending) - - if request then - - -- Route mobile ground group to the warehouse. Group has 60 seconds to get there or it is despawned and added as asset to the new warehouse regardless. - if group:IsGround() and group:GetSpeedMax()>1 then - group:RouteGroundTo(request.warehouse.coordinate, group:GetSpeedMax()*0.3, "Off Road") - end - - -- Move asset from pending queue into new warehouse. - request.warehouse:__AddAsset(60, group) - - end - ]] end @@ -3535,7 +3640,7 @@ end -- Routing functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Route ground units to destination. +--- Route ground units to destination. ROE is set to return fire and alarm state to green. -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group The ground group to be routed -- @param #WAREHOUSE.Queueitem request The request for this group. @@ -3547,19 +3652,50 @@ function WAREHOUSE:_RouteGround(group, request) -- Set speed to 70% of max possible. local _speed=group:GetSpeedMax()*0.7 - -- Waypoints for road-to-road connection. - local Waypoints, canroad = group:TaskGroundOnRoad(request.warehouse.road, _speed, "Off Road", false, self.road) + -- Route waypoints. + local Waypoints={} - -- First waypoint = current position of the group. - local FromWP=group:GetCoordinate():WaypointGround(_speed, "Off Road") - table.insert(Waypoints, 1, FromWP) + -- Check if an off road path has been defined. + local hasoffroad=self:HasConnectionOffRoad(request.warehouse, self.Debug) - -- Final coordinate. - local ToWP=request.warehouse.spawnzone:GetRandomCoordinate():WaypointGround(_speed, "Off Road") - table.insert(Waypoints, #Waypoints+1, ToWP) + if hasoffroad then + + -- Get off road path to remote warehouse. + local path=self.offroadpaths[request.warehouse.warehouse:GetName()] + + -- Loop over user defined shipping lanes. + for i=1,#path do + + -- Shortcut and coordinate intellisense. + local coord=path[i] --Core.Point#COORDINATE + + -- Get waypoint for coordinate. + local Waypoint=coord:WaypointGround(_speed, "Off Road") + + -- Add waypoint to route. + table.insert(Waypoints, Waypoint) + end + + else + + -- Waypoints for road-to-road connection. + Waypoints = group:TaskGroundOnRoad(request.warehouse.road, _speed, "Off Road", false, self.road) + + -- First waypoint = current position of the group. + local FromWP=group:GetCoordinate():WaypointGround(_speed, "Off Road") + table.insert(Waypoints, 1, FromWP) + + -- Final coordinate. + local ToWP=request.warehouse.spawnzone:GetRandomCoordinate():WaypointGround(_speed, "Off Road") + table.insert(Waypoints, #Waypoints+1, ToWP) + + end -- Task function triggering the arrived event. - local TaskFunction = group:TaskFunction("WAREHOUSE._Arrived", self) + --local TaskFunction = group:TaskFunction("WAREHOUSE._Arrived", self) + + -- Task function triggering the arrived event at the last waypoint. + local TaskFunction = self:_SimpleTaskFunction("warehouse:_ArrivedSimple", group) -- Put task function on last waypoint. local Waypoint = Waypoints[#Waypoints] @@ -3574,7 +3710,7 @@ function WAREHOUSE:_RouteGround(group, request) end end ---- Route naval units along user defined shipping lanes to destination warehouse. +--- Route naval units along user defined shipping lanes to destination warehouse. ROE is set to return fire. -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group The naval group to be routed -- @param #WAREHOUSE.Queueitem request The request for this group. @@ -3601,7 +3737,6 @@ function WAREHOUSE:_RouteNaval(group, request) local coord=lane[i] --Core.Point#COORDINATE -- Get waypoint for coordinate. - -- TODO: Might need optimization for Naval. local Waypoint=coord:WaypointGround(_speed) -- Add waypoint to route. @@ -3631,6 +3766,7 @@ end --- Route the airplane from one airbase another. Activates uncontrolled aircraft and sets ROE/ROT for ferry flights. +-- ROE is set to return fire and ROT to passive defence. -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP Aircraft Airplane group to be routed. function WAREHOUSE:_RouteAir(aircraft) @@ -3709,7 +3845,7 @@ end -- Event handler functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---- Arrived event if an air unit/group arrived at its destination. +--- Arrived event if an air unit/group arrived at its destination. This can be an engine shutdown or a landing event. -- @param #WAREHOUSE self -- @param Core.Event#EVENTDATA EventData Event data table. function WAREHOUSE:_OnEventArrived(EventData) @@ -3735,7 +3871,7 @@ function WAREHOUSE:_OnEventArrived(EventData) if self.uid==wid then -- Debug info. - local text=string.format("Air asset group %s arrived at warehouse %s.", group:GetName(), self.alias) + local text=string.format("Air asset group %s from warehouse %s arrived at its destination.", group:GetName(), self.alias) self:_InfoMessage(text) -- Trigger arrived event for this group. Note that each unit of a group will trigger this event. So the onafterArrived function needs to take care of that. @@ -3807,30 +3943,37 @@ end -- @param #WAREHOUSE self -- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventLanding(EventData) - self:T3(self.wid..string.format("Warehouse %s captured event landing!",self.alias)) + self:T3(self.wid..string.format("Warehouse %s captured event landing!", self.alias)) if EventData and EventData.IniGroup then local group=EventData.IniGroup + + -- Try to get UIDs from group name. local wid,aid,rid=self:_GetIDsFromGroup(group) - if wid==self.uid then + + -- Check that this group belongs to this warehouse. + if wid~=nil and wid==self.uid then + + -- Debug info. self:T(self.wid..string.format("Warehouse %s captured event landing of its asset unit %s.", self.alias, EventData.IniUnitName)) - - -- Get request of this group - local request=self:_GetRequestOfGroup(group, self.pending) - - -- If request is nil, the cargo has been delivered. - -- TODO: I might need to add a delivered table, to be better able to get this right. - if request==nil then + + -- Check if all cargo was delivered. + if self.delivered[rid]==true then -- Check if helicopter landed in spawn zone. If so, we call it a day and add it back to stock. if group:GetCategory()==Group.Category.HELICOPTER then if self.spawnzone:IsCoordinateInZone(EventData.IniUnit:GetCoordinate()) then + -- Debug message. self:_DebugMessage("Helicopter landed in spawn zone. No pending request. Putting back into stock.") if self.Debug then group:SmokeWhite() end - self:__AddAsset(30, group) + + -- Group arrived. + self:Arrived(group) + --self:__AddAsset(30, group) + end end @@ -4249,8 +4392,12 @@ function WAREHOUSE:_CheckRequestValid(request) -- Check if there is a valid path on road. local hasroad=self:HasConnectionRoad(request.warehouse) - if not hasroad then - self:E("ERROR: Incorrect request. No valid path on road for ground assets!") + + -- Check if there is a valid off road path. + local hasoffroad=self:HasConnectionOffRoad(request.warehouse) + + if not (hasroad or hasoffroad) then + self:E("ERROR: Incorrect request. No valid path on or off road for ground assets!") valid=false end @@ -5395,10 +5542,10 @@ function WAREHOUSE:_Fireworks(coord) end end ---- Info Message. +--- Info Message. Message send to coalition if reports or debug mode activated (and duration > 0). Text self:I(text) added to DCS.log file. -- @param #WAREHOUSE self -- @param #string text The text of the error message. --- @param #number duration Message display duration in seconds. Default 20 sec. +-- @param #number duration Message display duration in seconds. Default 20 sec. If duration is zero, no message is displayed. function WAREHOUSE:_InfoMessage(text, duration) duration=duration or 20 if duration>0 then @@ -5408,10 +5555,10 @@ function WAREHOUSE:_InfoMessage(text, duration) end ---- Debug message. +--- Debug message. Message send to all if debug mode is activated (and duration > 0). Text self:T(text) added to DCS.log file. -- @param #WAREHOUSE self -- @param #string text The text of the error message. --- @param #number duration Message display duration in seconds. Default 20 sec. +-- @param #number duration Message display duration in seconds. Default 20 sec. If duration is zero, no message is displayed. function WAREHOUSE:_DebugMessage(text, duration) duration=duration or 20 if duration>0 then @@ -5420,14 +5567,14 @@ function WAREHOUSE:_DebugMessage(text, duration) self:T(self.wid..text) end ---- Error message. +--- Error message. Message send to all (if duration > 0). Text self:E(text) added to DCS.log file. -- @param #WAREHOUSE self -- @param #string text The text of the error message. --- @param #number duration Message display duration in seconds. Default 20 sec. +-- @param #number duration Message display duration in seconds. Default 20 sec. If duration is zero, no message is displayed. function WAREHOUSE:_ErrorMessage(text, duration) duration=duration or 20 if duration>0 then - MESSAGE:New(text, duration):ToAllIf(self.Debug) + MESSAGE:New(text, duration):ToAllIf() end self:E(self.wid..text) end From a0e77c04e09cda76845752b9d16d280ea3b3e613 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sat, 8 Sep 2018 14:14:31 +0200 Subject: [PATCH 328/420] Fix for shadowze on the cargo templates not correctly destroyed after being respawned. The original groups should be cleaned from the database. --- Moose Development/Moose/Cargo/CargoGroup.lua | 2 +- Moose Development/Moose/Cargo/CargoUnit.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index abcbb1e83..98ab0028d 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -69,7 +69,7 @@ do -- CARGO_GROUP local WeightGroup = 0 local VolumeGroup = 0 - self.CargoGroup:Destroy() + self.CargoGroup:Destroy( true ) -- generate the crash events, so that the database gets cleaned, and the linked sets get properly cleaned. local GroupName = CargoGroup:GetName() self.CargoName = Name diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index 18ff9743d..4429fc279 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -297,7 +297,7 @@ do -- CARGO_UNIT if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then self:__Load( 1, CargoCarrier, ... ) else - self:__Boarding( -5, CargoCarrier, NearRadius, ... ) + self:__Boarding( -1, CargoCarrier, NearRadius, ... ) self.RunCount = self.RunCount + 1 if self.RunCount >= 40 then self.RunCount = 0 From 094822933532a2d47095b8751be503bc44ed78a8 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sat, 8 Sep 2018 15:42:06 +0200 Subject: [PATCH 329/420] Implemented the option for APC dispatcher and APC cargo, not to disembark when enemies are nearby. The CombatRadius is zero. --- Moose Development/Moose/AI/AI_Cargo_APC.lua | 136 +++++++++++------- .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 27 +++- 2 files changed, 107 insertions(+), 56 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 8b2bd9eee..ce9aa85b0 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -23,11 +23,13 @@ -- -- ## Cargo loading. -- --- The module will load automatically cargo when the APCs are within boarding or loading range. --- The boarding or loading range is specified when the cargo is created in the simulation, and therefore, this range depends on the type of cargo --- and the specified boarding range. +-- The module will load automatically cargo when the APCs are within boarding or loading radius. +-- The boarding or loading radius is specified when the cargo is created in the simulation, and therefore, this radius depends on the type of cargo +-- and the specified boarding radius. -- --- ## Enemies nearby. +-- ## **Defending** the APCs when enemies nearby. +-- +-- Cargo will defend the carrier with its available arms, and to avoid cargo being lost within the battlefield. -- -- When the APCs are approaching enemy units, something special is happening. -- The APCs will stop moving, and the loaded infantry will unboard and follow the APCs and will help to defend the group. @@ -35,13 +37,17 @@ -- to ensure that the APCs are not too far away from the following running infantry. -- Once all enemies are cleared, the infantry will board again automatically into the APCs. Once boarded, the APCs will follow its pre-defined route. -- --- A combat range needs to be specified in meters at the @{#AI_CARGO_APC.New}() method. --- This combat range will trigger the unboarding of troops when enemies are within the combat range around the APCs. --- During my tests, I've noticed that there is a balance between ensuring that the infantry is within sufficient hit range (effectiveness) versus +-- A combat radius needs to be specified in meters at the @{#AI_CARGO_APC.New}() method. +-- This combat radius will trigger the unboarding of troops when enemies are within the combat radius around the APCs. +-- During my tests, I've noticed that there is a balance between ensuring that the infantry is within sufficient hit radius (effectiveness) versus -- vulnerability of the infantry. It all depends on the kind of enemies that are expected to be encountered. --- A combat range of 350 meters to 500 meters has been proven to be the most effective and efficient. +-- A combat radius of 350 meters to 500 meters has been proven to be the most effective and efficient. -- --- ## Infantry health. +-- However, when the defense of the carrier, is not required, it must be switched off. +-- This is done by disabling the defense of the carrier using the method @{#AI_CARGO_APC.SetCombatRadius}(), and providing a combat radius of 0 meters. +-- It can be switched on later when required by reenabling the defense using the method and providing a combat radius larger than 0. +-- +-- ## Infantry or cargo **health**. -- -- When infantry is unboarded from the APCs, the infantry is actually respawned into the battlefield. -- As a result, the unboarding infantry is very _healthy_ every time it unboards. @@ -79,16 +85,14 @@ AI_CARGO_APC = { --- Creates a new AI_CARGO_APC object. -- @param #AI_CARGO_APC self --- @param Wrapper.Group#GROUP APC --- @param Core.Set#SET_CARGO CargoSet --- @param #number CombatRadius +-- @param Wrapper.Group#GROUP APC The carrier APC group. +-- @param Core.Set#SET_CARGO CargoSet The set of cargo to be transported. +-- @param #number CombatRadius Provide the combat radius to defend the carrier by unboarding the cargo when enemies are nearby. When the combat radius is 0, no defense will happen of the carrier. -- @return #AI_CARGO_APC function AI_CARGO_APC:New( APC, CargoSet, CombatRadius ) local self = BASE:Inherit( self, AI_CARGO:New( APC, CargoSet ) ) -- #AI_CARGO_APC - self.CombatRadius = CombatRadius - self:AddTransition( "*", "Monitor", "*" ) self:AddTransition( "*", "Follow", "Following" ) self:AddTransition( "*", "Guard", "Unloaded" ) @@ -96,7 +100,7 @@ function AI_CARGO_APC:New( APC, CargoSet, CombatRadius ) self:AddTransition( "*", "Destroyed", "Destroyed" ) - self:__Monitor( 1 ) + self:SetCombatRadius( CombatRadius ) self:SetCarrier( APC ) @@ -123,7 +127,7 @@ function AI_CARGO_APC:SetCarrier( CargoCarrier ) if AICargoTroops then self:F({}) if not AICargoTroops:Is( "Loaded" ) then - -- There are enemies within combat range. Unload the CargoCarrier. + -- There are enemies within combat radius. Unload the CargoCarrier. AICargoTroops:Destroyed() end end @@ -135,7 +139,7 @@ function AI_CARGO_APC:SetCarrier( CargoCarrier ) if AICargoTroops then self:F( { OnHitLoaded = AICargoTroops:Is( "Loaded" ) } ) if AICargoTroops:Is( "Loaded" ) or AICargoTroops:Is( "Boarding" ) then - -- There are enemies within combat range. Unload the CargoCarrier. + -- There are enemies within combat radius. Unload the CargoCarrier. AICargoTroops:Unload( false ) end end @@ -152,7 +156,7 @@ function AI_CARGO_APC:SetCarrier( CargoCarrier ) end ---- Find a free Carrier within a range. +--- Find a free Carrier within a radius. -- @param #AI_CARGO_APC self -- @param Core.Point#COORDINATE Coordinate -- @param #number Radius @@ -177,6 +181,30 @@ function AI_CARGO_APC:FindCarrier( Coordinate, Radius ) end +--- Enable/Disable unboarding of cargo (infantry) when enemies are nearby (to help defend the carrier). +-- This is only valid for APCs and trucks etc, thus ground vehicles. +-- @param #AI_CARGO_APC self +-- @param #number CombatRadius Provide the combat radius to defend the carrier by unboarding the cargo when enemies are nearby. +-- When the combat radius is 0, no defense will happen of the carrier. +-- When the combat radius is not provided, no defense will happen! +-- @return #AI_CARGO_APC +-- @usage +-- +-- -- Disembark the infantry when the carrier is under attack. +-- AICargoAPC:SetCombatRadius( true ) +-- +-- -- Keep the cargo in the carrier when the carrier is under attack. +-- AICargoAPC:SetCombatRadius( false ) +function AI_CARGO_APC:SetCombatRadius( CombatRadius ) + + self.CombatRadius = CombatRadius or 0 + + if self.CombatRadius > 0 then + self:__Monitor( -5 ) + end + + return self +end --- Follow Infantry to the Carrier. @@ -243,38 +271,40 @@ end function AI_CARGO_APC:onafterMonitor( APC, From, Event, To ) self:F( { APC, From, Event, To } ) - if APC and APC:IsAlive() then - if self.CarrierCoordinate then - if self:IsTransporting() == true then - local Coordinate = APC:GetCoordinate() - self.Zone:Scan( { Object.Category.UNIT } ) - if self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then - if self:Is( "Unloaded" ) or self:Is( "Following" ) then - -- There are no enemies within combat range. Load the CargoCarrier. - self:Load() - end - else - if self:Is( "Loaded" ) then - -- There are enemies within combat range. Unload the CargoCarrier. - self:__Unload( 1 ) - else - if self:Is( "Unloaded" ) then - self:Follow() + if self.CombatRadius > 0 then + if APC and APC:IsAlive() then + if self.CarrierCoordinate then + if self:IsTransporting() == true then + local Coordinate = APC:GetCoordinate() + self.Zone:Scan( { Object.Category.UNIT } ) + if self.Zone:IsAllInZoneOfCoalition( self.Coalition ) then + if self:Is( "Unloaded" ) or self:Is( "Following" ) then + -- There are no enemies within combat radius. Load the CargoCarrier. + self:Load() end - self:F( "I am here" .. self:GetCurrentState() ) - if self:Is( "Following" ) then - for Cargo, APCUnit in pairs( self.Carrier_Cargo ) do - local Cargo = Cargo -- Cargo.Cargo#CARGO - local APCUnit = APCUnit -- Wrapper.Unit#UNIT - if Cargo:IsAlive() then - if not Cargo:IsNear( APCUnit, 40 ) then - APCUnit:RouteStop() - self.CarrierStopped = true - else - if self.CarrierStopped then - if Cargo:IsNear( APCUnit, 25 ) then - APCUnit:RouteResume() - self.CarrierStopped = nil + else + if self:Is( "Loaded" ) then + -- There are enemies within combat radius. Unload the CargoCarrier. + self:__Unload( 1 ) + else + if self:Is( "Unloaded" ) then + self:Follow() + end + self:F( "I am here" .. self:GetCurrentState() ) + if self:Is( "Following" ) then + for Cargo, APCUnit in pairs( self.Carrier_Cargo ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + local APCUnit = APCUnit -- Wrapper.Unit#UNIT + if Cargo:IsAlive() then + if not Cargo:IsNear( APCUnit, 40 ) then + APCUnit:RouteStop() + self.CarrierStopped = true + else + if self.CarrierStopped then + if Cargo:IsNear( APCUnit, 25 ) then + APCUnit:RouteResume() + self.CarrierStopped = nil + end end end end @@ -283,14 +313,14 @@ function AI_CARGO_APC:onafterMonitor( APC, From, Event, To ) end end end + end - + self.CarrierCoordinate = APC:GetCoordinate() end - self.CarrierCoordinate = APC:GetCoordinate() + + self:__Monitor( -5 ) end - self:__Monitor( -5 ) - end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index fffaaf90f..9f9848c16 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -115,18 +115,39 @@ function AI_CARGO_DISPATCHER_APC:New( APCSet, CargoSet, PickupZoneSet, DeployZon local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithZones( APCSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) -- #AI_CARGO_DISPATCHER_APC - self.CombatRadius = CombatRadius or 500 - self:SetDeploySpeed( 70, 120 ) self:SetPickupSpeed( 70, 120 ) self:SetPickupRadius( 0, 0 ) self:SetDeployRadius( 0, 0 ) + + self:SetCombatRadius( CombatRadius ) return self end - function AI_CARGO_DISPATCHER_APC:AICargo( APC, CargoSet ) return AI_CARGO_APC:New( APC, CargoSet, self.CombatRadius ) end + +--- Enable/Disable unboarding of cargo (infantry) when enemies are nearby (to help defend the carrier). +-- This is only valid for APCs and trucks etc, thus ground vehicles. +-- @param #AI_CARGO_DISPATCHER_APC self +-- @param #number CombatRadius Provide the combat radius to defend the carrier by unboarding the cargo when enemies are nearby. +-- When the combat radius is 0, no defense will happen of the carrier. +-- When the combat radius is not provided, no defense will happen! +-- @return #AI_CARGO_DISPATCHER_APC +-- @usage +-- +-- -- Disembark the infantry when the carrier is under attack. +-- AICargoDispatcher:SetCombatRadius( true ) +-- +-- -- Keep the cargo in the carrier when the carrier is under attack. +-- AICargoDispatcher:SetCombatRadius( false ) +function AI_CARGO_DISPATCHER_APC:SetCombatRadius( CombatRadius ) + + self.CombatRadius = CombatRadius or 0 + + return self +end + From e13cf07999f3cdf7c6355e9ddeca8ed270151280 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sat, 8 Sep 2018 16:42:45 +0200 Subject: [PATCH 330/420] Fixing respawn problem. --- Moose Development/Moose/Cargo/CargoGroup.lua | 93 ++++++++------------ 1 file changed, 37 insertions(+), 56 deletions(-) diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 98ab0028d..9bb3a8c1b 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -75,19 +75,19 @@ do -- CARGO_GROUP self.CargoName = Name self.CargoTemplate = UTILS.DeepCopy( _DATABASE:GetGroupTemplate( GroupName ) ) - local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate ) - GroupTemplate.name = self.CargoName .. "#CARGO" - GroupTemplate.groupId = nil + self.GroupTemplate = UTILS.DeepCopy( self.CargoTemplate ) + self.GroupTemplate.name = self.CargoName .. "#CARGO" + self.GroupTemplate.groupId = nil - GroupTemplate.units = {} + self.GroupTemplate.units = {} for UnitID, UnitTemplate in pairs( self.CargoTemplate.units ) do UnitTemplate.name = UnitTemplate.name .. "#CARGO" local CargoUnitName = UnitTemplate.name self.CargoUnitTemplate[CargoUnitName] = UnitTemplate - GroupTemplate.units[#GroupTemplate.units+1] = self.CargoUnitTemplate[CargoUnitName] - GroupTemplate.units[#GroupTemplate.units].unitId = nil + self.GroupTemplate.units[#self.GroupTemplate.units+1] = self.CargoUnitTemplate[CargoUnitName] + self.GroupTemplate.units[#self.GroupTemplate.units].unitId = nil -- And we register the spawned unit as part of the CargoSet. local Unit = UNIT:Register( CargoUnitName ) @@ -95,10 +95,10 @@ do -- CARGO_GROUP end -- Then we register the new group in the database - self.CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID ) + self.CargoGroup = GROUP:NewTemplate( self.GroupTemplate, self.GroupTemplate.CoalitionID, self.GroupTemplate.CategoryID, self.GroupTemplate.CountryID ) -- Now we spawn the new group based on the template created. - self.CargoObject = _DATABASE:Spawn( GroupTemplate ) + self.CargoObject = _DATABASE:Spawn( self.GroupTemplate ) for CargoUnitID, CargoUnit in pairs( self.CargoObject:GetUnits() ) do @@ -109,7 +109,6 @@ do -- CARGO_GROUP self.CargoSet:Add( CargoUnitName, Cargo ) WeightGroup = WeightGroup + Cargo:GetWeight() - --VolumeGroup = VolumeGroup + VolumeUnit end @@ -129,6 +128,35 @@ do -- CARGO_GROUP return self end + + --- Respawn the CargoGroup. + -- @param #CARGO_GROUP self + function CARGO_GROUP:Respawn() + + self:F( { "Respawning" } ) + + for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do + local Cargo = CargoData -- Cargo.Cargo#CARGO + Cargo:Destroy() + Cargo:SetStartState( "UnLoaded" ) + end + + -- Now we spawn the new group based on the template created. + _DATABASE:Spawn( self.GroupTemplate ) + + for CargoUnitID, CargoUnit in pairs( self.CargoObject:GetUnits() ) do + + local CargoUnitName = CargoUnit:GetName() + + local Cargo = CARGO_UNIT:New( CargoUnit, self.Type, CargoUnitName, self.LoadRadius ) + self.CargoSet:Add( CargoUnitName, Cargo ) + + end + + self:SetDeployed( false ) + self:SetStartState( "UnLoaded" ) + + end --- Ungroup the cargo group into individual groups with one unit. -- This is required because by default a group will move in formation and this is really an issue for group control. @@ -678,53 +706,6 @@ do -- CARGO_GROUP end - --- Respawn the CargoGroup. - -- @param #CARGO_GROUP self - function CARGO_GROUP:Respawn() - - self:F( { "Respawning" } ) - - for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do - local Cargo = CargoData -- Cargo.Cargo#CARGO - Cargo:Destroy() - Cargo:SetStartState( "UnLoaded" ) - end - - - -- We iterate through the group template and for each unit in the template, we create a new group with one unit. - for UnitID, UnitTemplate in pairs( self.CargoTemplate.units ) do - - local GroupTemplate = UTILS.DeepCopy( self.CargoTemplate ) - local GroupName = env.getValueDictByKey( GroupTemplate.name ) - - -- We create a new group object with one unit... - -- First we prepare the template... - GroupTemplate.name = GroupName .. "#CARGO#" .. UnitID - GroupTemplate.groupId = nil - GroupTemplate.units = {} - GroupTemplate.units[1] = UnitTemplate - local UnitName = UnitTemplate.name .. "#CARGO" - GroupTemplate.units[1].name = UnitTemplate.name .. "#CARGO" - - - -- Then we register the new group in the database - local CargoGroup = GROUP:NewTemplate( GroupTemplate, GroupTemplate.CoalitionID, GroupTemplate.CategoryID, GroupTemplate.CountryID) - - -- Now we spawn the new group based on the template created. - _DATABASE:Spawn( GroupTemplate ) - - -- And we register the spawned unit as part of the CargoSet. - local Unit = UNIT:FindByName( UnitName ) - --local WeightUnit = Unit:GetDesc().massEmpty - --WeightGroup = WeightGroup + WeightUnit - local CargoUnit = CARGO_UNIT:New( Unit, Type, UnitName, 10 ) - self.CargoSet:Add( UnitName, CargoUnit ) - end - - self:SetDeployed( false ) - self:SetStartState( "UnLoaded" ) - - end --- Signal a flare at the position of the CargoGroup. -- @param #CARGO_GROUP self From 69934a8caecd597b0743637d493e1b67ff72f3b1 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sat, 8 Sep 2018 17:08:52 +0200 Subject: [PATCH 331/420] Fixes for relocation. --- Moose Development/Moose/AI/AI_Cargo_Airplane.lua | 6 ------ Moose Development/Moose/AI/AI_Cargo_Helicopter.lua | 7 ------- 2 files changed, 13 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 0b4be9907..8081a88de 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -141,12 +141,6 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) -- Set carrier. self:SetCarrier( Airplane ) - for _, AirplaneUnit in pairs( Airplane:GetUnits() ) do - AirplaneUnit:SetCargoBayWeightLimit() - end - - self.Relocating = true - return self end diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 11d32053b..019840b32 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -171,13 +171,6 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) end ) - for _, HelicopterUnit in pairs( Helicopter:GetUnits() ) do - HelicopterUnit:SetCargoBayWeightLimit() - end - - self.Relocating = false - self.Transporting = false - self:SetCarrier( Helicopter ) return self From dbd358be35feb02a7556e49e61965e6923f7696f Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sat, 8 Sep 2018 23:50:11 +0200 Subject: [PATCH 332/420] Warehosue v0.3.8 Added AAA, SAM, UAV general attributes. Added Quantity enumerator. Updated to new dispatcher API (still WIP). Improved handling for immobile groups. Fixed some bugs. --- Moose Development/Moose/Core/Zone.lua | 10 +- .../Moose/Functional/Warehouse.lua | 701 ++++++++++++------ 2 files changed, 459 insertions(+), 252 deletions(-) diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index a7a7f7a4f..295adabb8 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -1610,16 +1610,16 @@ do -- ZONE_AIRBASE --- Constructor to create a ZONE_AIRBASE instance, taking the zone name, a zone @{Wrapper.Airbase#AIRBASE} and a radius. -- @param #ZONE_AIRBASE self - -- @param #string ZoneName Name of the zone. - -- @param Wrapper.Airbase#AIRBASE ZoneAirbase The @{Wrapper.Airbase} as the center of the zone. - -- @param DCS#Distance Radius The radius of the zone. + -- @param #string AirbaseName Name of the airbase. + -- @param DCS#Distance Radius (Optional)The radius of the zone in meters. Default 4000 meters. -- @return #ZONE_AIRBASE self - function ZONE_AIRBASE:New( AirbaseName ) + function ZONE_AIRBASE:New( AirbaseName, Radius ) + Radius=Radius or 4000 local Airbase = AIRBASE:FindByName( AirbaseName ) - local self = BASE:Inherit( self, ZONE_RADIUS:New( AirbaseName, Airbase:GetVec2(), 4000 ) ) + local self = BASE:Inherit( self, ZONE_RADIUS:New( AirbaseName, Airbase:GetVec2(), Radius ) ) self._.ZoneAirbase = Airbase self._.ZoneVec2Cache = self._.ZoneAirbase:GetVec2() diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index bf0d52a17..dc321d0d7 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -73,6 +73,7 @@ -- of important supply lines by capturing or destroying warehouses or their associated infrastructure is equally important. -- -- ## What is a warehouse? +-- -- A warehouse is an abstract object represented by a physical (static) building that can hold virtual assets in stock. -- It can (but it must not) be associated with a particular airbase. The associated airbase can be an airdrome, a Helipad/FARP or a ship. -- @@ -80,8 +81,10 @@ -- by themselfs. Once arrived at the requesting warehouse, the assets go into the stock of the requestor and can be activated/deployed when necessary. -- -- ## What assets can be stored? +-- -- Any kind of ground, airborne or naval asset can be stored and are spawned upon request. --- The fact that the assets "live" only virtually in the stock has a positive impact on the game performance. +-- The fact that the assets live only virtually in stock and are put into the game only when needed has a positive impact on the game performance. +-- It also alliviates the problem of limited parking spots at smaller air bases -- -- ## What means of transportation are available? -- Firstly, all mobile assets can be send from warehouse to another on their own. @@ -94,8 +97,13 @@ -- a reasonable degree in DCS at the moment and hence cannot be used yet. -- -- Furthermore, ground assets can be transferred between warehouses by transport units. These are APCs, helicopters and airplanes. The transportation process is modelled --- in a realistic way by using the corresponding cargo dispatcher classes, i.e. @{AI.AI_Cargo_Dispatcher_APC#AI_DISPATCHER_APC}, --- @{AI.AI_Cargo_Dispatcher_Helicopter#AI_DISPATCHER_HELICOPTER} and @{AI.AI_Cargo_Dispatcher_Airplane#AI_DISPATCHER_AIRPLANE}. +-- in a realistic way by using the corresponding cargo dispatcher classes, i.e. +-- +-- * @{AI.AI_Cargo_Dispatcher_APC#AI_DISPATCHER_APC}, +-- * @{AI.AI_Cargo_Dispatcher_Helicopter#AI_DISPATCHER_HELICOPTER} and +-- * @{AI.AI_Cargo_Dispatcher_Airplane#AI_DISPATCHER_AIRPLANE}. +-- +-- Depending on which cargo dispatcher is used (ground or airbore), similar considerations like in the self propelled case are necessary. -- -- === -- @@ -265,10 +273,16 @@ -- An off road path can be defined via the @{#WAREHOUSE.AddOffRoadPath}(*remotewarehouse*, *group*, *oneway*) function, where -- *remotewarehouse* is the warehouse to which the path leads. -- The parameter *group* is a late activated template group. The waypoints of this group are used to define the path between the two warehouses. --- By default, the reverse paths is automatically added to get *from* the remote warehouse to this warehouse unless the parameter *oneway* is set to true. +-- By default, the reverse paths is automatically added to get *from* the remote warehouse to this warehouse unless the parameter *oneway* is set to *true*. -- -- **Note** that if an off road connection is defined between two warehouses this becomes the default path, i.e. even if there is a path *on road* possible --- this will not be used. +-- this will not be used. +-- +-- Also note that you can define multiple off road connections between two warehouses. If there are multiple paths defined, the connection is chosen randomly. +-- It is also possible to add the same path multiple times. By this you can influence the probability of the chosen path. For example Path_1(A->B) has been +-- added two times while Path_2(A->B) was added only once. Hence, the group will choose Path_1 with a probability of 66.6 % while Path_2 is only chosen with +-- a probability of 33.3 %. +-- -- -- ## Rail Connections -- @@ -305,7 +319,12 @@ -- -- The parameter *group* should be a late activated group defined in the mission editor. The waypoints of this group are used as waypoints of the shipping lane. -- --- By default, the reverse lane is automatically added to the remote warehouse. This can be disabled by setting the *oneway* parameter to *false*. +-- By default, the reverse lane is automatically added to the remote warehouse. This can be disabled by setting the *oneway* parameter to *true*. +-- +-- Similar to off road connections, you can also define multiple shipping lanes between two warehouse ports. If there are multiple lanes defined, one is chosen randomly. +-- It is possible to add the same lane multiple times. By this you can influence the probability of the chosen lane. For example Lane_1(A->B) has been +-- added two times while Lane_2(A->B) was added only once. Therefore, the ships will choose Lane_1 with a probability of 66.6 % while Path_2 is only chosen with +-- a probability of 33.3 %. -- -- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_ShippingLane.png) -- @@ -870,7 +889,7 @@ WAREHOUSE.Descriptor = { CATEGORY="category", } ---- Generalized asset attributes. Can be used to request assets with certain general characteristics. +--- Generalized asset attributes. Can be used to request assets with certain general characteristics. See [DCS attributes](https://wiki.hoggitworld.com/view/DCS_enum_attributes) on hoggit. -- @type WAREHOUSE.Attribute -- @field #string AIR_TRANSPORTPLANE Airplane with transport capability. This can be used to transport other assets. -- @field #string AIR_AWACS Airborne Early Warning and Control System. @@ -879,6 +898,7 @@ WAREHOUSE.Descriptor = { -- @field #string AIR_TANKER Airplane which can refuel other aircraft. -- @field #string AIR_TRANSPORTHELO Helicopter with transport capability. This can be used to transport other assets. -- @field #string AIR_ATTACKHELO Attack helicopter. +-- @field #string AIR_UAV Unpiloted Aerial Vehicle, e.g. drones. -- @field #string AIR_OTHER Any airborne unit that does not fall into any other airborne category. -- @field #string GROUND_APC Infantry carriers, in particular Amoured Personell Carrier. This can be used to transport other assets. -- @field #string GROUND_TRUCK Unarmed ground vehicles. @@ -886,6 +906,8 @@ WAREHOUSE.Descriptor = { -- @field #string GROUND_ARTILLERY Artillery assets. -- @field #string GROUND_TANK Tanks (modern or old). -- @field #string GROUND_TRAIN Trains. Not that trains are **not** yet properly implemented in DCS and cannot be used currently. +-- @field #string GROUND_AAA Anti-Aircraft Artillery. +-- @field #string GROUND_SAM Surface-to-Air Missile system or components. -- @field #string GROUND_OTHER Any ground unit that does not fall into any other ground category. -- @field #string NAVAL_AIRCRAFTCARRIER Aircraft carrier. -- @field #string NAVAL_WARSHIP War ship, i.e. cruisers, destroyers, firgates and corvettes. @@ -901,6 +923,7 @@ WAREHOUSE.Attribute = { AIR_TANKER="Air_Tanker", AIR_TRANSPORTHELO="Air_TransportHelo", AIR_ATTACKHELO="Air_AttackHelo", + AIR_UAV="Air_UAV", AIR_OTHER="Air_OtherAir", GROUND_APC="Ground_APC", GROUND_TRUCK="Ground_Truck", @@ -908,6 +931,8 @@ WAREHOUSE.Attribute = { GROUND_ARTILLERY="Ground_Artillery", GROUND_TANK="Ground_Tank", GROUND_TRAIN="Ground_Train", + GROUND_AAA="Ground_AAA", + GROUND_SAM="Ground_SAM", GROUND_OTHER="Ground_OtherGround", NAVAL_AIRCRAFTCARRIER="Naval_AircraftCarrier", NAVAL_WARSHIP="Naval_WarShip", @@ -934,6 +959,21 @@ WAREHOUSE.TransportType = { SELFPROPELLED = "Selfpropelled", } +--- Warehouse quantity enumerator for selecting number of assets, e.g. all, half etc. of what is in stock rather than an absolute number. +-- @type WAREHOUSE.Quantity +-- @field #string ALL All "all" assets currently in stock. +-- @field #string THREEQUARTERS Three quarters "3/4" of assets in stock. +-- @field #string HALF Half "1/2" of assets in stock. +-- @field #string THIRD One third "1/3" of assets in stock. +-- @field #string QUARTER One quarter "1/4" of assets in stock. +WAREHOUSE.Quantity = { + ALL = "all", + THREEQUARTERS = "3/4", + HALF = "1/2", + THIRD = "1/3", + QUARTER = "1/4", +} + --- Warehouse database. Note that this is a global array to have easier exchange between warehouses. -- @type WAREHOUSE.db -- @field #number AssetID Unique ID of each asset. This is a running number, which is increased each time a new asset is added. @@ -947,21 +987,27 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.3.7w" +WAREHOUSE.version="0.3.8" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Test mortars! Check also general requests like all ground. Is this a problem for self propelled if immobile units are among the assets? Check if transport. +-- TODO: Case when all transports are killed and there is still cargo to be delivered. Put cargo back into warehouse. +-- TODO: Test capturing a neutral warehouse. +-- TODO: Make more examples: ARTY, CAP, +-- TODO: Add SAMs and UAVs to generalized attributes. +-- TODO: Check also general requests like all ground. Is this a problem for self propelled if immobile units are among the assets? Check if transport. -- TODO: Add transport units from dispatchers back to warehouse stock once they completed their mission. -- TODO: Added habours as interface for transport to from warehouses? --- TODO: Set ROE for spawned groups. --- TODO: Add offroad lanes between warehouses if road connection is not available. --- DONE: Add possibility to add active groups. Need to create a pseudo template before destroy. <== Does not seem to be necessary any more. -- TODO: Write documentation. --- TODO: Handle the case when units of a group die during the transfer. Adjust template?! See Grouping in SPAWN. --- TODO: Add save/load capability of warehouse <==> percistance after mission restart. Difficult! +-- TODO: Handle the case when units of a group die during the transfer. +-- TODO: Add save/load capability of warehouse <==> percistance after mission restart. Difficult in lua! +-- DONE: Add warehouse quantity enumerator. +-- DONE: Test mortars. Immobile units need a transport. +-- DONE: Set ROE for spawned groups. +-- DONE: Add offroad lanes between warehouses if road connection is not available. +-- DONE: Add possibility to add active groups. Need to create a pseudo template before destroy. <== Does not seem to be necessary any more. -- DONE: Add a time stamp when an asset is added to the stock and for requests. -- DONE: How to get a specific request once the cargo is delivered? Make addrequest addasset non FSM function? Callback for requests like in SPAWN? -- DONE: Add autoselfdefence switch and user function. Default should be off. @@ -1047,8 +1093,9 @@ function WAREHOUSE:New(warehouse, alias) -- Add FSM transitions. -- From State --> Event --> To State - self:AddTransition("NotReadyYet", "Load", "NotReadyYet") -- TODO Load the warehouse state. No sure if it should be in stopped state. - self:AddTransition("NotReadyYet", "Start", "Running") -- Start the warehouse. + self:AddTransition("NotReadyYet", "Load", "Loaded") -- TODO Load the warehouse state. No sure if it should be in stopped state. + self:AddTransition("NotReadyYet", "Start", "Running") -- Start the warehouse from scratch. + self:AddTransition("Loaded", "Start", "Running") -- Start the warehouse when loaded from disk. self:AddTransition("*", "Status", "*") -- Status update. self:AddTransition("*", "AddAsset", "*") -- Add asset to warehouse stock. self:AddTransition("*", "AddRequest", "*") -- New request from other warehouse. @@ -1068,7 +1115,7 @@ function WAREHOUSE:New(warehouse, alias) self:AddTransition("Attacked", "Captured", "Running") -- DONE Warehouse was captured by another coalition. It must have been attacked first. self:AddTransition("*", "AirbaseCaptured", "*") -- DONE Airbase was captured by other coalition. self:AddTransition("*", "AirbaseRecaptured", "*") -- DONE Airbase was re-captured from other coalition. - self:AddTransition("*", "Destroyed", "*") -- DONE Warehouse was destroyed. All assets in stock are gone and warehouse is stopped. + self:AddTransition("*", "Destroyed", "Destoyed") -- DONE Warehouse was destroyed. All assets in stock are gone and warehouse is stopped. ------------------------ --- Pseudo Functions --- @@ -1523,10 +1570,12 @@ function WAREHOUSE:AddShippingLane(remotewarehouse, group, oneway) return self end + -- Initial and final coordinates are random points within the port zones. local startcoord=self.portzone:GetRandomCoordinate() local finalcoord=remotewarehouse.portzone:GetRandomCoordinate() - local lane=self:_NewLane(group,startcoord,finalcoord) + -- Create new lane from waypoints of the template group. + local lane=self:_NewLane(group, startcoord, finalcoord) -- Debug info. Marks along shipping lane. if self.Debug then @@ -1536,14 +1585,21 @@ function WAREHOUSE:AddShippingLane(remotewarehouse, group, oneway) coord:MarkToCoalition(text, self.coalition) end end + + -- Name of the remote warehouse. + local remotename=remotewarehouse.warehouse:GetName() + -- Create new table if no shipping lane exists yet. + if self.shippinglanes[remotename]==nil then + self.shippinglanes[remotename]={} + end + -- Add shipping lane. - -- TODO: Maybe add multiple lanes as a table and later randomly select one for the actual route. - self.shippinglanes[remotewarehouse.warehouse:GetName()]=lane + table.insert(self.shippinglanes[remotename], lane) -- Add shipping lane in the opposite direction. if not oneway then - remotewarehouse:AddShippingLane(self, group, false) + remotewarehouse:AddShippingLane(self, group, true) end return self @@ -1560,26 +1616,36 @@ end -- @return #WAREHOUSE self function WAREHOUSE:AddOffRoadPath(remotewarehouse, group, oneway) + -- Initial and final points are random points within the spawn zone. local startcoord=self.spawnzone:GetRandomCoordinate() local finalcoord=remotewarehouse.spawnzone:GetRandomCoordinate() - local lane=self:_NewLane(group,startcoord,finalcoord) + -- Create new path from template group waypoints. + local path=self:_NewLane(group, startcoord, finalcoord) - -- Debug info. Marks along shipping lane. + -- Debug info. Marks along path. if self.Debug then - for i=1,#lane do - local coord=lane[i] --Core.Point#COORDINATE + for i=1,#path do + local coord=path[i] --Core.Point#COORDINATE local text=string.format("Off road path from %s to %s. Point %d.", self.alias, remotewarehouse.alias, i) coord:MarkToCoalition(text, self.coalition) end end - - -- Add shipping lane. - self.offroadpaths[remotewarehouse.warehouse:GetName()]=lane - -- Add shipping lane in the opposite direction. + -- Name of the remote warehouse. + local remotename=remotewarehouse.warehouse:GetName() + + -- Create new table if no shipping lane exists yet. + if self.offroadpaths[remotename]==nil then + self.offroadpaths[remotename]={} + end + + -- Add off road path. + table.insert(self.offroadpaths[remotename], path) + + -- Add off road path in the opposite direction (if not forbidden). if not oneway then - remotewarehouse:AddOffRoadPath(self, group, false) + remotewarehouse:AddOffRoadPath(self, group, true) end return self @@ -1606,7 +1672,7 @@ function WAREHOUSE:_NewLane(group, startcoord, finalcoord) -- Figure out which point is closer to the port of this warehouse. local distF=startcoord:Get2DDistance(coordF) - local distL=startcoord:GetCoordinate():Get2DDistance(coordL) + local distL=startcoord:Get2DDistance(coordL) -- Add the shipping lane. Need to take care of the wrong "direction". local lane={} @@ -1632,6 +1698,20 @@ function WAREHOUSE:_NewLane(group, startcoord, finalcoord) end +--- Check if the warehouse has not been started yet, i.e. is in the state "NotReadyYet". +-- @param #WAREHOUSE self +-- @return #boolean If true, the warehouse object has been created but the warehouse has not been started yet. +function WAREHOUSE:IsNotReadyYet() + return self:is("NotReadyYet") +end + +--- Check if the warehouse has been loaded from disk via the "Load" event. +-- @param #WAREHOUSE self +-- @return #boolean If true, the warehouse was loaded from disk. +function WAREHOUSE:IsLoaded() + return self:is("Loaded") +end + --- Check if the warehouse is running. -- @param #WAREHOUSE self -- @return #boolean If true, the warehouse is running and requests are processed. @@ -1653,6 +1733,13 @@ function WAREHOUSE:IsAttacked() return self:is("Attacked") end +--- Check if the warehouse has been destroyed. +-- @param #WAREHOUSE self +-- @return #boolean If true, the warehouse had been destroyed. +function WAREHOUSE:IsDestroyed() + return self:is("Destroyed") +end + --- Check if the warehouse is stopped. -- @param #WAREHOUSE self -- @return #boolean If true, the warehouse is stopped. @@ -1946,8 +2033,8 @@ function WAREHOUSE:onafterStatus(From, Event, To) self:_CheckConquered() -- Print queue. - self:_PrintQueue(self.queue, "Queue waiting - before request") - self:_PrintQueue(self.pending, "Queue pending - before request") + --self:_PrintQueue(self.queue, "Queue waiting - before request") + --self:_PrintQueue(self.pending, "Queue pending - before request") -- Check if requests are valid and remove invalid one. self:_CheckRequestConsistancy(self.queue) @@ -1964,8 +2051,8 @@ function WAREHOUSE:onafterStatus(From, Event, To) end -- Print queue after processing requests. - self:_PrintQueue(self.queue, "Queue waiting - after request") - self:_PrintQueue(self.pending, "Queue pending - after request") + self:_PrintQueue(self.queue, "Queue waiting") + self:_PrintQueue(self.pending, "Queue pending") end @@ -1981,30 +2068,112 @@ function WAREHOUSE:onafterStatus(From, Event, To) self:__Status(self.dTstatus) end +--- Count alive and filter dead groups. +-- @param #WAREHOUSE self +-- @param Core.Set#SET_GROUP groupset Set of groups. Dead groups are removed from the set. +-- @return #number Number of alive groups. Returns zero if groupset is nil. +function WAREHOUSE:_FilterDead(groupset) + + local nalive=0 + + if groupset then + + -- Check if groups are still alive + local dead={} + for _,group in pairs(groupset:GetSetObjects()) do + if group and group:IsAlive() then + nalive=nalive+1 + else + table.insert(dead, group) + end + end + + self:E(string.format("FF FilterDead: Alive=%d, Dead=%d", nalive, #dead)) + + -- Remove dead groups + local NoTriggerEvent=false + for _,group in pairs(dead) do + groupset:Remove(group, NoTriggerEvent) + end + end + + return nalive +end --- Function that checks if a pending job is done and can be removed from queue -- @param #WAREHOUSE self function WAREHOUSE:_JobDone() + -- Loop over all pending requests of this warehouse. local done={} for _,request in pairs(self.pending) do local request=request --#WAREHOUSE.Pendingitem - local ncargo=0 - if request.cargogroupset then - ncargo=request.cargogroupset:Count() - end + -- Count number of cargo groups still alive and filter out dead groups. + local ncargo=self:_FilterDead(request.cargogroupset) - local ntransport=0 - if request.transportgroupset then - ntransport=request.transportgroupset:Count() - end + -- Count number of transport groups (if any) and filter out dead groups. Dead groups are removed from the set. + local ntransport=self:_FilterDead(request.transportgroupset) - --TODO: Check if any transports, e.g. APCs were not used and are still standing around in the spawn zone. - -- Also planes and helos might not be used? if ncargo==0 and ntransport==0 then + + --------------- + -- Job done! -- + --------------- + + self:E(string.format("Request id=%d done! No more cargo and transport assets alive.", request.uid)) table.insert(done, request) + + elseif ncargo>0 and ntransport==0 and request.ntransport>0 then + + ----------------------------------- + -- Still cargo but no transports -- + ----------------------------------- + + -- Info message. + self:_InfoMessage(string.format("Warehouse %s: All transports of request id=%s dead! Putting remaining %s cargo assets back into warehouse!", self.alias, request.uid, ncargo)) + + -- All transports are dead but there is still cargo left ==> Put cargo back into stock. + for _,group in pairs(request.cargogroupset) do + local group=group --Wrapper.Group#GROUP + group:SmokeRed() + + -- Add all assets back to stock + if group:IsPartlyOrCompletelyInZone(self.spawnzone) then + self:__AddAsset(5, group) + end + + end + + elseif ncargo==0 and ntransport>0 then + + ----------------------------------- + -- No cargo but still transports -- + ----------------------------------- + + -- This is difficult! How do I know if transports were unused? They could also be just on their way back home. + + -- TODO: Handle this case. + if true then + return + end + + -- Info message. + self:_InfoMessage(string.format("Warehouse %s: No cargo assets left for request id=%s. Putting remaining %s transport assets back into warehouse!", self.alias, request.uid, ntransport)) + + -- All transports are dead but there is still cargo left ==> Put cargo back into stock. + for _,group in pairs(request.transportgroupset) do + local group=group --Wrapper.Group#GROUP + group:SmokeRed() + + -- Add all assets back to stock + if group:IsPartlyOrCompletelyInZone(self.spawnzone) then + self:__AddAsset(5, group) + end + + end + end end @@ -2839,16 +3008,15 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) _loadradius=1000 end + --_loadradius=nil + --_nearradius=nil + -- Empty cargo group set. CargoGroups = SET_CARGO:New() -- Add cargo groups to set. for _i,_group in pairs(_spawngroups:GetSetObjects()) do - local group=_group --Wrapper.Group#GROUP - local _wid,_aid,_rid=self:_GetIDsFromGroup(group) - local _alias=self:_alias(group:GetTypeName(),_wid,_aid,_rid) - local cargogroup = CARGO_GROUP:New(_group, _cargotype,_alias,_loadradius,_nearradius) - CargoGroups:AddCargo(cargogroup) + CargoGroups:AddCargo(CARGO_GROUP:New(_group, _cargotype,_group:GetName(),_loadradius,_nearradius)) end ------------------------------------------------------------------------------------------------------------------------------------ @@ -2858,17 +3026,6 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Set of cargo carriers. local TransportSet = SET_GROUP:New() - -- Pickup and deploy zones/bases. - local PickupAirbaseSet = SET_AIRBASE:New():AddAirbase(self.airbase) - local DeployAirbaseSet = SET_AIRBASE:New():AddAirbase(Request.airbase) - local DeployZoneSet = SET_ZONE:New():AddZone(Request.warehouse.spawnzone) - - --local PickupAirbaseSet = SET_AIRBASE:New() - --ZONE_AIRBASE:New(ZoneName,ZoneAirbase,Radius,AirbaseName) - - -- Cargo dispatcher. - local CargoTransport --AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER - -- Shortcut to transport assets. local _assetstock=Request.transportassets @@ -2884,160 +3041,147 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Transport assets table. local _transportassets={} - - -- Dependent on transport type, spawn the transports and set up the dispatchers. - if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then - ---------------- - --- AIRPLANE --- - ---------------- - - -- Spawn the transport groups. - for i=1,Request.ntransport do + ---------------------------- + -- Spawn Transport Groups -- + ---------------------------- - -- Get stock item. - local _assetitem=_assetstock[i] --#WAREHOUSE.Assetitem + -- Spawn the transport groups. + for i=1,Request.ntransport do + + -- Get stock item. + local _assetitem=_assetstock[i] --#WAREHOUSE.Assetitem -- Create an alias name with the UIDs for the sending warehouse, asset and request. - local _alias=self:_alias(_assetitem.unittype, self.uid, _assetitem.uid, Request.uid) - + local _alias=self:_alias(_assetitem.unittype, self.uid, _assetitem.uid, Request.uid) + + local spawngroup=nil --Wrapper.Group#GROUP + + -- Spawn assets depending on type. + if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then + -- Spawn plane at airport in uncontrolled state. Will get activated when cargo is loaded. - local spawngroup=self:_SpawnAssetAircraft(_alias,_assetitem, Pending, Parking[_assetitem.uid], true) - - if spawngroup then - -- Set state of warehouse so we can retrieve it later. - spawngroup:SetState(spawngroup, "WAREHOUSE", self) - - -- Add group to transportset. - TransportSet:AddGroup(spawngroup) - - Pending.assets[_assetitem.uid]=_assetitem - table.insert(_transportassets,_assetitem) - end + spawngroup=self:_SpawnAssetAircraft(_alias,_assetitem, Pending, Parking[_assetitem.uid], true) + + elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then + + -- Spawn helos at airport in controlled state. They need to fly to the spawn zone. + spawngroup=self:_SpawnAssetAircraft(_alias,_assetitem, Pending, Parking[_assetitem.uid], false) + + elseif Request.transporttype==WAREHOUSE.TransportType.APC then + + -- Spawn APCs in spawn zone. + spawngroup=self:_SpawnAssetGroundNaval(_alias, _assetitem, Request, self.spawnzone) + + elseif Request.transporttype==WAREHOUSE.TransportType.TRAIN then + + self:E(self.wid.."ERROR: cargo transport by train not supported yet!") + return + + elseif Request.transporttype==WAREHOUSE.TransportType.SHIP then + + self:E(self.wid.."ERROR: cargo transport by ship not supported yet!") + return + + elseif Request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then + + self:E(self.wid.."ERROR: transport type selfpropelled was already handled above. We should not get here!") + return + + else + self:E(self.wid.."ERROR: unknown transport type!") + return end + + -- Check if group was spawned. + if spawngroup then + -- Set state of warehouse so we can retrieve it later. + spawngroup:SetState(spawngroup, "WAREHOUSE", self) - -- Delete spawned items from warehouse stock. - for _,_item in pairs(_transportassets) do - self:_DeleteStockItem(_item) + -- Add group to transportset. + TransportSet:AddGroup(spawngroup) + + -- Add assets to pending request. + Pending.assets[_assetitem.uid]=_assetitem + + -- Add transport assets. + table.insert(_transportassets,_assetitem) end + end + + -- Delete spawned items from warehouse stock. + for _,_item in pairs(_transportassets) do + self:_DeleteStockItem(_item) + end + + -- Add cargo groups to request. + Pending.transportgroupset=TransportSet + + -- Add request to pending queue. + table.insert(self.pending, Pending) + + -- Delete request from queue. + self:_DeleteQueueItem(Request, self.queue) + + ------------------------ + -- Create Dispatchers -- + ------------------------ + + -- Cargo dispatcher. + local CargoTransport --AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER + + if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then + + -- Pickup and deloay zones. + local PickupAirbaseSet = SET_ZONE:New():AddZone(ZONE_AIRBASE:New(self.airbase:GetName())) + local DeployAirbaseSet = SET_ZONE:New():AddZone(ZONE_AIRBASE:New(Request.airbase:GetName())) -- Define dispatcher for this task. CargoTransport = AI_CARGO_DISPATCHER_AIRPLANE:New(TransportSet, CargoGroups, PickupAirbaseSet, DeployAirbaseSet) - + elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then - ------------------ - --- HELICOPTER --- - ------------------ + -- Pickup and deloay zones. + --local PickupAirbaseSet = SET_ZONE:New():AddZone(ZONE_AIRBASE:New(self.airbase:GetName())) + --local DeployAirbaseSet = SET_ZONE:New():AddZone(ZONE_AIRBASE:New(Request.airbase:GetName())) - -- Spawn the transport groups. - for i=1,Request.ntransport do - - -- Get stock item. - local _assetitem=_assetstock[i] --#WAREHOUSE.Assetitem - - -- Create an alias name with the UIDs for the sending warehouse, asset and request. - local _alias=self:_alias(_assetitem.unittype, self.uid, _assetitem.uid, Request.uid) - - -- Spawn plane at airport in controlled state. They need to fly to the spawn zone. - local spawngroup=self:_SpawnAssetAircraft(_alias,_assetitem, Pending, Parking[_assetitem.uid], false) - - if spawngroup then - - -- Set state of warehouse so we can retrieve it later. - spawngroup:SetState(spawngroup, "WAREHOUSE", self) - - -- Add group to transportset. - TransportSet:AddGroup(spawngroup) - - Pending.assets[_assetitem.uid]=_assetitem - table.insert(_transportassets,_assetitem) - else - self:E(self.wid.."ERROR: spawngroup helo transport does not exist!") - end - end - - -- Delete spawned items from warehouse stock. - for _,_item in pairs(_transportassets) do - self:_DeleteStockItem(_item) - end + -- Pickup and deloay zones. + local PickupZoneSet = SET_ZONE:New():AddZone(self.spawnzone) + local DeployZoneSet = SET_ZONE:New():AddZone(Request.warehouse.spawnzone) -- Define dispatcher for this task. - CargoTransport = AI_CARGO_DISPATCHER_HELICOPTER:New(TransportSet, CargoGroups, DeployZoneSet) + CargoTransport = AI_CARGO_DISPATCHER_HELICOPTER:New(TransportSet, CargoGroups, PickupZoneSet, DeployZoneSet) + + -- Home zone. + CargoTransport:SetHomeZone(self.spawnzone) --TODO: Need to check/optimize if/how this works with polygon zones! -- The 20 m inner radius are to ensure that the helo does not land on the warehouse itself in the middle of the default spawn zone. CargoTransport:SetPickupRadius(self.spawnzone:GetRadius(), 20) - CargoTransport:SetDeployRadius(Request.warehouse.spawnzone:GetRadius(), 20) + CargoTransport:SetDeployRadius(Request.warehouse.spawnzone:GetRadius(), 20) - -- Home zone. - CargoTransport:SetHomeZone(self.spawnzone) - - -- Home airbase (not working). - --CargoTransport:SetHomeBase(self.airbase) - elseif Request.transporttype==WAREHOUSE.TransportType.APC then - ----------- - --- APC --- - ----------- - - -- Spawn the transport groups. - for i=1,Request.ntransport do - - -- Get stock item. - local _assetitem=_assetstock[i] --#WAREHOUSE.Assetitem - - -- Create an alias name with the UIDs for the sending warehouse, asset and request. - local _alias=self:_alias(_assetitem.unittype, self.uid, _assetitem.uid, Request.uid) - - -- Spawn ground asset. - local spawngroup=self:_SpawnAssetGroundNaval(_alias, _assetitem, Request, self.spawnzone) - - if spawngroup then - - -- Set state of warehouse so we can retrieve it later. - spawngroup:SetState(spawngroup, "WAREHOUSE", self) - - -- Add group to transportset. - TransportSet:AddGroup(spawngroup) - - Pending.assets[_assetitem.uid]=_assetitem - table.insert(_transportassets,_assetitem) - end - end - - -- Delete spawned items from warehouse stock. - for _,_item in pairs(_transportassets) do - self:_DeleteStockItem(_item) - end + + -- Pickup and deloay zones. + local PickupZoneSet = SET_ZONE:New():AddZone(self.spawnzone) + local DeployZoneSet = SET_ZONE:New():AddZone(Request.warehouse.spawnzone) -- Define dispatcher for this task. - CargoTransport = AI_CARGO_DISPATCHER_APC:New(TransportSet, CargoGroups, DeployZoneSet, 0) + CargoTransport = AI_CARGO_DISPATCHER_APC:New(TransportSet, CargoGroups, PickupZoneSet, DeployZoneSet, 0) -- Set home zone. CargoTransport:SetHomeZone(self.spawnzone) - elseif Request.transporttype==WAREHOUSE.TransportType.TRAIN then - - self:E(self.wid.."ERROR: cargo transport by train not supported yet!") - return - - elseif Request.transporttype==WAREHOUSE.TransportType.SHIP then - - self:E(self.wid.."ERROR: cargo transport by ship not supported yet!") - return - - elseif Request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then - - self:E(self.wid.."ERROR: transport type selfpropelled was already handled above. We should not get here!") - return + --TODO: Need to check/optimize if/how this works with polygon zones! + -- The 20 m inner radius are to ensure that the helo does not land on the warehouse itself in the middle of the default spawn zone. + CargoTransport:SetPickupRadius(self.spawnzone:GetRadius(), 20) + CargoTransport:SetDeployRadius(Request.warehouse.spawnzone:GetRadius(), 20) + else - self:E(self.wid.."ERROR: unknown transport type!") - return + self:E(self.wid.."ERROR: Unknown transporttype!") end - --- Function called when cargo has arrived and was unloaded. function CargoTransport:OnAfterUnloaded(From, Event, To, Carrier, Cargo) @@ -3050,21 +3194,19 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Get group obejet. local group=Cargo:GetObject() --Wrapper.Group#GROUP - + -- Get warehouse state. local warehouse=Carrier:GetState(Carrier, "WAREHOUSE") --#WAREHOUSE - -- Load the cargo in the warehouse. - --Cargo:Load(warehouse.warehouse) - - -- Get warehouse ID of the - local wid=warehouse:_GetIDsFromGroup(group) - - -- Get the receiving warehouse. - local warehouseReceiving=warehouse:_FindWarehouseInDB(wid) + local text=string.format("FF Group %s was unloaded from carrier %s.", tostring(group:GetName()), tostring(Carrier:GetName())) + env.info(text) - -- Trigger Arrived event at the receiving warehouse. - warehouseReceiving:__Arrived(1, group) + + -- Load the cargo in the warehouse. + Cargo:Load(warehouse.warehouse) + + -- Trigger Arrived event. + warehouse:Arrived(group) end --- On after BackHome event. @@ -3092,15 +3234,6 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Start dispatcher. CargoTransport:__Start(5) - - -- Add cargo groups to request. - Pending.transportgroupset=TransportSet - - -- Add request to pending queue. - table.insert(self.pending, Pending) - - -- Delete request from queue. - self:_DeleteQueueItem(Request, self.queue) end @@ -3466,7 +3599,7 @@ function WAREHOUSE:onafterAttacked(From, Event, To, Coalition, Country) text=text..string.format("Deploying all %d ground assets.", nground) -- Add self request. - self:AddRequest(self, WAREHOUSE.Descriptor.CATEGORY, Group.Category.GROUND, "all", nil, nil , 0) + self:AddRequest(self, WAREHOUSE.Descriptor.CATEGORY, Group.Category.GROUND, WAREHOUSE.Quantity.ALL, nil, nil , 0) else text=text..string.format("No ground assets currently available.") end @@ -3621,7 +3754,7 @@ function WAREHOUSE:onafterAirbaseRecaptured(From, Event, To, Coalition) end ---- On after "Destroyed" event. Warehouse was destroyed. All services are stopped. +--- On after "Destroyed" event. Warehouse was destroyed. All services are stopped. Warehouse is going to "Stopped" state in one minute. -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. @@ -3632,8 +3765,8 @@ function WAREHOUSE:onafterDestroyed(From, Event, To) local text=string.format("Warehouse %s was destroyed!", self.alias) self:_InfoMessage(text) - -- Stop warehouse FSM. - self:Stop() + -- Stop warehouse FSM in one minute. + self:__Stop(60) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -3658,10 +3791,12 @@ function WAREHOUSE:_RouteGround(group, request) -- Check if an off road path has been defined. local hasoffroad=self:HasConnectionOffRoad(request.warehouse, self.Debug) + -- Check if any off road paths have be defined. They have priority! if hasoffroad then - -- Get off road path to remote warehouse. - local path=self.offroadpaths[request.warehouse.warehouse:GetName()] + -- Get off road path to remote warehouse. If more have been defined, pick one randomly. + local remotename=request.warehouse.warehouse:GetName() + local path=self.offroadpaths[remotename][math.random(#self.offroadpaths[remotename])] -- Loop over user defined shipping lanes. for i=1,#path do @@ -3723,8 +3858,12 @@ function WAREHOUSE:_RouteNaval(group, request) local _speed=group:GetSpeedMax()*0.8 -- Get shipping lane to remote warehouse. - local lane=self.shippinglanes[request.warehouse.warehouse:GetName()] + --local lane=self.shippinglanes[request.warehouse.warehouse:GetName()] + -- Get off road path to remote warehouse. If more have been defined, pick one randomly. + local remotename=request.warehouse.warehouse:GetName() + local path=self.shippinglanes[remotename][math.random(#self.ship[remotename])] + if lane then -- Route waypoints. @@ -4022,6 +4161,23 @@ function WAREHOUSE:_OnEventCrashOrDead(EventData) local wid,aid,rid=self:_GetIDsFromGroup(group) if wid==self.uid then self:T(self.wid..string.format("Warehouse %s captured event dead or crash of its asset unit %s.", self.alias, EventData.IniUnitName)) + + for _,request in pairs(self.pending) do + local request=request --#WAREHOUSE.Pendingitem + + if request.uid==rid then + -- Count number of cargo groups still alive and filter out dead groups. Dead groups are removed from the set. + local ncargo=self:_FilterDead(request.cargogroupset) + + -- Count number of transport groups (if any) and filter out dead groups. Dead groups are removed from the set. + local ntransport=self:_FilterDead(request.transportgroupset) + + self:T(self.wid..string.format("Asset groups left for request id=%d: ncargo=%d, ntransport=%d", rid, ncargo, ntransport)) + end + + end + + end end @@ -4242,7 +4398,7 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) -- Request from enemy coalition? if self.coalition~=request.warehouse.coalition then - self:E(self.wid..string.format("ERROR: INVALID request. Requesting warehouse is of wrong coaltion! Own coalition %d. Requesting warehouse %d", self.coalition, request.warehouse.coalition)) + self:E(self.wid..string.format("ERROR: INVALID request. Requesting warehouse is of wrong coaltion! Own coalition %d != %d of requesting warehouse.", self.coalition, request.warehouse.coalition)) valid=false end @@ -4505,9 +4661,15 @@ function WAREHOUSE:_CheckRequestNow(request) return false end - + + -- If no transport is requested, assets need to be mobile unless it is a self request. + local onlymobile=false + if request.ntransport==0 and not request.toself then + onlymobile=true + end + -- Check if number of requested assets is in stock. - local _assets,_nassets,_enough=self:_FilterStock(self.stock, request.assetdesc, request.assetdescval, request.nasset) + local _assets,_nassets,_enough=self:_FilterStock(self.stock, request.assetdesc, request.assetdescval, request.nasset, onlymobile) -- Check if enough assets are in stock. if not _enough then @@ -4543,6 +4705,12 @@ function WAREHOUSE:_CheckRequestNow(request) request.cargoassets=_assets request.cargoattribute=_assetattribute request.cargocategory=_assetcategory + + env.info("FF selected cargo assets") + for _,_asset in pairs(_assets) do + local asset=_asset --#WAREHOUSE.Assetitem + env.info(string.format("- name = %s", asset.templatename)) + end end @@ -5117,25 +5285,33 @@ end --- Filter stock assets by table entry. -- @param #WAREHOUSE self -- @param #table stock Table holding all assets in stock of the warehouse. Each entry is of type @{#WAREHOUSE.Assetitem}. --- @param #string item Descriptor --- @param value Value of the descriptor. +-- @param #string descriptor Descriptor describing the filtered assets. +-- @param attribute Value of the descriptor. -- @param #number nmax (Optional) Maximum number of items that will be returned. Default nmax=nil is all matching items are returned. +-- @param #boolean mobile (Optional) If true, filter only mobile assets. -- @return #table Filtered stock items table. -- @return #number Total number of (requested) assets available. -- @return #boolean If true, enough assets are available. -function WAREHOUSE:_FilterStock(stock, item, value, nmax) +function WAREHOUSE:_FilterStock(stock, descriptor, attribute, nmax, mobile) -- Default all. - nmax=nmax or "all" + nmax=nmax or WAREHOUSE.Quantity.ALL + if mobile==nil then + mobile=false + end -- Filtered array. local filtered={} -- Count total number in stock. local ntot=0 - for _,_stock in ipairs(stock) do - if _stock[item]==value then - ntot=ntot+1 + for _,_asset in ipairs(stock) do + local asset=_asset --#WAREHOUSE.Assetitem + local ismobile=asset.speedmax>0 + if asset[descriptor]==attribute then + if (mobile==true and ismobile) or mobile==false then + ntot=ntot+1 + end end end @@ -5146,15 +5322,15 @@ function WAREHOUSE:_FilterStock(stock, item, value, nmax) -- Handle string input for nmax. if type(nmax)=="string" then - if nmax:lower()=="all" then + if nmax:lower()==WAREHOUSE.Quantity.ALL then nmax=ntot - elseif nmax:lower()=="threequarter" then + elseif nmax==WAREHOUSE.Quantity.THREEQUARTERS then nmax=ntot*3/4 - elseif nmax:lower()=="half" then + elseif nmax==WAREHOUSE.Quantity.HALF then nmax=ntot/2 - elseif nmax:lower()=="third" then + elseif nmax==WAREHOUSE.Quantity.THIRD then nmax=ntot/3 - elseif nmax:lower()=="quarter" then + elseif nmax==WAREHOUSE.Quantity.QUARTER then nmax=ntot/4 else nmax=math.min(1, ntot) @@ -5162,13 +5338,24 @@ function WAREHOUSE:_FilterStock(stock, item, value, nmax) end -- Loop over stock items. - for _i,_stock in ipairs(stock) do - if _stock[item]==value then - _stock.pos=_i - table.insert(filtered, _stock) - if nmax~=nil and #filtered>=nmax then - return filtered, ntot, true - end + for _i,_asset in ipairs(stock) do + local asset=_asset --#WAREHOUSE.Assetitem + + -- Check if asset has the right attribute. + if asset[descriptor]==attribute then + + -- Check if asset has to be mobile. + if (mobile and asset.speedmax>0) or (not mobile) then + + -- Add asset to filtered table. + table.insert(filtered, asset) + + -- Break loop if nmax was reached. + if nmax~=nil and #filtered>=nmax then + return filtered, ntot, true + end + + end end end @@ -5210,7 +5397,8 @@ function WAREHOUSE:_GetAttribute(group) local awacs=group:HasAttribute("AWACS") local fighter=group:HasAttribute("Fighters") or group:HasAttribute("Interceptors") or group:HasAttribute("Multirole fighters") local bomber=group:HasAttribute("Bombers") - local tanker=group:HasAttribute("Tankers") + local tanker=group:HasAttribute("Tankers") + local uav=group:HasAttribute("UAV") -- Helicopters local transporthelo=group:HasAttribute("Transport helicopters") local attackhelicopter=group:HasAttribute("Attack helicopters") @@ -5224,6 +5412,8 @@ function WAREHOUSE:_GetAttribute(group) local infantry=group:HasAttribute("Infantry") local artillery=group:HasAttribute("Artillery") local tank=group:HasAttribute("Old Tanks") or group:HasAttribute("Modern Tanks") + local aaa=group:HasAttribute("AAA") + local sam=group:HasAttribute("SAM") -- Train local train=group:GetCategory()==Group.Category.TRAIN @@ -5252,6 +5442,8 @@ function WAREHOUSE:_GetAttribute(group) attribute=WAREHOUSE.Attribute.AIR_TRANSPORTHELO elseif attackhelicopter then attribute=WAREHOUSE.Attribute.AIR_ATTACKHELO + elseif uav then + attribute=WAREHOUSE.Attribute.AIR_UAV elseif apc then attribute=WAREHOUSE.Attribute.GROUND_APC elseif truck then @@ -5262,6 +5454,10 @@ function WAREHOUSE:_GetAttribute(group) attribute=WAREHOUSE.Attribute.GROUND_ARTILLERY elseif tank then attribute=WAREHOUSE.Attribute.GROUND_TANK + elseif aaa then + attribute=WAREHOUSE.Attribute.GROUND_AAA + elseif sam then + attribute=WAREHOUSE.Attribute.GROUND_SAM elseif train then attribute=WAREHOUSE.Attribute.GROUND_TRAIN elseif aircraftcarrier then @@ -5273,9 +5469,16 @@ function WAREHOUSE:_GetAttribute(group) elseif unarmedship then attribute=WAREHOUSE.Attribute.NAVAL_UNARMEDSHIP else - attribute=WAREHOUSE.Attribute.OTHER_UNKNOWN + if group:IsGround() then + attribute=WAREHOUSE.Attribute.GROUND_OTHER + elseif group:IsShip() then + attribute=WAREHOUSE.Attribute.NAVAL_OTHER + elseif group:IsAir() then + attribute=WAREHOUSE.Attribute.AIR_OTHER + else + attribute=WAREHOUSE.Attribute.OTHER_UNKNOWN + end end - end return attribute @@ -5299,7 +5502,7 @@ end --- Returns the number of assets for each generalized attribute. -- @param #WAREHOUSE self -- @param #table stock The stock of the warehouse. --- @return #table Data table holding the numbers. +-- @return #table Data table holding the numbers, i.e. data[attibute]=n. function WAREHOUSE:GetStockInfo(stock) local _data={} @@ -5389,6 +5592,7 @@ function WAREHOUSE:_PrintQueue(queue, name) if qitem.timestamp then clock=tostring(UTILS.SecondsToClock(qitem.timestamp)) end + local assignment=tostring(qitem.assignment) local requestor=qitem.warehouse.alias local requestorAirbaseCat=qitem.category local assetdesc=qitem.assetdesc @@ -5418,8 +5622,8 @@ function WAREHOUSE:_PrintQueue(queue, name) -- Output text: text=text..string.format( - "\n%d) UID=%d, Prio=%d, Clock=%s | Requestor=%s [Airbase=%s, category=%d] | Assets(%s)=%s: #requested=%s / #alive=%s / #delivered=%s | Transport=%s: #requested=%d / #alive=%s / #home=%s", - i, uid, prio, clock, requestor, airbasename, requestorAirbaseCat, assetdesc, assetdescval, nasset, ncargogroupset, ndelivered, transporttype, ntransport, ntransportalive, ntransporthome) + "\n%d) UID=%d, Prio=%d, Clock=%s, Assignment=%s | Requestor=%s [Airbase=%s, category=%d] | Assets(%s)=%s: #requested=%s / #alive=%s / #delivered=%s | Transport=%s: #requested=%d / #alive=%s / #home=%s", + i, uid, prio, clock, assignment, requestor, airbasename, requestorAirbaseCat, assetdesc, assetdescval, nasset, ncargogroupset, ndelivered, transporttype, ntransport, ntransportalive, ntransporthome) end @@ -5471,9 +5675,11 @@ function WAREHOUSE:_GetStockAssetsText(messagetoall) local text="Stock:\n" local total=0 for _attribute,_count in pairs(_data) do - local attribute=tostring(UTILS.Split(_attribute, "_")[2]) - text=text..string.format("%s = %d\n", attribute,_count) - total=total+_count + if _count>0 then + local attribute=tostring(UTILS.Split(_attribute, "_")[2]) + text=text..string.format("%s = %d\n", attribute,_count) + total=total+_count + end end text=text..string.format("===================\n") text=text..string.format("Total = %d\n", total) @@ -5485,7 +5691,7 @@ function WAREHOUSE:_GetStockAssetsText(messagetoall) return text end ---- Create or update mark text at warehouse, which is displayed in F10 map. +--- Create or update mark text at warehouse, which is displayed in F10 map showing how many assets of each type are in stock. -- Only the coaliton of the warehouse owner is able to see it. -- @param #WAREHOUSE self -- @return #string Text about warehouse stock @@ -5500,12 +5706,13 @@ function WAREHOUSE:_UpdateWarehouseMarkText() local _data=self:GetStockInfo(self.stock) -- Text. - local text="Warehouse Stock:\n" - text=text..string.format("Total assets: %d\n", #_data) - local total=0 + local text=string.format("Warehouse Stock - total assets %d:\n", #self.stock) + for _attribute,_count in pairs(_data) do - local attribute=tostring(UTILS.Split(_attribute, "_")[2]) - text=text..string.format("%s=%d, ", attribute,_count) + if _count>0 then + local attribute=tostring(UTILS.Split(_attribute, "_")[2]) + text=text..string.format("%s=%d, ", attribute,_count) + end end -- Create/update marker at warehouse in F10 map. From 492563d6f330777a9b78be2a27cb864931dd0128 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sun, 9 Sep 2018 07:39:13 +0200 Subject: [PATCH 333/420] Fixing errors with Aborted, Failed and Cancelled state transitions. The handling was wrong, causing the root handlers not being called anymore, causing the task not being able to fail, cancel, abort. --- Moose Development/Moose/Core/Fsm.lua | 17 +++++++++++++++-- .../Moose/Tasking/Task_A2A_Dispatcher.lua | 6 +++--- .../Moose/Tasking/Task_A2G_Dispatcher.lua | 6 +++--- .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 6 +++--- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index a4a833da2..7469a3726 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -981,8 +981,9 @@ do -- FSM_PROCESS self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] .. " *** Task: " .. self.Task:GetName() .. ", TaskUnit: " .. self.Controllable:GetName() ) end self._EventSchedules[EventName] = nil + local Result, Value if self.Controllable and self.Controllable:IsAlive() == true then - local Result, Value = xpcall( function() return self[handler]( self, self.Controllable, self.Task, unpack( params ) ) end, ErrorHandler ) + Result, Value = xpcall( function() return self[handler]( self, self.Controllable, self.Task, unpack( params ) ) end, ErrorHandler ) end return Value --return self[handler]( self, self.Controllable, unpack( params ) ) @@ -1204,10 +1205,22 @@ do -- FSM_TASK function FSM_TASK:_call_handler( step, trigger, params, EventName ) local handler = step .. trigger + local ErrorHandler = function( errmsg ) + + env.info( "Error in SCHEDULER function:" .. errmsg ) + if BASE.Debug ~= nil then + env.info( BASE.Debug.traceback() ) + end + + return errmsg + end + if self[handler] then self:T( "*** FSM *** " .. step .. " *** " .. params[1] .. " --> " .. params[2] .. " --> " .. params[3] .. " *** Task: " .. self.TaskName ) self._EventSchedules[EventName] = nil - return self[handler]( self, unpack( params ) ) + --return self[handler]( self, unpack( params ) ) + local Result, Value = xpcall( function() return self[handler]( self, unpack( params ) ) end, ErrorHandler ) + return Value end end diff --git a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua index 8f85ef7d2..44db945c7 100644 --- a/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2A_Dispatcher.lua @@ -574,15 +574,15 @@ do -- TASK_A2A_DISPATCHER self:Success( Task ) end - function Task.onenterCancelled( Task, From, Event, To ) + function Task.OnEnterCancelled( Task, From, Event, To ) self:Cancelled( Task ) end - function Task.onenterFailed( Task, From, Event, To ) + function Task.OnEnterFailed( Task, From, Event, To ) self:Failed( Task ) end - function Task.onenterAborted( Task, From, Event, To ) + function Task.OnEnterAborted( Task, From, Event, To ) self:Aborted( Task ) end diff --git a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua index 13cd2c5e7..499efcf89 100644 --- a/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_A2G_Dispatcher.lua @@ -775,15 +775,15 @@ do -- TASK_A2G_DISPATCHER self:Success( Task ) end - function Task.onenterCancelled( Task, From, Event, To ) + function Task.OnEnterCancelled( Task, From, Event, To ) self:Cancelled( Task ) end - function Task.onenterFailed( Task, From, Event, To ) + function Task.OnEnterFailed( Task, From, Event, To ) self:Failed( Task ) end - function Task.onenterAborted( Task, From, Event, To ) + function Task.OnEnterAborted( Task, From, Event, To ) self:Aborted( Task ) end diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index d1473dd4d..e82ac8ba3 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -686,15 +686,15 @@ do -- TASK_CARGO_DISPATCHER self:Success( Task ) end - function Transport.Task.onenterCancelled( Task, From, Event, To ) + function Transport.Task.OnEnterCancelled( Task, From, Event, To ) self:Cancelled( Task ) end - function Transport.Task.onenterFailed( Task, From, Event, To ) + function Transport.Task.OnEnterFailed( Task, From, Event, To ) self:Failed( Task ) end - function Transport.Task.onenterAborted( Task, From, Event, To ) + function Transport.Task.OnEnterAborted( Task, From, Event, To ) self:Aborted( Task ) end end From bd1aec6deba0df12707efade9a1be6529535a1be Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sun, 9 Sep 2018 13:46:12 +0200 Subject: [PATCH 334/420] Warehouse v0.3.9 Fixes and minor adjustments. --- .../Moose/Functional/Warehouse.lua | 64 +++++++++++-------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index dc321d0d7..7bf346cf7 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -987,22 +987,23 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.3.8" +WAREHOUSE.version="0.3.9" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Case when all transports are killed and there is still cargo to be delivered. Put cargo back into warehouse. +-- TODO: Spawn assets only virtually, i.e. remove requested assets from stock but do NOT spawn them ==> Interface to A2A dispatcher! Maybe do a negative sign on asset number? -- TODO: Test capturing a neutral warehouse. --- TODO: Make more examples: ARTY, CAP, --- TODO: Add SAMs and UAVs to generalized attributes. +-- TODO: Make more examples: ARTY, CAP, ... -- TODO: Check also general requests like all ground. Is this a problem for self propelled if immobile units are among the assets? Check if transport. -- TODO: Add transport units from dispatchers back to warehouse stock once they completed their mission. --- TODO: Added habours as interface for transport to from warehouses? --- TODO: Write documentation. -- TODO: Handle the case when units of a group die during the transfer. +-- TODO: Added habours as interface for transport to from warehouses? -- TODO: Add save/load capability of warehouse <==> percistance after mission restart. Difficult in lua! +-- DONE: Write documentation. +-- DONE: Add AAA, SAMs and UAVs to generalized attributes. -- DONE: Add warehouse quantity enumerator. -- DONE: Test mortars. Immobile units need a transport. -- DONE: Set ROE for spawned groups. @@ -1095,7 +1096,7 @@ function WAREHOUSE:New(warehouse, alias) -- From State --> Event --> To State self:AddTransition("NotReadyYet", "Load", "Loaded") -- TODO Load the warehouse state. No sure if it should be in stopped state. self:AddTransition("NotReadyYet", "Start", "Running") -- Start the warehouse from scratch. - self:AddTransition("Loaded", "Start", "Running") -- Start the warehouse when loaded from disk. + self:AddTransition("Loaded", "Start", "Running") -- TODO Start the warehouse when loaded from disk. self:AddTransition("*", "Status", "*") -- Status update. self:AddTransition("*", "AddAsset", "*") -- Add asset to warehouse stock. self:AddTransition("*", "AddRequest", "*") -- New request from other warehouse. @@ -1140,7 +1141,7 @@ function WAREHOUSE:New(warehouse, alias) -- @param #number delay Delay in seconds. --- Triggers the FSM event "Pause". Pauses the warehouse. Assets can still be added and requests be made. However, requests are not processed. - -- @function [parent=#WAREHOUSE] Pauses + -- @function [parent=#WAREHOUSE] Pause -- @param #WAREHOUSE self --- Triggers the FSM event "Pause" after a delay. Pauses the warehouse. Assets can still be added and requests be made. However, requests are not processed. @@ -2065,7 +2066,7 @@ function WAREHOUSE:onafterStatus(From, Event, To) end -- Call status again in ~30 sec (user choice). - self:__Status(self.dTstatus) + self:__Status(-self.dTstatus) end --- Count alive and filter dead groups. @@ -2088,7 +2089,10 @@ function WAREHOUSE:_FilterDead(groupset) end end - self:E(string.format("FF FilterDead: Alive=%d, Dead=%d", nalive, #dead)) + -- TODO: Since the cargo groups are de- and re-spawned, does the counting for cargo groups actually work, when they are transported? + + -- Debug info. + self:T(self.wid..string.format("FilterDead: Alive=%d, Dead=%d", nalive, #dead)) -- Remove dead groups local NoTriggerEvent=false @@ -2294,6 +2298,8 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu end + -- Update status. + self:__Status(-1) end --- Find an asset in the the global warehouse db. @@ -2788,11 +2794,6 @@ function WAREHOUSE:onafterAddRequest(From, Event, To, warehouse, AssetDescriptor nTransport=1 end end - - -- Not more transports than assets. - --if type(nAsset)=="number" then - -- nTransport=math.min(nAsset, nTransport) - --end -- Self request? local toself=false @@ -2824,7 +2825,13 @@ function WAREHOUSE:onafterAddRequest(From, Event, To, warehouse, AssetDescriptor -- Add request to queue. table.insert(self.queue, request) + + local text=string.format("Warehouse %s: New request from %s. Descriptor %s=%s, #assets=%s; Transport=%s, #transports =%s.", + self.alias, warehouse.alias, request.assetdesc, tostring(request.assetdescval), tostring(request.nasset), request.transporttype, tostring(request.ntransport)) + self:_DebugMessage(text, 5) + -- Update status + self:__Status(-1) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -4650,18 +4657,14 @@ end -- @param #WAREHOUSE.Queueitem request The request to be checked. -- @return #boolean If true, request can be executed. If false, something is not right. function WAREHOUSE:_CheckRequestNow(request) - - -- Assume request is okay and check scenarios. - local okay=true - + -- Check if receiving warehouse is running. We do allow self requests if the warehouse is under attack though! - if (not request.warehouse:IsRunning()) and (not request.toself and self:IsAttacked()) then + if (request.warehouse:IsRunning()==false) and not (request.toself and self:IsAttacked()) then local text=string.format("Warehouse %s: Request denied! Receiving warehouse %s is not running. Current state %s.", self.alias, request.warehouse.alias, request.warehouse:GetState()) self:_InfoMessage(text, 5) return false end - -- If no transport is requested, assets need to be mobile unless it is a self request. local onlymobile=false if request.ntransport==0 and not request.toself then @@ -4706,11 +4709,13 @@ function WAREHOUSE:_CheckRequestNow(request) request.cargoattribute=_assetattribute request.cargocategory=_assetcategory - env.info("FF selected cargo assets") - for _,_asset in pairs(_assets) do + -- Debug info: + local text=string.format("Selected cargo assets, attibute=%s, category=%d:\n", request.cargoattribute, request.cargocategory) + for _i,_asset in pairs(_assets) do local asset=_asset --#WAREHOUSE.Assetitem - env.info(string.format("- name = %s", asset.templatename)) + text=text..string.format("%d) asset name=%s, type=%s, category=%d, #units=%d",_i, asset.templatename, asset.unittype, asset.category, asset.nunits) end + self:T(self.wid..text) end @@ -4742,6 +4747,14 @@ function WAREHOUSE:_CheckRequestNow(request) request.transportassets=_transports request.transportattribute=_transportattribute request.transportcategory=_transportcategory + + -- Debug info: + local text=string.format("Selected transport assets, attibute=%s, category=%d:\n", request.transportattribute, request.transportcategory) + for _i,_asset in pairs(_assets) do + local asset=_asset --#WAREHOUSE.Assetitem + text=text..string.format("%d) asset name=%s, type=%s, category=%d, #units=%d",_i, asset.templatename, asset.unittype, asset.category, asset.nunits) + end + self:T(self.wid..text) else @@ -5641,9 +5654,9 @@ function WAREHOUSE:_DisplayStatus() end local text=string.format("\n------------------------------------------------------\n") - text=text..string.format("Warehouse %s status:\n", self.alias) + text=text..string.format("Warehouse %s status: %s\n", self.alias, self:GetState()) text=text..string.format("------------------------------------------------------\n") - text=text..string.format("Current status = %s\n", self:GetState()) + --text=text..string.format("Current status = %s\n", ) text=text..string.format("Coalition side = %d\n", self.coalition) text=text..string.format("Country name = %d\n", self.country) text=text..string.format("Airbase name = %s\n", airbasename) @@ -5652,7 +5665,6 @@ function WAREHOUSE:_DisplayStatus() text=text..string.format("------------------------------------------------------\n") text=text..self:_GetStockAssetsText() self:T(text) - --TODO: number of ground, air, naval assets. end --- Get text about warehouse stock. From 5c5b9df4703f0226d2e278768523f75d614474dc Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sun, 9 Sep 2018 18:44:04 +0200 Subject: [PATCH 335/420] Fixing problem with AI cargo helicopter pickup --- Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua | 4 +++- Moose Development/Moose/AI/AI_Cargo_Helicopter.lua | 14 +++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index eb448955f..4b40ae379 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -903,7 +903,8 @@ function AI_CARGO_DISPATCHER:onafterMonitor() if not self.PickupZoneSet or PickupZone then for CarrierPickup, Coordinate in pairs( self.PickupCargo ) do if CarrierPickup:IsAlive() == true then - if CargoCoordinate:Get2DDistance( Coordinate ) <= 100 then + if CargoCoordinate:Get2DDistance( Coordinate ) <= 25 then + self:F( { "Coordinate not free for ", Cargo = Cargo:GetName(), Carrier:GetName(), PickupCargo = self.PickupCargo[Carrier] ~= nil } ) CoordinateFree = false break end @@ -1025,6 +1026,7 @@ function AI_CARGO_DISPATCHER:onafterTransport( From, Event, To, Carrier, Cargo ) end end + self:F( { Carrier = Carrier:GetName(), PickupCargo = self.PickupCargo } ) self.PickupCargo[Carrier] = nil end diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 019840b32..199065f6d 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -254,7 +254,6 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To ) --self:Load( Helicopter:GetPointVec2() ) self:Load( self.PickupZone ) self.RoutePickup = false - self.Relocating = true end end @@ -262,8 +261,6 @@ function AI_CARGO_HELICOPTER:onafterLanded( Helicopter, From, Event, To ) if Helicopter:GetHeight( true ) <= 5 and Helicopter:GetVelocityKMH() < 10 then self:Unload( self.DeployZone ) self.RouteDeploy = false - self.Transporting = false - self.Relocating = false end end @@ -410,6 +407,7 @@ function AI_CARGO_HELICOPTER:onafterPickedUp( Helicopter, From, Event, To, Picku self:F( { Helicopter, From, Event, To } ) if Helicopter and Helicopter:IsAlive() then + self.Relocating = false self.Transporting = true end end @@ -437,6 +435,8 @@ function AI_CARGO_HELICOPTER:onafterDeployed( Helicopter, From, Event, To, Deplo end, Helicopter ) + self.Transporting = false + end --- On after Pickup event. @@ -500,7 +500,9 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin Helicopter:Route( Route, 1 ) self.PickupZone = PickupZone - self.Transporting = true + + self.Relocating = true + self.Transporting = false end end @@ -576,7 +578,9 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin -- Now route the helicopter Helicopter:Route( Route, 0 ) - + + self.Relocating = false + self.Transporting = true end end From 72538597adf043af8a6f5427347eddb9994204c0 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sun, 9 Sep 2018 19:45:49 +0200 Subject: [PATCH 336/420] Fixing additional problems with AI cargo helicopters --- Moose Development/Moose/AI/AI_Cargo.lua | 6 +- .../Moose/AI/AI_Cargo_Helicopter.lua | 69 ++++++++++--------- 2 files changed, 41 insertions(+), 34 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua index 885d717de..2da2566e7 100644 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -179,7 +179,7 @@ function AI_CARGO:onbeforeLoad( Carrier, From, Event, To, PickupZone ) local Boarding = false local LoadInterval = 10 - local LoadDelay = 10 + local LoadDelay = 0 local Carrier_List = {} local Carrier_Weight = {} @@ -246,7 +246,8 @@ function AI_CARGO:onbeforeLoad( Carrier, From, Event, To, PickupZone ) end if not Loaded then - -- If the cargo wasn't loaded in one of the carriers, then we need to stop the loading. + -- No loading happened, so we need to pickup something else. + self.Relocating = false end end @@ -344,6 +345,7 @@ function AI_CARGO:onafterUnload( Carrier, From, Event, To, DeployZone ) local CarrierUnit = CarrierUnit -- Wrapper.Unit#UNIT Carrier:RouteStop() for _, Cargo in pairs( CarrierUnit:GetCargo() ) do + self:F( { Cargo = Cargo:GetName(), Isloaded = Cargo:IsLoaded() } ) if Cargo:IsLoaded() then Cargo:__UnBoard( UnboardDelay ) UnboardDelay = UnboardDelay + UnboardInterval diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 199065f6d..284000275 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -358,37 +358,35 @@ function AI_CARGO_HELICOPTER:onafterOrbit( Helicopter, From, Event, To, Coordina if Helicopter and Helicopter:IsAlive() then - if not self:IsTransporting() then - local Route = {} - - -- local CoordinateFrom = Helicopter:GetCoordinate() - -- local WaypointFrom = CoordinateFrom:WaypointAir( - -- "RADIO", - -- POINT_VEC3.RoutePointType.TurningPoint, - -- POINT_VEC3.RoutePointAction.TurningPoint, - -- Speed, - -- true - -- ) - -- Route[#Route+1] = WaypointFrom - local CoordinateTo = Coordinate - local WaypointTo = CoordinateTo:WaypointAir( - "RADIO", - POINT_VEC3.RoutePointType.TurningPoint, - POINT_VEC3.RoutePointAction.TurningPoint, - 50, - true - ) - Route[#Route+1] = WaypointTo - - local Tasks = {} - Tasks[#Tasks+1] = Helicopter:TaskOrbitCircle( math.random( 30, 80 ), 150, CoordinateTo:GetRandomCoordinateInRadius( 800, 500 ) ) - Route[#Route].task = Helicopter:TaskCombo( Tasks ) - - Route[#Route+1] = WaypointTo - - -- Now route the helicopter - Helicopter:Route( Route, 0 ) - end + local Route = {} + +-- local CoordinateFrom = Helicopter:GetCoordinate() +-- local WaypointFrom = CoordinateFrom:WaypointAir( +-- "RADIO", +-- POINT_VEC3.RoutePointType.TurningPoint, +-- POINT_VEC3.RoutePointAction.TurningPoint, +-- Speed, +-- true +-- ) +-- Route[#Route+1] = WaypointFrom + local CoordinateTo = Coordinate + local WaypointTo = CoordinateTo:WaypointAir( + "RADIO", + POINT_VEC3.RoutePointType.TurningPoint, + POINT_VEC3.RoutePointAction.TurningPoint, + 50, + true + ) + Route[#Route+1] = WaypointTo + + local Tasks = {} + Tasks[#Tasks+1] = Helicopter:TaskOrbitCircle( math.random( 30, 80 ), 150, CoordinateTo:GetRandomCoordinateInRadius( 800, 500 ) ) + Route[#Route].task = Helicopter:TaskCombo( Tasks ) + + Route[#Route+1] = WaypointTo + + -- Now route the helicopter + Helicopter:Route( Route, 0 ) end end @@ -406,9 +404,16 @@ end function AI_CARGO_HELICOPTER:onafterPickedUp( Helicopter, From, Event, To, PickupZone ) self:F( { Helicopter, From, Event, To } ) + local HasCargo = false if Helicopter and Helicopter:IsAlive() then + for Cargo, CarrierUnit in pairs( self.Carrier_Cargo ) do + HasCargo = true + break + end self.Relocating = false - self.Transporting = true + if HasCargo then + self.Transporting = true + end end end From 53ac9ca5006d3980090dc385a93e934ae9cd1273 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sun, 9 Sep 2018 22:04:31 +0200 Subject: [PATCH 337/420] Warehouse v0.4.0 Added some onevent functions for AI_CARGO Added start command for CONTROLLER --- .../Moose/Functional/Warehouse.lua | 31 +++++++++++++++++-- .../Moose/Wrapper/Controllable.lua | 8 +++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 7bf346cf7..dbb2c19a4 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -987,7 +987,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.3.9" +WAREHOUSE.version="0.4.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -3189,8 +3189,32 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) self:E(self.wid.."ERROR: Unknown transporttype!") end + -------------------------------- + -- Dispatcher Event Functions -- + -------------------------------- + + function CargoTransport:OnAfterLoaded(From,Event,To,CarrierGroup,Cargo,CarrierUnit,PickupZone) + local text=string.format("FF Carrier group %s loaded cargo %s into unit %s in pickup zone %s", CarrierGroup:GetName(), Cargo:GetObject():GetName(), CarrierUnit:GetName(), PickupZone:GetName()) + env.info(text) + end + + function CargoTransport:OnAfterPickedUp(From,Event,To,CarrierGroup,PickupZone) + local text=string.format("FF Carrier group %s picked up event in pickup zone %s.", CarrierGroup:GetName(), PickupZone:GetName()) + env.info(text) + end + + function CargoTransport:OnAfterDeployed(From,Event,To,CarrierGroup,DeployZone) + local text=string.format("FF Carrier group %s deployed event for deploy zone %s.", CarrierGroup:GetName(), DeployZone:GetName()) + env.info(text) + end + + function CargoTransport:OnAfterHome(From,Event,To,CarrierGroup,Coordinate,Speed,HomeZone) + local text=string.format("FF Carrier group %s going home to zone %s.", CarrierGroup:GetName(), HomeZone:GetName()) + env.info(text) + end + --- Function called when cargo has arrived and was unloaded. - function CargoTransport:OnAfterUnloaded(From, Event, To, Carrier, Cargo) + function CargoTransport:OnAfterUnloaded(From, Event, To, Carrier, Cargo, CarrierUnit, DeployZone) self:I("FF OnAfterUnloaded:") self:I({From=From}) @@ -3923,7 +3947,8 @@ function WAREHOUSE:_RouteAir(aircraft) self:T2(self.wid..string.format("RouteAir aircraft group %s alive=%s", aircraft:GetName(), tostring(aircraft:IsAlive()))) -- Give start command to activate uncontrolled aircraft. - aircraft:SetCommand({id='Start', params={}}) + --aircraft:SetCommand({id='Start', params={}}) + aircraft:StartUncontrolled() -- Debug info. self:T2(self.wid..string.format("RouteAir aircraft group %s alive=%s (after start command)", aircraft:GetName(), tostring(aircraft:IsAlive()))) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 97529a6fa..92aed3843 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -611,6 +611,14 @@ function CONTROLLABLE:CommandStopRoute( StopRoute ) end +--- Give an uncontrolled air controllable the start command. +-- @param #CONTROLLABLE self +-- @return #CONTROLLABLE self +function CONTROLLABLE:StartUncontrolled() + self:SetCommand({id='Start', params={}}) + return self +end + -- TASKS FOR AIR CONTROLLABLES From cc9b695b68185f54e8b1ee64b343cbbf09165af0 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sun, 9 Sep 2018 23:57:17 +0200 Subject: [PATCH 338/420] Warehouse v0.4.1 Improved JobDone function. Fixed bugs. --- .../Moose/Functional/Warehouse.lua | 55 ++++++++----------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index dbb2c19a4..e9cb77b8c 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -987,7 +987,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.4.0" +WAREHOUSE.version="0.4.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -2114,10 +2114,18 @@ function WAREHOUSE:_JobDone() local request=request --#WAREHOUSE.Pendingitem -- Count number of cargo groups still alive and filter out dead groups. - local ncargo=self:_FilterDead(request.cargogroupset) + local ncargo=0 + if request.cargogroupset then + ncargo=request.cargogroupset:Count() + end + --=self:_FilterDead(request.cargogroupset) -- Count number of transport groups (if any) and filter out dead groups. Dead groups are removed from the set. - local ntransport=self:_FilterDead(request.transportgroupset) + local ntransport=0 + if request.transportgroupset then + request.transportgroupset:Count() + end + --self:_FilterDead(request.transportgroupset) if ncargo==0 and ntransport==0 then @@ -2293,7 +2301,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu -- Destroy group if it is alive. if group:IsAlive()==true then self:_DebugMessage(string.format("Destroying group %s.", group:GetName()), 5) - group:Destroy() + group:Destroy(true) end end @@ -3010,7 +3018,8 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then _loadradius=10000 elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then - _loadradius=1000 + --_loadradius=1000 + _loadradius=nil elseif Request.transporttype==WAREHOUSE.TransportType.APC then _loadradius=1000 end @@ -3232,9 +3241,8 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) local text=string.format("FF Group %s was unloaded from carrier %s.", tostring(group:GetName()), tostring(Carrier:GetName())) env.info(text) - -- Load the cargo in the warehouse. - Cargo:Load(warehouse.warehouse) + --Cargo:Load(warehouse.warehouse) -- Trigger Arrived event. warehouse:Arrived(group) @@ -3501,7 +3509,7 @@ function WAREHOUSE:_UpdatePending(group) if caid==aid then request.transportgroupset:Remove(transportgroup:GetName()) request.ntransporthome=request.ntransporthome+1 - env.info("FF transport back home # "..request.ntransporthome) + env.info(string.format("Transport back home #%s", request.ntransporthome)) isCargo=false break end @@ -3520,7 +3528,7 @@ function WAREHOUSE:_UpdatePending(group) if caid==aid then request.cargogroupset:Remove(cargogroup:GetName()) request.ndelivered=request.ndelivered+1 - env.info("FF delivered cargo # "..request.ndelivered) + env.info(string.format("FF delivered cargo # ", request.ndelivered)) isCargo=true break end @@ -3856,12 +3864,9 @@ function WAREHOUSE:_RouteGround(group, request) table.insert(Waypoints, #Waypoints+1, ToWP) end - - -- Task function triggering the arrived event. - --local TaskFunction = group:TaskFunction("WAREHOUSE._Arrived", self) -- Task function triggering the arrived event at the last waypoint. - local TaskFunction = self:_SimpleTaskFunction("warehouse:_ArrivedSimple", group) + local TaskFunction = self:_SimpleTaskFunction("warehouse:_Arrived", group) -- Put task function on last waypoint. local Waypoint = Waypoints[#Waypoints] @@ -3893,7 +3898,7 @@ function WAREHOUSE:_RouteNaval(group, request) -- Get off road path to remote warehouse. If more have been defined, pick one randomly. local remotename=request.warehouse.warehouse:GetName() - local path=self.shippinglanes[remotename][math.random(#self.ship[remotename])] + local lane=self.shippinglanes[remotename][math.random(#self.ship[remotename])] if lane then @@ -3914,7 +3919,7 @@ function WAREHOUSE:_RouteNaval(group, request) end -- Task function triggering the arrived event at the last waypoint. - local TaskFunction = self:_SimpleTaskFunction("warehouse:_ArrivedSimple", group) + local TaskFunction = self:_SimpleTaskFunction("warehouse:_Arrived", group) -- Put task function on last waypoint. local Waypoint = Waypoints[#Waypoints] @@ -3976,8 +3981,8 @@ function WAREHOUSE:_RouteTrain(Group, Coordinate, Speed) -- Create a local Waypoints = Group:TaskGroundOnRailRoads(Coordinate, Speed) - -- Task function triggering the arrived event. - local TaskFunction = Group:TaskFunction("WAREHOUSE._Arrived", self) + -- Task function triggering the arrived event at the last waypoint. + local TaskFunction = self:_SimpleTaskFunction("warehouse:_Arrived", Group) -- Put task function on last waypoint. local Waypoint = Waypoints[#Waypoints] @@ -3989,21 +3994,10 @@ function WAREHOUSE:_RouteTrain(Group, Coordinate, Speed) end --- Task function for last waypoint. Triggering the "Arrived" event. --- @param Wrapper.Group#GROUP group The group that arrived. --- @param #WAREHOUSE warehouse Warehouse self. -function WAREHOUSE._Arrived(group, warehouse) - env.info(warehouse.wid..string.format("Group %s arrived at destination.", tostring(group:GetName()))) - - --Trigger "Arrived" event. - warehouse:__Arrived(1, group) - -end - ---- Simple task function for last waypoint. Triggering the "Arrived" event. -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group The group that arrived. -function WAREHOUSE:_ArrivedSimple(group) - self:_DebugMessage(string.format("Group %s arrived (simple)!", tostring(group:GetName()))) +function WAREHOUSE:_Arrived(group) + self:_DebugMessage(string.format("Group %s arrived!", tostring(group:GetName()))) if group then --Trigger "Arrived event. @@ -4143,7 +4137,6 @@ function WAREHOUSE:_OnEventLanding(EventData) -- Group arrived. self:Arrived(group) - --self:__AddAsset(30, group) end end From 85505f3febbbb7ebf1ea183de9713317723ab942 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 10 Sep 2018 16:32:10 +0200 Subject: [PATCH 339/420] Warehouse v0.4.1w --- .../Moose/Functional/Warehouse.lua | 264 +++++++++++++----- 1 file changed, 193 insertions(+), 71 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index e9cb77b8c..3af2025c4 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -1,4 +1,4 @@ ---- **Functional** - (R2.5) - Simulation of logistics. +--- **Functional** - (R2.5) - Simulation of logistic operations. -- -- The MOOSE warehouse concept simulates the organization and implementation of complex operations regarding the flow of assets between the point of origin and the point of consumption -- in order to meet requirements of a potential conflict. In particular, this class is concerned with maintaining army supply lines while disrupting those of the enemy, since an armed @@ -136,14 +136,14 @@ -- Assets can be added to the warehouse stock by using the @{#WAREHOUSE.AddAsset}(*group*, *ngroups*, *forceattribute*) function. The parameter *group* has to be a MOOSE @{Wrapper.Group#GROUP}. -- The parameter *ngroups* specifies how many clones of this group are added to the stock. -- --- Note that the group should be a late activated template group, which was defined in the mission editor. -- -- infrantry=GROUP:FindByName("Some Infantry Group") -- warehouse:AddAsset(infantry, 5) -- --- This will add five infantry groups to the warehouse stock. +-- This will add five infantry groups to the warehouse stock. Note that the group will normally be a late activated template group, +-- which was defined in the mission editor. But you can also add other groups which are already spawned and present in the mission. -- --- Note that you can also add assets with a delay by using the @{#WAREHOUSE.__AddAsset}(*delay*, *group*, *ngroups*, *foceattribute*), where *delay* is the delay in seconds before the asset is added. +-- You can add assets with a delay by using the @{#WAREHOUSE.__AddAsset}(*delay*, *group*, *ngroups*, *foceattribute*), where *delay* is the delay in seconds before the asset is added. -- -- By default, the generalized attribute of the asset is determined automatically from the DCS descriptor attributes. However, this might not always result in the desired outcome. -- Therefore, it is possible, to force a generalized attribute for the asset with the third optional parameter *forceattribute*, which is of type @{#WAREHOUSE.Attribute}. @@ -233,10 +233,11 @@ -- -- end -- --- The variable *groupset* is a @{Core.Set#SET_GOUP} object and holds all asset groups from the request. The code above shows, how the mission designer can access the groups +-- The variable *groupset* is a @{Core.Set#SET_GROUP} object and holds all asset groups from the request. The code above shows, how the mission designer can access the groups -- for further tasking. Here, the groups are only smoked but, of course, you can use them for whatever task you fancy. -- -- Note that airborne groups are spawned in uncontrolled state and need to be activated first before they can start their assigned mission. +-- This can be done with the @{Wrapper.Controllable#CONTROLLABLE.StartUncontrolled} function. -- -- === -- @@ -369,6 +370,7 @@ -- -- A warehouse has a limited speed to process requests. Each time the status of the warehouse is updated only one requests is processed. -- The time interval between status updates is 30 seconds by default and can be adjusted via the @{#WAREHOUSE.SetStatusUpdate}(*interval*) function. +-- However, the status is also updated on other occasions, e.g. when a new request was added. -- -- === -- @@ -872,7 +874,9 @@ WAREHOUSE = { -- @field #number timestamp Absolute mission time in seconds when the request was processed. -- @field Core.Set#SET_GROUP cargogroupset Set of cargo groups do be delivered. -- @field #number ndelivered Number of groups delivered to destination. --- @field Core.Set#SET_GROUP transportgroupset Set of cargo transport groups. +-- @field Core.Set#SET_GROUP transportgroupset Set of cargo transport carrier groups. +-- @field Core.Set#SET_CARGO transportcargoset Set of cargo objects. +-- @field #table carriercargo Table holding the cargo groups of each carrier unit. -- @field #number ntransporthome Number of transports back home. -- @extends #WAREHOUSE.Queueitem @@ -987,7 +991,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.4.1" +WAREHOUSE.version="0.4.1w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -1107,8 +1111,8 @@ function WAREHOUSE:New(warehouse, alias) self:AddTransition("*", "Delivered", "*") -- All cargo groups of a request have been delivered to the requesting warehouse. self:AddTransition("Running", "SelfRequest", "*") -- Request to warehouse itself. Requested assets are only spawned but not delivered anywhere. self:AddTransition("Attacked", "SelfRequest", "*") -- Request to warehouse itself. Also possible when warehouse is under attack! - self:AddTransition("Running", "Pause", "Paused") -- TODO Pause the processing of new requests. Still possible to add assets and requests. - self:AddTransition("Paused", "Unpause", "Running") -- TODO Unpause the warehouse. Queued requests are processed again. + self:AddTransition("Running", "Pause", "Paused") -- DONE Pause the processing of new requests. Still possible to add assets and requests. + self:AddTransition("Paused", "Unpause", "Running") -- DONE Unpause the warehouse. Queued requests are processed again. self:AddTransition("*", "Stop", "Stopped") -- DONE Stop the warehouse. self:AddTransition("*", "Save", "*") -- TODO Save the warehouse state to disk. self:AddTransition("*", "Attacked", "Attacked") -- DONE Warehouse is under attack by enemy coalition. @@ -2069,40 +2073,6 @@ function WAREHOUSE:onafterStatus(From, Event, To) self:__Status(-self.dTstatus) end ---- Count alive and filter dead groups. --- @param #WAREHOUSE self --- @param Core.Set#SET_GROUP groupset Set of groups. Dead groups are removed from the set. --- @return #number Number of alive groups. Returns zero if groupset is nil. -function WAREHOUSE:_FilterDead(groupset) - - local nalive=0 - - if groupset then - - -- Check if groups are still alive - local dead={} - for _,group in pairs(groupset:GetSetObjects()) do - if group and group:IsAlive() then - nalive=nalive+1 - else - table.insert(dead, group) - end - end - - -- TODO: Since the cargo groups are de- and re-spawned, does the counting for cargo groups actually work, when they are transported? - - -- Debug info. - self:T(self.wid..string.format("FilterDead: Alive=%d, Dead=%d", nalive, #dead)) - - -- Remove dead groups - local NoTriggerEvent=false - for _,group in pairs(dead) do - groupset:Remove(group, NoTriggerEvent) - end - end - - return nalive -end --- Function that checks if a pending job is done and can be removed from queue -- @param #WAREHOUSE self @@ -2113,20 +2083,17 @@ function WAREHOUSE:_JobDone() for _,request in pairs(self.pending) do local request=request --#WAREHOUSE.Pendingitem - -- Count number of cargo groups still alive and filter out dead groups. + -- Count number of cargo groups. local ncargo=0 if request.cargogroupset then ncargo=request.cargogroupset:Count() end - --=self:_FilterDead(request.cargogroupset) - -- Count number of transport groups (if any) and filter out dead groups. Dead groups are removed from the set. + -- Count number of transport groups (if any). local ntransport=0 if request.transportgroupset then request.transportgroupset:Count() - end - --self:_FilterDead(request.transportgroupset) - + end if ncargo==0 and ntransport==0 then @@ -3091,21 +3058,21 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) elseif Request.transporttype==WAREHOUSE.TransportType.TRAIN then - self:E(self.wid.."ERROR: cargo transport by train not supported yet!") + self:_ErrorMessage("ERROR: Cargo transport by train not supported yet!") return elseif Request.transporttype==WAREHOUSE.TransportType.SHIP then - self:E(self.wid.."ERROR: cargo transport by ship not supported yet!") + self:_ErrorMessage("ERROR: Cargo transport by ship not supported yet!") return elseif Request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then - self:E(self.wid.."ERROR: transport type selfpropelled was already handled above. We should not get here!") + self:_ErrorMessage("ERROR: Transport type selfpropelled was already handled above. We should not get here!") return else - self:E(self.wid.."ERROR: unknown transport type!") + self:_ErrorMessage("ERROR: Unknown transport type!") return end @@ -3130,8 +3097,19 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) self:_DeleteStockItem(_item) end - -- Add cargo groups to request. + -- Add transport groups, i.e. the carriers to request. Pending.transportgroupset=TransportSet + + -- Add cargo group set. + Pending.transportcargoset=CargoGroups + + -- Create empty tables which will be filled with the cargo groups of each carrier unit. Needed in case a carrier unit dies. + Pending.carriercargo={} + for _,carriergroup in pairs(TransportSet:GetObject()) do + for _,carrierunit in pairs(carriergroup:GetUnits()) do + Pending.carriercargo[carrierunit:GetName()]={} + end + end -- Add request to pending queue. table.insert(self.pending, Pending) @@ -3202,11 +3180,6 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Dispatcher Event Functions -- -------------------------------- - function CargoTransport:OnAfterLoaded(From,Event,To,CarrierGroup,Cargo,CarrierUnit,PickupZone) - local text=string.format("FF Carrier group %s loaded cargo %s into unit %s in pickup zone %s", CarrierGroup:GetName(), Cargo:GetObject():GetName(), CarrierUnit:GetName(), PickupZone:GetName()) - env.info(text) - end - function CargoTransport:OnAfterPickedUp(From,Event,To,CarrierGroup,PickupZone) local text=string.format("FF Carrier group %s picked up event in pickup zone %s.", CarrierGroup:GetName(), PickupZone:GetName()) env.info(text) @@ -3222,6 +3195,28 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) env.info(text) end + --- Function called when a carrier unit has loaded a cargo group. + function CargoTransport:OnAfterLoaded(From, Event, To, Carrier, Cargo, CarrierUnit, PickupZone) + local text=string.format("FF Carrier group %s loaded cargo %s into unit %s in pickup zone %s", CarrierGroup:GetName(), Cargo:GetObject():GetName(), CarrierUnit:GetName(), PickupZone:GetName()) + env.info(text) + + -- Get group object. + local group=Cargo:GetObject() --Wrapper.Group#GROUP + + -- Get warehouse state. + local warehouse=Carrier:GetState(Carrier, "WAREHOUSE") --#WAREHOUSE + + local text=string.format("FF Group %s was loaded into carrier %s.", tostring(group:GetName()), tostring(Carrier:GetName())) + env.info(text) + + -- Get request. + local request=warehouse:_GetRequestOfGroup(group, warehouse.pending) + + -- Add cargo group to this carrier. + table.insert(request.carriercargo[CarrierUnit:GetName()], group) + + end + --- Function called when cargo has arrived and was unloaded. function CargoTransport:OnAfterUnloaded(From, Event, To, Carrier, Cargo, CarrierUnit, DeployZone) @@ -4182,32 +4177,160 @@ function WAREHOUSE:_OnEventCrashOrDead(EventData) -- Check if an asset unit was destroyed. if EventData and EventData.IniGroup then + local group=EventData.IniGroup + + -- Get warehouse, asset and request IDs from the group name. local wid,aid,rid=self:_GetIDsFromGroup(group) + if wid==self.uid then self:T(self.wid..string.format("Warehouse %s captured event dead or crash of its asset unit %s.", self.alias, EventData.IniUnitName)) + -- Loop over all pending requests and get the one belonging to this unit. for _,request in pairs(self.pending) do local request=request --#WAREHOUSE.Pendingitem if request.uid==rid then - -- Count number of cargo groups still alive and filter out dead groups. Dead groups are removed from the set. - local ncargo=self:_FilterDead(request.cargogroupset) - - -- Count number of transport groups (if any) and filter out dead groups. Dead groups are removed from the set. - local ntransport=self:_FilterDead(request.transportgroupset) - - self:T(self.wid..string.format("Asset groups left for request id=%d: ncargo=%d, ntransport=%d", rid, ncargo, ntransport)) + + -- Update cargo and transport group sets of this request. We need to know if this job is finished. + self:_UnitDead(EventData.IniUnit, request) + + --[[ + if self:_GroupIsTransport(group, request) then + -- Count number of transport groups (if any) and filter out dead groups. Dead groups are removed from the set. + -- TODO: Now, what about the cargo groups that are inside the transport?! Need to save in which carrier the cargo is? + self:_FilterDead(request.transportgroupset) + else + -- Count number of cargo groups still alive and filter out dead groups. Dead groups are removed from the set. + self:_FilterDead(request.cargogroupset) + end + ]] end - end - - + end end end - end +--- A unit of a group just died. Update group sets in request. +-- This is important in order to determine if a job is done and can be removed from the (pending) queue. +-- @param #WAREHOUSE self +-- @param Wrapper.Unit#UNIT deadunit Unit that died. +-- @param #WAREHOUSE.Pendingitem request Request that needs to be updated. +function WAREHOUSE:_UnitDead(deadunit, request) + + -- Group the dead unit belongs to. + local group=deadunit:GetGroup() + + -- Check if this was the last unit of the group ==> whole group dead. + local isgroupdead=false + local nunits=0 + if group then + local nunits=group:GetSize() + -- One (or less) units in group. + if nunits<=1 then + isgroupdead=true + end + end + + local text=string.format("Unit %s died! Group %s: #units=%d, IsAlive=%s", deadunit:GetName(), group:GetName(), nunits, tostring(group:IsAlive())) + self:E(self.wid..text) + + local NoTriggerEvent=false + + if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then + + --- + -- Easy case: Group can simply be removed from the cargogroupset. + --- + + -- Remove dead group from carg group set. + if isgroupdead==true then + request.cargogroupset:Remove(group, NoTriggerEvent) + end + + else + + --- + -- Complicated case: Dead unit could be: + -- 1.) A Cargo unit (e.g. waiting to be picked up). + -- 2.) A Transport unit which itself holds cargo groups. + --- + + if self:_GroupIsTransport(group,request) then + + -- Get the carrier unit table holding the cargo groups inside this carrier. + local carrierunit=request.carriercargo[deadunit:GetName()] + + if carrierunit then + + -- Loop over all groups inside the carrier ==> all dead. + for _,cargogroup in pairs(carrierunit) do + + -- TODO: Check if remove really needs the name? Did I not always use the group object?! + --request.cargogroupset:Remove(ObjectName,NoTriggerEvent) + request.cargogroupset:Remove(cargogroup,NoTriggerEvent) + end + + end + + -- Whole carrier group is dead. Remove it from the carrier group set. + if isgroupdead then + request.transportcargoset:Remove(group, NoTriggerEvent) + end + + else + + -- This must have been an alive cargo group that was killed outside the carrier, e.g. waiting to be transported or waiting to be put back. + -- Remove dead group from cargo group set. + if isgroupdead==true then + + request.cargogroupset:Remove(group, NoTriggerEvent) + + -- TODO: This as well? + --request.transportcargoset:RemoveCargosByName(RemoveCargoNames) + end + + end + end +end + +--- Count alive and filter dead groups. +-- @param #WAREHOUSE self +-- @param Core.Set#SET_GROUP groupset Set of groups. Dead groups are removed from the set. +-- @return #number Number of alive groups. Returns zero if groupset is nil. +function WAREHOUSE:_FilterDead(groupset) + + local nalive=0 + + if groupset then + + -- Check if groups are still alive + local dead={} + for _,group in pairs(groupset:GetSetObjects()) do + if group and group:IsAlive() then + nalive=nalive+1 + else + table.insert(dead, group) + end + end + + -- TODO: Since the cargo groups are de- and re-spawned, does the counting for cargo groups actually work, when they are transported? + + -- Debug info. + self:T(self.wid..string.format("FilterDead: Alive=%d, Dead=%d", nalive, #dead)) + + -- Remove dead groups + local NoTriggerEvent=false + for _,group in pairs(dead) do + groupset:Remove(group, NoTriggerEvent) + end + end + + return nalive +end + + --- Warehouse event handling function. -- Handles the case when the airbase associated with the warehous is captured. -- @param #WAREHOUSE self @@ -5674,7 +5797,6 @@ function WAREHOUSE:_DisplayStatus() local text=string.format("\n------------------------------------------------------\n") text=text..string.format("Warehouse %s status: %s\n", self.alias, self:GetState()) text=text..string.format("------------------------------------------------------\n") - --text=text..string.format("Current status = %s\n", ) text=text..string.format("Coalition side = %d\n", self.coalition) text=text..string.format("Country name = %d\n", self.country) text=text..string.format("Airbase name = %s\n", airbasename) From 552718e3039b0a060a2f058315578ce9581d4ac6 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Tue, 11 Sep 2018 00:04:33 +0200 Subject: [PATCH 340/420] Warehouse v0.4.1alpha Not working version. fixed a few bugs. Uncommented check for coordinate in CargoGroup.lua etc. --- Moose Development/Moose/Cargo/CargoGroup.lua | 11 +- .../Moose/Functional/Warehouse.lua | 540 +++++++++--------- 2 files changed, 267 insertions(+), 284 deletions(-) diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 9bb3a8c1b..48378297a 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -661,12 +661,13 @@ do -- CARGO_GROUP else CargoCoordinate = Cargo.CargoObject:GetCoordinate() end - --- if CargoCoordinate then + + -- FF check if coordinate could be obtained. This was commented out for some (unknown) reason. But the check seems valid! + if CargoCoordinate then Distance = Coordinate:Get2DDistance( CargoCoordinate ) --- else --- return false --- end + else + return false + end self:F( { Distance = Distance, LoadRadius = self.LoadRadius } ) if Distance <= self.LoadRadius then diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 3af2025c4..abaa4d385 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -991,7 +991,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.4.1w" +WAREHOUSE.version="0.4.1alpha" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -2082,7 +2082,7 @@ function WAREHOUSE:_JobDone() local done={} for _,request in pairs(self.pending) do local request=request --#WAREHOUSE.Pendingitem - + -- Count number of cargo groups. local ncargo=0 if request.cargogroupset then @@ -2092,7 +2092,7 @@ function WAREHOUSE:_JobDone() -- Count number of transport groups (if any). local ntransport=0 if request.transportgroupset then - request.transportgroupset:Count() + ntransport=request.transportgroupset:Count() end if ncargo==0 and ntransport==0 then @@ -2114,13 +2114,20 @@ function WAREHOUSE:_JobDone() self:_InfoMessage(string.format("Warehouse %s: All transports of request id=%s dead! Putting remaining %s cargo assets back into warehouse!", self.alias, request.uid, ncargo)) -- All transports are dead but there is still cargo left ==> Put cargo back into stock. - for _,group in pairs(request.cargogroupset) do + for _,group in pairs(request.cargogroupset:GetSetObjects()) do local group=group --Wrapper.Group#GROUP - group:SmokeRed() - -- Add all assets back to stock - if group:IsPartlyOrCompletelyInZone(self.spawnzone) then - self:__AddAsset(5, group) + -- Check if group is alive. + if group and group:IsAlive() then + + if self.Debug then + group:SmokeRed() + end + + -- Add all assets back to stock + if group:IsPartlyOrCompletelyInZone(self.spawnzone) then + self:__AddAsset(5, group) + end end end @@ -2142,13 +2149,21 @@ function WAREHOUSE:_JobDone() self:_InfoMessage(string.format("Warehouse %s: No cargo assets left for request id=%s. Putting remaining %s transport assets back into warehouse!", self.alias, request.uid, ntransport)) -- All transports are dead but there is still cargo left ==> Put cargo back into stock. - for _,group in pairs(request.transportgroupset) do + for _,group in pairs(request.transportgroupset:GetSetObjects()) do local group=group --Wrapper.Group#GROUP - group:SmokeRed() - -- Add all assets back to stock - if group:IsPartlyOrCompletelyInZone(self.spawnzone) then - self:__AddAsset(5, group) + -- Check if group is alive. + if group and group:IsAlive() then + + -- Assets back to stock. + if group:IsPartlyOrCompletelyInZone(self.spawnzone) then + + if self.Debug then + group:SmokeRed() + end + + self:__AddAsset(5, group) + end end end @@ -2192,43 +2207,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu -- This is a known asset -- --------------------------- - -- Get the warehouse this group belonged to. (could also be the same for self requests). - local warehouseOld=self:_FindWarehouseInDB(wid) - - -- Now get the request from the pending queue and update it. - - -- Update pending request. Increase ndelivered/ntransporthome and delete group from corresponding group set. - local request, isCargo=warehouseOld:_UpdatePending(group) - - if request then - - -- Number of cargo assets still in group set. - if isCargo==true then - - -- Current size of cargo group set. - local ncargo=request.cargogroupset:Count() - - -- Debug message. - local text=string.format("Cargo %d of %s added to warehouse %s stock. Assets still to deliver %d.", - request.ndelivered, tostring(request.nasset), request.warehouse.alias, ncargo) - self:_DebugMessage(text, 5) - - -- All cargo delivered. - if ncargo==0 then - warehouseOld:Delivered(request) - end - - elseif isCargo==false then - - -- Current size of cargo group set. - local ntransport=request.transportgroupset:Count() - - -- Debug message. - local text=string.format("Transport %d of %s added to warehouse %s stock. Transports still missing %d.", - request.ntransporthome, tostring(request.ntransport), request.warehouse.alias, ntransport) - self:_DebugMessage(text, 5) - - end + if true then -- Get the asset from the global DB. local asset=self:_FindAssetInDB(group) @@ -2277,6 +2256,73 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu self:__Status(-1) end + +--- Update the pending requests by removing assets that have arrived. +-- @param #WAREHOUSE self +-- @param Wrapper.Group#GROUP group The group that has arrived at its destination. +-- @return #WAREHOUSE.Pendingitem The updated request from the pending queue. +-- @return #boolean If true, group is a cargo asset. If false, group is a transport asset. If nil, group is neither cargo nor transport. +function WAREHOUSE:_UpdatePending(group) + + -- Get request from group name. + local request=self:_GetRequestOfGroup(group, self.pending) + + -- Get the IDs for this group. In particular, we use the asset ID to figure out which group was delivered. + local wid,aid,rid=self:_GetIDsFromGroup(group) + + local isCargo=nil + + if request then + + -- If this request was already delivered. + if self.delivered[rid]==true then + + -- Loop over transport groups. + for _,_transportgroup in pairs(request.transportgroupset:GetSetObjects()) do + local transportgroup=_transportgroup --Wrapper.Group#GROUP + + -- IDs of cargo group. + local cwid,caid,crid=self:_GetIDsFromGroup(transportgroup) + + -- Remove group from transport group set and increase home counter. + if caid==aid then + request.transportgroupset:Remove(transportgroup:GetName()) + request.ntransporthome=request.ntransporthome+1 + env.info(string.format("Transport back home #%s", request.ntransporthome)) + isCargo=false + break + end + end + + else + + -- Loop over cargo groups. + for _,_cargogroup in pairs(request.cargogroupset:GetSetObjects()) do + local cargogroup=_cargogroup --Wrapper.Group#GROUP + + -- IDs of cargo group. + local cwid,caid,crid=self:_GetIDsFromGroup(cargogroup) + + -- Remove group from cargo group set and increase delivered counter. + if caid==aid then + request.cargogroupset:Remove(cargogroup:GetName()) + request.ndelivered=request.ndelivered+1 + env.info(string.format("FF delivered cargo # ", request.ndelivered)) + isCargo=true + break + end + end + + end + else + self:E(self.wid..string.format("WARNING: pending request could not be updated since request did not exist in pending queue!")) + end + + return request, isCargo +end + + + --- Find an asset in the the global warehouse db. -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group The group from which it is assumed that it has a registered asset. @@ -3105,7 +3151,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Create empty tables which will be filled with the cargo groups of each carrier unit. Needed in case a carrier unit dies. Pending.carriercargo={} - for _,carriergroup in pairs(TransportSet:GetObject()) do + for _,carriergroup in pairs(TransportSet:GetSetObjects()) do for _,carrierunit in pairs(carriergroup:GetUnits()) do Pending.carriercargo[carrierunit:GetName()]={} end @@ -3190,14 +3236,14 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) env.info(text) end - function CargoTransport:OnAfterHome(From,Event,To,CarrierGroup,Coordinate,Speed,HomeZone) + function CargoTransport:OnAfterHome(From, Event, To, CarrierGroup, Coordinate, Speed, HomeZone) local text=string.format("FF Carrier group %s going home to zone %s.", CarrierGroup:GetName(), HomeZone:GetName()) env.info(text) end --- Function called when a carrier unit has loaded a cargo group. function CargoTransport:OnAfterLoaded(From, Event, To, Carrier, Cargo, CarrierUnit, PickupZone) - local text=string.format("FF Carrier group %s loaded cargo %s into unit %s in pickup zone %s", CarrierGroup:GetName(), Cargo:GetObject():GetName(), CarrierUnit:GetName(), PickupZone:GetName()) + local text=string.format("FF Carrier group %s loaded cargo %s into unit %s in pickup zone %s", Carrier:GetName(), Cargo:GetObject():GetName(), CarrierUnit:GetName(), PickupZone:GetName()) env.info(text) -- Get group object. @@ -3473,69 +3519,7 @@ function WAREHOUSE:_GetAssetFromGroupRequest(group,request) local asset=request.assets[aid] end ---- Update the pending requests by removing assets that have arrived. --- @param #WAREHOUSE self --- @param Wrapper.Group#GROUP group The group that has arrived at its destination. --- @return #WAREHOUSE.Pendingitem The updated request from the pending queue. --- @return #boolean If true, group is a cargo asset. If false, group is a transport asset. If nil, group is neither cargo nor transport. -function WAREHOUSE:_UpdatePending(group) - - -- Get request from group name. - local request=self:_GetRequestOfGroup(group, self.pending) - - -- Get the IDs for this group. In particular, we use the asset ID to figure out which group was delivered. - local wid,aid,rid=self:_GetIDsFromGroup(group) - - local isCargo=nil - - if request then - - -- If this request was already delivered. - if self.delivered[rid]==true then - - -- Loop over transport groups. - for _,_transportgroup in pairs(request.transportgroupset:GetSetObjects()) do - local transportgroup=_transportgroup --Wrapper.Group#GROUP - - -- IDs of cargo group. - local cwid,caid,crid=self:_GetIDsFromGroup(transportgroup) - - -- Remove group from transport group set and increase home counter. - if caid==aid then - request.transportgroupset:Remove(transportgroup:GetName()) - request.ntransporthome=request.ntransporthome+1 - env.info(string.format("Transport back home #%s", request.ntransporthome)) - isCargo=false - break - end - end - - else - - -- Loop over cargo groups. - for _,_cargogroup in pairs(request.cargogroupset:GetSetObjects()) do - local cargogroup=_cargogroup --Wrapper.Group#GROUP - - -- IDs of cargo group. - local cwid,caid,crid=self:_GetIDsFromGroup(cargogroup) - - -- Remove group from cargo group set and increase delivered counter. - if caid==aid then - request.cargogroupset:Remove(cargogroup:GetName()) - request.ndelivered=request.ndelivered+1 - env.info(string.format("FF delivered cargo # ", request.ndelivered)) - isCargo=true - break - end - end - - end - else - self:E(self.wid..string.format("WARNING: pending request could not be updated since request did not exist in pending queue!")) - end - - return request, isCargo -end + --- On after "Delivered" event. Triggered when all asset groups have reached their destination. Corresponding request is deleted from the pending queue. @@ -4005,6 +3989,122 @@ end -- Event handler functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +--- Warehouse event function, handling the birth of a unit. +-- @param #WAREHOUSE self +-- @param Core.Event#EVENTDATA EventData Event data. +function WAREHOUSE:_OnEventBirth(EventData) + self:T3(self.wid..string.format("Warehouse %s (id=%s) captured event birth!", self.alias, self.uid)) + + if EventData and EventData.IniGroup then + local group=EventData.IniGroup + -- Note: Remember, group:IsAlive might(?) not return true here. + local wid,aid,rid=self:_GetIDsFromGroup(group) + if wid==self.uid then + self:T(self.wid..string.format("Warehouse %s captured event birth of its asset unit %s.", self.alias, EventData.IniUnitName)) + else + --self:T3({wid=wid, uid=self.uid, match=(wid==self.uid), tw=type(wid), tu=type(self.uid)}) + end + end +end + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Function handling the event when a (warehouse) unit starts its engines. +-- @param #WAREHOUSE self +-- @param Core.Event#EVENTDATA EventData Event data. +function WAREHOUSE:_OnEventEngineStartup(EventData) + self:T3(self.wid..string.format("Warehouse %s captured event engine startup!",self.alias)) + + if EventData and EventData.IniGroup then + local group=EventData.IniGroup + local wid,aid,rid=self:_GetIDsFromGroup(group) + if wid==self.uid then + self:T(self.wid..string.format("Warehouse %s captured event engine startup of its asset unit %s.", self.alias, EventData.IniUnitName)) + end + end +end + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Function handling the event when a (warehouse) unit takes off. +-- @param #WAREHOUSE self +-- @param Core.Event#EVENTDATA EventData Event data. +function WAREHOUSE:_OnEventTakeOff(EventData) + self:T3(self.wid..string.format("Warehouse %s captured event takeoff!",self.alias)) + + if EventData and EventData.IniGroup then + local group=EventData.IniGroup + local wid,aid,rid=self:_GetIDsFromGroup(group) + if wid==self.uid then + self:T(self.wid..string.format("Warehouse %s captured event takeoff of its asset unit %s.", self.alias, EventData.IniUnitName)) + end + end +end + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Function handling the event when a (warehouse) unit lands. +-- @param #WAREHOUSE self +-- @param Core.Event#EVENTDATA EventData Event data. +function WAREHOUSE:_OnEventLanding(EventData) + self:T3(self.wid..string.format("Warehouse %s captured event landing!", self.alias)) + + if EventData and EventData.IniGroup then + local group=EventData.IniGroup + + -- Try to get UIDs from group name. + local wid,aid,rid=self:_GetIDsFromGroup(group) + + -- Check that this group belongs to this warehouse. + if wid~=nil and wid==self.uid then + + -- Debug info. + self:T(self.wid..string.format("Warehouse %s captured event landing of its asset unit %s.", self.alias, EventData.IniUnitName)) + + -- Check if all cargo was delivered. + if self.delivered[rid]==true then + + -- Check if helicopter landed in spawn zone. If so, we call it a day and add it back to stock. + if group:GetCategory()==Group.Category.HELICOPTER then + if self.spawnzone:IsCoordinateInZone(EventData.IniUnit:GetCoordinate()) then + + -- Debug message. + self:_DebugMessage("Helicopter landed in spawn zone. No pending request. Putting back into stock.") + if self.Debug then + group:SmokeWhite() + end + + -- Group arrived. + self:Arrived(group) + + end + end + + end + + end + end +end + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Function handling the event when a (warehouse) unit shuts down its engines. +-- @param #WAREHOUSE self +-- @param Core.Event#EVENTDATA EventData Event data. +function WAREHOUSE:_OnEventEngineShutdown(EventData) + self:T3(self.wid..string.format("Warehouse %s captured event engine shutdown!", self.alias)) + + if EventData and EventData.IniGroup then + local group=EventData.IniGroup + local wid,aid,rid=self:_GetIDsFromGroup(group) + if wid==self.uid then + self:T(self.wid..string.format("Warehouse %s captured event engine shutdown of its asset unit %s.", self.alias, EventData.IniUnitName)) + end + end +end + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + --- Arrived event if an air unit/group arrived at its destination. This can be an engine shutdown or a landing event. -- @param #WAREHOUSE self -- @param Core.Event#EVENTDATA EventData Event data table. @@ -4051,111 +4151,7 @@ function WAREHOUSE:_OnEventArrived(EventData) end ---- Warehouse event handling function. --- @param #WAREHOUSE self --- @param Core.Event#EVENTDATA EventData Event data. -function WAREHOUSE:_OnEventBirth(EventData) - self:T3(self.wid..string.format("Warehouse %s (id=%s) captured event birth!", self.alias, self.uid)) - - if EventData and EventData.IniGroup then - local group=EventData.IniGroup - -- Note: Remember, group:IsAlive might(?) not return true here. - local wid,aid,rid=self:_GetIDsFromGroup(group) - if wid==self.uid then - self:T(self.wid..string.format("Warehouse %s captured event birth of its asset unit %s.", self.alias, EventData.IniUnitName)) - else - --self:T3({wid=wid, uid=self.uid, match=(wid==self.uid), tw=type(wid), tu=type(self.uid)}) - end - end -end - ---- Warehouse event handling function. --- @param #WAREHOUSE self --- @param Core.Event#EVENTDATA EventData Event data. -function WAREHOUSE:_OnEventEngineStartup(EventData) - self:T3(self.wid..string.format("Warehouse %s captured event engine startup!",self.alias)) - - if EventData and EventData.IniGroup then - local group=EventData.IniGroup - local wid,aid,rid=self:_GetIDsFromGroup(group) - if wid==self.uid then - self:T(self.wid..string.format("Warehouse %s captured event engine startup of its asset unit %s.", self.alias, EventData.IniUnitName)) - end - end -end - ---- Warehouse event handling function. --- @param #WAREHOUSE self --- @param Core.Event#EVENTDATA EventData Event data. -function WAREHOUSE:_OnEventTakeOff(EventData) - self:T3(self.wid..string.format("Warehouse %s captured event takeoff!",self.alias)) - - if EventData and EventData.IniGroup then - local group=EventData.IniGroup - local wid,aid,rid=self:_GetIDsFromGroup(group) - if wid==self.uid then - self:T(self.wid..string.format("Warehouse %s captured event takeoff of its asset unit %s.", self.alias, EventData.IniUnitName)) - end - end -end - ---- Warehouse event handling function. --- @param #WAREHOUSE self --- @param Core.Event#EVENTDATA EventData Event data. -function WAREHOUSE:_OnEventLanding(EventData) - self:T3(self.wid..string.format("Warehouse %s captured event landing!", self.alias)) - - if EventData and EventData.IniGroup then - local group=EventData.IniGroup - - -- Try to get UIDs from group name. - local wid,aid,rid=self:_GetIDsFromGroup(group) - - -- Check that this group belongs to this warehouse. - if wid~=nil and wid==self.uid then - - -- Debug info. - self:T(self.wid..string.format("Warehouse %s captured event landing of its asset unit %s.", self.alias, EventData.IniUnitName)) - - -- Check if all cargo was delivered. - if self.delivered[rid]==true then - - -- Check if helicopter landed in spawn zone. If so, we call it a day and add it back to stock. - if group:GetCategory()==Group.Category.HELICOPTER then - if self.spawnzone:IsCoordinateInZone(EventData.IniUnit:GetCoordinate()) then - - -- Debug message. - self:_DebugMessage("Helicopter landed in spawn zone. No pending request. Putting back into stock.") - if self.Debug then - group:SmokeWhite() - end - - -- Group arrived. - self:Arrived(group) - - end - end - - end - - end - end -end - ---- Warehouse event handling function. --- @param #WAREHOUSE self --- @param Core.Event#EVENTDATA EventData Event data. -function WAREHOUSE:_OnEventEngineShutdown(EventData) - self:T3(self.wid..string.format("Warehouse %s captured event engine shutdown!", self.alias)) - - if EventData and EventData.IniGroup then - local group=EventData.IniGroup - local wid,aid,rid=self:_GetIDsFromGroup(group) - if wid==self.uid then - self:T(self.wid..string.format("Warehouse %s captured event engine shutdown of its asset unit %s.", self.alias, EventData.IniUnitName)) - end - end -end +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Warehouse event handling function. -- @param #WAREHOUSE self @@ -4163,7 +4159,7 @@ end function WAREHOUSE:_OnEventCrashOrDead(EventData) self:T3(self.wid..string.format("Warehouse %s captured event dead or crash!",self.alias)) - if EventData and EventData.IniUnit then + if EventData and EventData.IniUnit and EventData.IniGroup then -- Check if warehouse was destroyed. local warehousename=self.warehouse:GetName() @@ -4173,40 +4169,62 @@ function WAREHOUSE:_OnEventCrashOrDead(EventData) -- Trigger Destroyed event. self:Destroyed() end - end - - -- Check if an asset unit was destroyed. - if EventData and EventData.IniGroup then - + + -- Check if an asset unit was destroyed. local group=EventData.IniGroup -- Get warehouse, asset and request IDs from the group name. local wid,aid,rid=self:_GetIDsFromGroup(group) + -- Check that we have the right warehouse. if wid==self.uid then + + -- Debug message. self:T(self.wid..string.format("Warehouse %s captured event dead or crash of its asset unit %s.", self.alias, EventData.IniUnitName)) -- Loop over all pending requests and get the one belonging to this unit. for _,request in pairs(self.pending) do local request=request --#WAREHOUSE.Pendingitem + -- This is the right request. if request.uid==rid then -- Update cargo and transport group sets of this request. We need to know if this job is finished. self:_UnitDead(EventData.IniUnit, request) - - --[[ - if self:_GroupIsTransport(group, request) then - -- Count number of transport groups (if any) and filter out dead groups. Dead groups are removed from the set. - -- TODO: Now, what about the cargo groups that are inside the transport?! Need to save in which carrier the cargo is? - self:_FilterDead(request.transportgroupset) - else - -- Count number of cargo groups still alive and filter out dead groups. Dead groups are removed from the set. - self:_FilterDead(request.cargogroupset) + + -- Update pending request. Increase ndelivered/ntransporthome and delete group from corresponding group set. + self:_UpdatePending(group) + + -- Number of cargo assets still in group set. + if isCargo==true then + + -- Current size of cargo group set. + local ncargo=request.cargogroupset:Count() + + -- Debug message. + local text=string.format("Cargo %d of %s added to warehouse %s stock. Assets still to deliver %d.", + request.ndelivered, tostring(request.nasset), request.warehouse.alias, ncargo) + self:_DebugMessage(text, 5) + + -- All cargo delivered. + if ncargo==0 then + self:Delivered(request) + end + + elseif isCargo==false then + + -- Current size of cargo group set. + local ntransport=request.transportgroupset:Count() + + -- Debug message. + local text=string.format("Transport %d of %s added to warehouse %s stock. Transports still missing %d.", + request.ntransporthome, tostring(request.ntransport), request.warehouse.alias, ntransport) + self:_DebugMessage(text, 5) + end - ]] + end - + end end end @@ -4246,7 +4264,7 @@ function WAREHOUSE:_UnitDead(deadunit, request) -- Remove dead group from carg group set. if isgroupdead==true then - request.cargogroupset:Remove(group, NoTriggerEvent) + request.cargogroupset:Remove(group:GetName(), NoTriggerEvent) end else @@ -4265,18 +4283,15 @@ function WAREHOUSE:_UnitDead(deadunit, request) if carrierunit then -- Loop over all groups inside the carrier ==> all dead. - for _,cargogroup in pairs(carrierunit) do - - -- TODO: Check if remove really needs the name? Did I not always use the group object?! - --request.cargogroupset:Remove(ObjectName,NoTriggerEvent) - request.cargogroupset:Remove(cargogroup,NoTriggerEvent) + for _,cargogroup in pairs(carrierunit) do + request.cargogroupset:Remove(cargogroup:GetName(),NoTriggerEvent) end end -- Whole carrier group is dead. Remove it from the carrier group set. if isgroupdead then - request.transportcargoset:Remove(group, NoTriggerEvent) + request.transportcargoset:Remove(group:GetName(), NoTriggerEvent) end else @@ -4285,7 +4300,7 @@ function WAREHOUSE:_UnitDead(deadunit, request) -- Remove dead group from cargo group set. if isgroupdead==true then - request.cargogroupset:Remove(group, NoTriggerEvent) + request.cargogroupset:Remove(group:GetName(), NoTriggerEvent) -- TODO: This as well? --request.transportcargoset:RemoveCargosByName(RemoveCargoNames) @@ -4295,40 +4310,7 @@ function WAREHOUSE:_UnitDead(deadunit, request) end end ---- Count alive and filter dead groups. --- @param #WAREHOUSE self --- @param Core.Set#SET_GROUP groupset Set of groups. Dead groups are removed from the set. --- @return #number Number of alive groups. Returns zero if groupset is nil. -function WAREHOUSE:_FilterDead(groupset) - local nalive=0 - - if groupset then - - -- Check if groups are still alive - local dead={} - for _,group in pairs(groupset:GetSetObjects()) do - if group and group:IsAlive() then - nalive=nalive+1 - else - table.insert(dead, group) - end - end - - -- TODO: Since the cargo groups are de- and re-spawned, does the counting for cargo groups actually work, when they are transported? - - -- Debug info. - self:T(self.wid..string.format("FilterDead: Alive=%d, Dead=%d", nalive, #dead)) - - -- Remove dead groups - local NoTriggerEvent=false - for _,group in pairs(dead) do - groupset:Remove(group, NoTriggerEvent) - end - end - - return nalive -end --- Warehouse event handling function. From c1191e286af760d0ed3ede4bd1921bb0d165289f Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 11 Sep 2018 09:00:30 +0200 Subject: [PATCH 341/420] Added the new event S_EVENT_REMOVE_UNIT to trigger the removal of units (from groups also) when a :Destroy(false) or :Destroy() is called for a UNIT object. The units are then removed from each SET that is subscribed to a set of UNIT objects or GROUP objects! Also the DATABASE is correctly managing this new removal method. This to prevent the DATABASE getting corrupted with dead units, which were removed with :Destroy(), but which weren't cleaned from the database. --- Moose Development/Moose/Core/Base.lua | 16 ++++++++++ Moose Development/Moose/Core/Database.lua | 7 +--- Moose Development/Moose/Core/Event.lua | 14 ++++++-- Moose Development/Moose/Core/Set.lua | 4 ++- Moose Development/Moose/Wrapper/Group.lua | 6 ++-- .../Moose/Wrapper/Positionable.lua | 32 +++++++++++++++++++ Moose Development/Moose/Wrapper/Unit.lua | 28 ---------------- 7 files changed, 68 insertions(+), 39 deletions(-) diff --git a/Moose Development/Moose/Core/Base.lua b/Moose Development/Moose/Core/Base.lua index 1b4d96efb..3d6af24d3 100644 --- a/Moose Development/Moose/Core/Base.lua +++ b/Moose Development/Moose/Core/Base.lua @@ -656,6 +656,22 @@ function BASE:CreateEventDead( EventTime, Initiator ) world.onEvent( Event ) end +--- Creation of a Remove Unit Event. +-- @param #BASE self +-- @param DCS#Time EventTime The time stamp of the event. +-- @param DCS#Object Initiator The initiating object of the event. +function BASE:CreateEventRemoveUnit( EventTime, Initiator ) + self:F( { EventTime, Initiator } ) + + local Event = { + id = EVENTS.RemoveUnit, + time = EventTime, + initiator = Initiator, + } + + world.onEvent( Event ) +end + --- Creation of a Takeoff Event. -- @param #BASE self -- @param DCS#Time EventTime The time stamp of the event. diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index e497e306d..913ec4fe9 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -95,6 +95,7 @@ function DATABASE:New() self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) + self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Hit, self.AccountHits ) self:HandleEvent( EVENTS.NewCargo ) self:HandleEvent( EVENTS.DeleteCargo ) @@ -1347,18 +1348,12 @@ end self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType } ) end - self:T( "Something got destroyed" ) - local Destroyed = false -- What is the player destroying? if self.HITS[Event.IniUnitName] then -- Was there a hit for this unit for this player before registered??? - - self.DESTROYS[Event.IniUnitName] = self.DESTROYS[Event.IniUnitName] or {} - self.DESTROYS[Event.IniUnitName] = true - end end diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index c7f937c8b..c493a453f 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -181,6 +181,8 @@ world.event.S_EVENT_NEW_CARGO = world.event.S_EVENT_MAX + 1000 world.event.S_EVENT_DELETE_CARGO = world.event.S_EVENT_MAX + 1001 world.event.S_EVENT_NEW_ZONE = world.event.S_EVENT_MAX + 1002 world.event.S_EVENT_DELETE_ZONE = world.event.S_EVENT_MAX + 1003 +world.event.S_EVENT_REMOVE_UNIT = world.event.S_EVENT_MAX + 1004 + --- The different types of events supported by MOOSE. -- Use this structure to subscribe to events using the @{Core.Base#BASE.HandleEvent}() method. @@ -216,6 +218,7 @@ EVENTS = { DeleteCargo = world.event.S_EVENT_DELETE_CARGO, NewZone = world.event.S_EVENT_NEW_ZONE, DeleteZone = world.event.S_EVENT_DELETE_ZONE, + RemoveUnit = world.event.S_EVENT_REMOVE_UNIT, } --- The Event structure @@ -441,6 +444,11 @@ local _EVENTMETA = { Event = "OnEventDeleteZone", Text = "S_EVENT_DELETE_ZONE" }, + [EVENTS.RemoveUnit] = { + Order = -1, + Event = "OnEventRemoveUnit", + Text = "S_EVENT_REMOVE_UNIT" + }, } @@ -985,7 +993,8 @@ function EVENT:onEvent( Event ) if EventClass:IsAlive() or Event.id == EVENTS.PlayerEnterUnit or Event.id == EVENTS.Crash or - Event.id == EVENTS.Dead then + Event.id == EVENTS.Dead or + Event.id == EVENTS.RemoveUnit then local UnitName = EventClass:GetName() @@ -1035,7 +1044,8 @@ function EVENT:onEvent( Event ) if EventClass:IsAlive() or Event.id == EVENTS.PlayerEnterUnit or Event.id == EVENTS.Crash or - Event.id == EVENTS.Dead then + Event.id == EVENTS.Dead or + Event.id == EVENTS.RemoveUnit then -- We can get the name of the EventClass, which is now always a GROUP object. local GroupName = EventClass:GetName() diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 650c22323..262cbed56 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -1023,6 +1023,7 @@ function SET_GROUP:FilterStart() self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) + self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash ) end @@ -1804,8 +1805,9 @@ do -- SET_UNIT if _DATABASE then self:_FilterStart() self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) + self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrashOr ) self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) + self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash ) end return self diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 81d26911e..dc31a60d6 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -261,13 +261,15 @@ function GROUP:Destroy( GenerateEvent ) local DCSGroup = self:GetDCSObject() if DCSGroup then - if GenerateEvent and GenerateEvent == true then - for Index, UnitData in pairs( DCSGroup:getUnits() ) do + for Index, UnitData in pairs( DCSGroup:getUnits() ) do + if GenerateEvent and GenerateEvent == true then if self:IsAir() then self:CreateEventCrash( timer.getTime(), UnitData ) else self:CreateEventDead( timer.getTime(), UnitData ) end + else + self:CreateEventRemove( timer.getTime(), UnitData ) end end USERFLAG:New( self:GetName() ):Set( 100 ) diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index aabd88670..20bbfe9d2 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -69,6 +69,38 @@ function POSITIONABLE:New( PositionableName ) return self end +--- Destroys the POSITIONABLE. +-- @param #POSITIONABLE self +-- @param #boolean GenerateEvent (Optional) true if you want to generate a crash or dead event for the unit. +-- @return #nil The DCS Unit is not existing or alive. +function POSITIONABLE:Destroy( GenerateEvent ) + self:F2( self.ObjectName ) + + local DCSObject = self:GetDCSObject() + + if DCSObject then + + local UnitGroup = self:GetGroup() + local UnitGroupName = UnitGroup:GetName() + self:F( { UnitGroupName = UnitGroupName } ) + + if GenerateEvent and GenerateEvent == true then + if self:IsAir() then + self:CreateEventCrash( timer.getTime(), DCSObject ) + else + self:CreateEventDead( timer.getTime(), DCSObject ) + end + else + self:CreateEventRemoveUnit( timer.getTime(), DCSObject ) + end + + USERFLAG:New( UnitGroupName ):Set( 100 ) + DCSObject:destroy() + end + + return nil +end + --- Returns the @{DCS#Position3} position vectors indicating the point and direction vectors in 3D of the POSITIONABLE within the mission. -- @param Wrapper.Positionable#POSITIONABLE self -- @return DCS#Position The 3D position vectors of the POSITIONABLE. diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 059a0266c..f50ca5c61 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -157,35 +157,7 @@ function UNIT:GetDCSObject() return nil end ---- Destroys the UNIT. --- @param #UNIT self --- @param #boolean GenerateEvent (Optional) true if you want to generate a crash or dead event for the unit. --- @return #nil The DCS Unit is not existing or alive. -function UNIT:Destroy( GenerateEvent ) - self:F2( self.ObjectName ) - local DCSObject = self:GetDCSObject() - - if DCSObject then - - local UnitGroup = self:GetGroup() - local UnitGroupName = UnitGroup:GetName() - self:F( { UnitGroupName = UnitGroupName } ) - - if GenerateEvent and GenerateEvent == true then - if self:IsAir() then - self:CreateEventCrash( timer.getTime(), DCSObject ) - else - self:CreateEventDead( timer.getTime(), DCSObject ) - end - end - - USERFLAG:New( UnitGroupName ):Set( 100 ) - DCSObject:destroy() - end - - return nil -end --- Respawn the @{Wrapper.Unit} using a (tweaked) template of the parent Group. From 9647a1a84e329de064039a67d4df284b6d1e252f Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 11 Sep 2018 10:04:33 +0200 Subject: [PATCH 342/420] Implementation of unit maintenance in DATABASE and SETs for Cargo objects. Pls retest. --- Moose Development/Moose/Cargo/Cargo.lua | 2 +- Moose Development/Moose/Cargo/CargoCrate.lua | 1 + Moose Development/Moose/Cargo/CargoGroup.lua | 5 +++-- Moose Development/Moose/Cargo/CargoSlingload.lua | 1 + 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index a8cd46b66..433836ba5 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -531,7 +531,7 @@ do -- CARGO -- @param #CARGO self function CARGO:Destroy() if self.CargoObject then - self.CargoObject:Destroy( false ) + self.CargoObject:Destroy() end self:Destroyed() end diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua index a1258d633..fce280625 100644 --- a/Moose Development/Moose/Cargo/CargoCrate.lua +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -63,6 +63,7 @@ do -- CARGO_CRATE self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead ) self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead ) + self:HandleEvent( EVENTS.RemoveUnit, self.OnEventCargoDead ) self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead ) self:SetEventPriority( 4 ) diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 9bb3a8c1b..aa0917810 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -69,7 +69,7 @@ do -- CARGO_GROUP local WeightGroup = 0 local VolumeGroup = 0 - self.CargoGroup:Destroy( true ) -- generate the crash events, so that the database gets cleaned, and the linked sets get properly cleaned. + self.CargoGroup:Destroy() -- destroy and generate a unit removal event, so that the database gets cleaned, and the linked sets get properly cleaned. local GroupName = CargoGroup:GetName() self.CargoName = Name @@ -121,6 +121,7 @@ do -- CARGO_GROUP self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead ) self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead ) + self:HandleEvent( EVENTS.RemoveUnit, self.OnEventCargoDead ) self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead ) self:SetEventPriority( 4 ) @@ -137,7 +138,7 @@ do -- CARGO_GROUP for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do local Cargo = CargoData -- Cargo.Cargo#CARGO - Cargo:Destroy() + Cargo:Destroy() -- Destroy the cargo and generate a remove unit event to update the sets. Cargo:SetStartState( "UnLoaded" ) end diff --git a/Moose Development/Moose/Cargo/CargoSlingload.lua b/Moose Development/Moose/Cargo/CargoSlingload.lua index 452bbe3e8..c22fdb38b 100644 --- a/Moose Development/Moose/Cargo/CargoSlingload.lua +++ b/Moose Development/Moose/Cargo/CargoSlingload.lua @@ -56,6 +56,7 @@ do -- CARGO_SLINGLOAD self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead ) self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead ) + self:HandleEvent( EVENTS.RemoveUnit, self.OnEventCargoDead ) self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead ) self:SetEventPriority( 4 ) From d5e2365bb370a4d8bd871f8c120bdbda56b95b7d Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 11 Sep 2018 15:26:23 +0200 Subject: [PATCH 343/420] More updates to fix the RemoveUnit problems for DATABASE and SETs. --- Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua | 4 ++-- Moose Development/Moose/Wrapper/Group.lua | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 4b40ae379..6d390c500 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -885,7 +885,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() -- The Pickup sequence ... -- Check if this Carrier need to go and Pickup something... -- So, if the cargo bay is not full yet with cargo to be loaded ... - self:I( { IsRelocating = AI_Cargo:IsRelocating(), IsTransporting = AI_Cargo:IsTransporting() } ) + self:I( { Carrier = CarrierGroupName, IsRelocating = AI_Cargo:IsRelocating(), IsTransporting = AI_Cargo:IsTransporting() } ) if AI_Cargo:IsRelocating() == false and AI_Cargo:IsTransporting() == false then -- ok, so there is a free Carrier -- now find the first cargo that is Unloaded @@ -944,7 +944,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() if self.HomeZone then if not self.CarrierHome[Carrier] then self.CarrierHome[Carrier] = true - AI_Cargo:__Home( 60, self.HomeZone:GetRandomPointVec2(), math.random( self.PickupMinSpeed, self.PickupMaxSpeed ), self.HomeZone ) + AI_Cargo:Home( self.HomeZone:GetRandomPointVec2(), math.random( self.PickupMinSpeed, self.PickupMaxSpeed ), self.HomeZone ) end end end diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index dc31a60d6..44ca57d5f 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -269,7 +269,7 @@ function GROUP:Destroy( GenerateEvent ) self:CreateEventDead( timer.getTime(), UnitData ) end else - self:CreateEventRemove( timer.getTime(), UnitData ) + self:CreateEventRemoveUnit( timer.getTime(), UnitData ) end end USERFLAG:New( self:GetName() ):Set( 100 ) From 9c9633ba23c1472d187b7c1968054e083b30d9b0 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 11 Sep 2018 20:38:09 +0200 Subject: [PATCH 344/420] Added FilterActive() method in SET\_UNIT, SET\GROUP, SET\_CLIENT. Haven't tested the behaviour for CLIENTs joining and leaving. --- Moose Development/Moose/AI/AI_Cargo.lua | 2 +- Moose Development/Moose/Cargo/CargoGroup.lua | 3 +- Moose Development/Moose/Core/Set.lua | 214 ++++++++++++++----- Moose Development/Moose/Core/Settings.lua | 1 - Moose Development/Moose/Wrapper/Group.lua | 30 ++- 5 files changed, 186 insertions(+), 64 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua index 2da2566e7..1d358b735 100644 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -178,7 +178,7 @@ function AI_CARGO:onbeforeLoad( Carrier, From, Event, To, PickupZone ) local Boarding = false - local LoadInterval = 10 + local LoadInterval = 2 local LoadDelay = 0 local Carrier_List = {} local Carrier_Weight = {} diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index aa0917810..7c3a2530c 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -258,7 +258,6 @@ do -- CARGO_GROUP --- @param #CARGO_GROUP self -- @param Core.Event#EVENTDATA EventData function CARGO_GROUP:OnEventCargoDead( EventData ) - self:I( EventData ) local Destroyed = false @@ -424,7 +423,7 @@ do -- CARGO_GROUP ToVec=ToPointVec2 end Cargo:__UnBoard( Timer, ToVec, NearRadius ) - Timer = Timer + 3 + Timer = Timer + 1 end end, { NearRadius } ) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 262cbed56..45a0f4091 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -636,7 +636,7 @@ function SET_BASE:Flush( MasterObject ) for ObjectName, Object in pairs( self.Set ) do ObjectNames = ObjectNames .. ObjectName .. ", " end - self:I( { MasterObject = MasterObject and MasterObject:GetClassNameAndID(), "Objects in Set:", ObjectNames } ) + self:F( { MasterObject = MasterObject and MasterObject:GetClassNameAndID(), "Objects in Set:", ObjectNames } ) return ObjectNames end @@ -672,6 +672,7 @@ end -- * @{#SET_GROUP.FilterCategories}: Builds the SET_GROUP with the groups belonging to the category(ies). -- * @{#SET_GROUP.FilterCountries}: Builds the SET_GROUP with the gruops belonging to the country(ies). -- * @{#SET_GROUP.FilterPrefixes}: Builds the SET_GROUP with the groups starting with the same prefix string(s). +-- * @{#SET_GROUP.FilterActive}: Builds the SET_GROUP with the groups that are only active. Groups that are inactive (late activation) won't be included in the set! -- -- For the Category Filter, extra methods have been added: -- @@ -685,6 +686,7 @@ end -- Once the filter criteria have been set for the SET_GROUP, you can start filtering using: -- -- * @{#SET_GROUP.FilterStart}: Starts the filtering of the groups within the SET_GROUP and add or remove GROUP objects **dynamically**. +-- * @{#SET_GROUP.FilterOnce}: Filters of the groups **once**. -- -- Planned filter criteria within development are (so these are not yet available): -- @@ -783,7 +785,9 @@ SET_GROUP = { function SET_GROUP:New() -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.GROUPS ) ) + local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.GROUPS ) ) -- #SET_GROUP + + self:FilterActive( false ) return self end @@ -1012,6 +1016,32 @@ function SET_GROUP:FilterPrefixes( Prefixes ) return self end +--- Builds a set of groups that are only active. +-- Only the groups that are active will be included within the set. +-- @param #SET_GROUP self +-- @param #boolean Active (optional) Include only active groups to the set. +-- Include inactive groups if you provide false. +-- @return #SET_GROUP self +-- @usage +-- +-- -- Include only active groups to the set. +-- GroupSet = SET_GROUP:New():FilterActive():FilterStart() +-- +-- -- Include only active groups to the set of the blue coalition, and filter one time. +-- GroupSet = SET_GROUP:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() +-- +-- -- Include only active groups to the set of the blue coalition, and filter one time. +-- -- Later, reset to include back inactive groups to the set. +-- GroupSet = SET_GROUP:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() +-- ... logic ... +-- GroupSet = SET_GROUP:New():FilterActive( false ):FilterCoalition( "blue" ):FilterOnce() +-- +function SET_GROUP:FilterActive( Active ) + Active = Active or not ( Active == false ) + self.Filter.Active = Active + return self +end + --- Starts the filtering. -- @param #SET_GROUP self @@ -1377,58 +1407,67 @@ end --- -- @param #SET_GROUP self --- @param Wrapper.Group#GROUP MooseGroup +-- @param Wrapper.Group#GROUP MGroup The group that is checked for inclusion. -- @return #SET_GROUP self -function SET_GROUP:IsIncludeObject( MooseGroup ) - self:F2( MooseGroup ) - local MooseGroupInclude = true +function SET_GROUP:IsIncludeObject( MGroup ) + self:F2( MGroup ) + local MGroupInclude = true + if self.Filter.Active ~= nil then + local MGroupActive = false + self:F( { Active = self.Filter.Active } ) + if self.Filter.Active == false or ( self.Filter.Active == true and MGroup:IsActive() == true ) then + MGroupActive = true + end + MGroupInclude = MGroupInclude and MGroupActive + end + if self.Filter.Coalitions then - local MooseGroupCoalition = false + local MGroupCoalition = false for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - self:T3( { "Coalition:", MooseGroup:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) - if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == MooseGroup:GetCoalition() then - MooseGroupCoalition = true + self:T3( { "Coalition:", MGroup:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == MGroup:GetCoalition() then + MGroupCoalition = true end end - MooseGroupInclude = MooseGroupInclude and MooseGroupCoalition + MGroupInclude = MGroupInclude and MGroupCoalition end if self.Filter.Categories then - local MooseGroupCategory = false + local MGroupCategory = false for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - self:T3( { "Category:", MooseGroup:GetCategory(), self.FilterMeta.Categories[CategoryName], CategoryName } ) - if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == MooseGroup:GetCategory() then - MooseGroupCategory = true + self:T3( { "Category:", MGroup:GetCategory(), self.FilterMeta.Categories[CategoryName], CategoryName } ) + if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == MGroup:GetCategory() then + MGroupCategory = true end end - MooseGroupInclude = MooseGroupInclude and MooseGroupCategory + MGroupInclude = MGroupInclude and MGroupCategory end if self.Filter.Countries then - local MooseGroupCountry = false + local MGroupCountry = false for CountryID, CountryName in pairs( self.Filter.Countries ) do - self:T3( { "Country:", MooseGroup:GetCountry(), CountryName } ) - if country.id[CountryName] == MooseGroup:GetCountry() then - MooseGroupCountry = true + self:T3( { "Country:", MGroup:GetCountry(), CountryName } ) + if country.id[CountryName] == MGroup:GetCountry() then + MGroupCountry = true end end - MooseGroupInclude = MooseGroupInclude and MooseGroupCountry + MGroupInclude = MGroupInclude and MGroupCountry end if self.Filter.GroupPrefixes then - local MooseGroupPrefix = false + local MGroupPrefix = false for GroupPrefixId, GroupPrefix in pairs( self.Filter.GroupPrefixes ) do - self:T3( { "Prefix:", string.find( MooseGroup:GetName(), GroupPrefix, 1 ), GroupPrefix } ) - if string.find( MooseGroup:GetName(), GroupPrefix:gsub ("-", "%%-"), 1 ) then - MooseGroupPrefix = true + self:T3( { "Prefix:", string.find( MGroup:GetName(), GroupPrefix, 1 ), GroupPrefix } ) + if string.find( MGroup:GetName(), GroupPrefix:gsub ("-", "%%-"), 1 ) then + MGroupPrefix = true end end - MooseGroupInclude = MooseGroupInclude and MooseGroupPrefix + MGroupInclude = MGroupInclude and MGroupPrefix end - self:T2( MooseGroupInclude ) - return MooseGroupInclude + self:T2( MGroupInclude ) + return MGroupInclude end @@ -1463,18 +1502,18 @@ do -- SET_UNIT -- * Unit types -- * Starting with certain prefix strings. -- - -- ## SET_UNIT constructor + -- ## 1) SET_UNIT constructor -- -- Create a new SET_UNIT object with the @{#SET_UNIT.New} method: -- -- * @{#SET_UNIT.New}: Creates a new SET_UNIT object. -- - -- ## Add or Remove UNIT(s) from SET_UNIT + -- ## 2) Add or Remove UNIT(s) from SET_UNIT -- -- UNITs can be added and removed using the @{Core.Set#SET_UNIT.AddUnitsByName} and @{Core.Set#SET_UNIT.RemoveUnitsByName} respectively. -- These methods take a single UNIT name or an array of UNIT names to be added or removed from SET_UNIT. -- - -- ## SET_UNIT filter criteria + -- ## 3) SET_UNIT filter criteria -- -- You can set filter criteria to define the set of units within the SET_UNIT. -- Filter criteria are defined by: @@ -1484,24 +1523,26 @@ do -- SET_UNIT -- * @{#SET_UNIT.FilterTypes}: Builds the SET_UNIT with the units belonging to the unit type(s). -- * @{#SET_UNIT.FilterCountries}: Builds the SET_UNIT with the units belonging to the country(ies). -- * @{#SET_UNIT.FilterPrefixes}: Builds the SET_UNIT with the units starting with the same prefix string(s). + -- * @{#SET_UNIT.FilterActive}: Builds the SET_UNIT with the units that are only active. Units that are inactive (late activation) won't be included in the set! -- -- Once the filter criteria have been set for the SET_UNIT, you can start filtering using: -- - -- * @{#SET_UNIT.FilterStart}: Starts the filtering of the units within the SET_UNIT. + -- * @{#SET_UNIT.FilterStart}: Starts the filtering of the units **dynamically**. + -- * @{#SET_UNIT.FilterOnce}: Filters of the units **once**. -- -- Planned filter criteria within development are (so these are not yet available): -- -- * @{#SET_UNIT.FilterZones}: Builds the SET_UNIT with the units within a @{Core.Zone#ZONE}. -- - -- ## SET_UNIT iterators + -- ## 4) SET_UNIT iterators -- -- Once the filters have been defined and the SET_UNIT has been built, you can iterate the SET_UNIT with the available iterator methods. -- The iterator methods will walk the SET_UNIT set, and call for each element within the set a function that you provide. -- The following iterator methods are currently available within the SET_UNIT: -- -- * @{#SET_UNIT.ForEachUnit}: Calls a function for each alive unit it finds within the SET_UNIT. - -- * @{#SET_GROUP.ForEachGroupCompletelyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. - -- * @{#SET_GROUP.ForEachGroupNotInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. + -- * @{#SET_UNIT.ForEachUnitInZone}: Iterate the SET_UNIT and call an iterator function for each **alive** UNIT object presence completely in a @{Zone}, providing the UNIT object and optional parameters to the called function. + -- * @{#SET_UNIT.ForEachUnitNotInZone}: Iterate the SET_UNIT and call an iterator function for each **alive** UNIT object presence not in a @{Zone}, providing the UNIT object and optional parameters to the called function. -- -- Planned iterators methods in development are (so these are not yet available): -- @@ -1509,27 +1550,17 @@ do -- SET_UNIT -- * @{#SET_UNIT.ForEachUnitCompletelyInZone}: Iterate and call an iterator function for each **alive** UNIT presence completely in a @{Zone}, providing the UNIT and optional parameters to the called function. -- * @{#SET_UNIT.ForEachUnitNotInZone}: Iterate and call an iterator function for each **alive** UNIT presence not in a @{Zone}, providing the UNIT and optional parameters to the called function. -- - -- ## SET_UNIT atomic methods + -- ## 5) SET_UNIT atomic methods -- -- Various methods exist for a SET_UNIT to perform actions or calculations and retrieve results from the SET_UNIT: -- -- * @{#SET_UNIT.GetTypeNames}(): Retrieve the type names of the @{Wrapper.Unit}s in the SET, delimited by a comma. -- - -- ## SET_UNIT iterators - -- - -- Once the filters have been defined and the SET_UNIT has been built, you can iterate the SET_UNIT with the available iterator methods. - -- The iterator methods will walk the SET_UNIT set, and call for each element within the set a function that you provide. - -- The following iterator methods are currently available within the SET_UNIT: - -- - -- * @{#SET_UNIT.ForEachUnit}: Calls a function for each alive group it finds within the SET_UNIT. - -- * @{#SET_UNIT.ForEachUnitInZone}: Iterate the SET_UNIT and call an iterator function for each **alive** UNIT object presence completely in a @{Zone}, providing the UNIT object and optional parameters to the called function. - -- * @{#SET_UNIT.ForEachUnitNotInZone}: Iterate the SET_UNIT and call an iterator function for each **alive** UNIT object presence not in a @{Zone}, providing the UNIT object and optional parameters to the called function. - -- - -- ## SET_UNIT trigger events on the UNIT objects. + -- ## 6) SET_UNIT trigger events on the UNIT objects. -- -- The SET is derived from the FSM class, which provides extra capabilities to track the contents of the UNIT objects in the SET_UNIT. -- - -- ### When a UNIT object crashes or is dead, the SET_UNIT will trigger a **Dead** event. + -- ### 6.1) When a UNIT object crashes or is dead, the SET_UNIT will trigger a **Dead** event. -- -- You can handle the event using the OnBefore and OnAfter event handlers. -- The event handlers need to have the paramters From, Event, To, GroupObject. @@ -1611,7 +1642,9 @@ do -- SET_UNIT function SET_UNIT:New() -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.UNITS ) ) -- Core.Set#SET_UNIT + local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.UNITS ) ) -- #SET_UNIT + + self:FilterActive( false ) return self end @@ -1769,6 +1802,32 @@ do -- SET_UNIT return self end + --- Builds a set of units that are only active. + -- Only the units that are active will be included within the set. + -- @param #SET_UNIT self + -- @param #boolean Active (optional) Include only active units to the set. + -- Include inactive units if you provide false. + -- @return #SET_UNIT self + -- @usage + -- + -- -- Include only active units to the set. + -- UnitSet = SET_UNIT:New():FilterActive():FilterStart() + -- + -- -- Include only active units to the set of the blue coalition, and filter one time. + -- UnitSet = SET_UNIT:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() + -- + -- -- Include only active units to the set of the blue coalition, and filter one time. + -- -- Later, reset to include back inactive units to the set. + -- UnitSet = SET_UNIT:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() + -- ... logic ... + -- UnitSet = SET_UNIT:New():FilterActive( false ):FilterCoalition( "blue" ):FilterOnce() + -- + function SET_UNIT:FilterActive( Active ) + Active = Active or not ( Active == false ) + self.Filter.Active = Active + return self + end + --- Builds a set of units having a radar of give types. -- All the units having a radar of a given type will be included within the set. -- @param #SET_UNIT self @@ -2343,6 +2402,14 @@ do -- SET_UNIT self:F2( MUnit ) local MUnitInclude = true + if self.Filter.Active ~= nil then + local MUnitActive = false + if self.Filter.Active == false or ( self.Filter.Active == true and MUnit:IsActive() == true ) then + MUnitActive = true + end + MUnitInclude = MUnitInclude and MUnitActive + end + if self.Filter.Coalitions then local MUnitCoalition = false for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do @@ -3155,18 +3222,18 @@ end -- * Client types -- * Starting with certain prefix strings. -- --- ## SET_CLIENT constructor +-- ## 1) SET_CLIENT constructor -- -- Create a new SET_CLIENT object with the @{#SET_CLIENT.New} method: -- -- * @{#SET_CLIENT.New}: Creates a new SET_CLIENT object. -- --- ## Add or Remove CLIENT(s) from SET_CLIENT +-- ## 2) Add or Remove CLIENT(s) from SET_CLIENT -- -- CLIENTs can be added and removed using the @{Core.Set#SET_CLIENT.AddClientsByName} and @{Core.Set#SET_CLIENT.RemoveClientsByName} respectively. -- These methods take a single CLIENT name or an array of CLIENT names to be added or removed from SET_CLIENT. -- --- ## SET_CLIENT filter criteria +-- ## 3) SET_CLIENT filter criteria -- -- You can set filter criteria to define the set of clients within the SET_CLIENT. -- Filter criteria are defined by: @@ -3176,16 +3243,18 @@ end -- * @{#SET_CLIENT.FilterTypes}: Builds the SET_CLIENT with the clients belonging to the client type(s). -- * @{#SET_CLIENT.FilterCountries}: Builds the SET_CLIENT with the clients belonging to the country(ies). -- * @{#SET_CLIENT.FilterPrefixes}: Builds the SET_CLIENT with the clients starting with the same prefix string(s). +-- * @{#SET_CLIENT.FilterActive}: Builds the SET_CLIENT with the units that are only active. Units that are inactive (late activation) won't be included in the set! -- -- Once the filter criteria have been set for the SET_CLIENT, you can start filtering using: -- --- * @{#SET_CLIENT.FilterStart}: Starts the filtering of the clients within the SET_CLIENT. +-- * @{#SET_CLIENT.FilterStart}: Starts the filtering of the clients **dynamically**. +-- * @{#SET_CLIENT.FilterOnce}: Filters the clients **once**. -- -- Planned filter criteria within development are (so these are not yet available): -- -- * @{#SET_CLIENT.FilterZones}: Builds the SET_CLIENT with the clients within a @{Core.Zone#ZONE}. -- --- ## SET_CLIENT iterators +-- ## 4) SET_CLIENT iterators -- -- Once the filters have been defined and the SET_CLIENT has been built, you can iterate the SET_CLIENT with the available iterator methods. -- The iterator methods will walk the SET_CLIENT set, and call for each element within the set a function that you provide. @@ -3230,7 +3299,9 @@ SET_CLIENT = { -- DBObject = SET_CLIENT:New() function SET_CLIENT:New() -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.CLIENTS ) ) + local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.CLIENTS ) ) -- #SET_CLIENT + + self:FilterActive( false ) return self end @@ -3372,6 +3443,31 @@ function SET_CLIENT:FilterPrefixes( Prefixes ) return self end +--- Builds a set of clients that are only active. +-- Only the clients that are active will be included within the set. +-- @param #SET_CLIENT self +-- @param #boolean Active (optional) Include only active clients to the set. +-- Include inactive clients if you provide false. +-- @return #SET_CLIENT self +-- @usage +-- +-- -- Include only active clients to the set. +-- ClientSet = SET_CLIENT:New():FilterActive():FilterStart() +-- +-- -- Include only active clients to the set of the blue coalition, and filter one time. +-- ClientSet = SET_CLIENT:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() +-- +-- -- Include only active clients to the set of the blue coalition, and filter one time. +-- -- Later, reset to include back inactive clients to the set. +-- ClientSet = SET_CLIENT:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() +-- ... logic ... +-- ClientSet = SET_CLIENT:New():FilterActive( false ):FilterCoalition( "blue" ):FilterOnce() +-- +function SET_CLIENT:FilterActive( Active ) + Active = Active or not ( Active == false ) + self.Filter.Active = Active + return self +end @@ -3482,6 +3578,14 @@ function SET_CLIENT:IsIncludeObject( MClient ) if MClient then local MClientName = MClient.UnitName + if self.Filter.Active ~= nil then + local MClientActive = false + if self.Filter.Active == false or ( self.Filter.Active == true and MClient:IsActive() == true ) then + MClientActive = true + end + MClientInclude = MClientInclude and MClientActive + end + if self.Filter.Coalitions then local MClientCoalition = false for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do diff --git a/Moose Development/Moose/Core/Settings.lua b/Moose Development/Moose/Core/Settings.lua index 819690607..20935af35 100644 --- a/Moose Development/Moose/Core/Settings.lua +++ b/Moose Development/Moose/Core/Settings.lua @@ -364,7 +364,6 @@ do -- SETTINGS -- @param #SETTINGS self -- @return #boolean true if BRA function SETTINGS:IsA2A_BRAA() - self:E( { BRA = ( self.A2ASystem and self.A2ASystem == "BRAA" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BRAA() ) } ) return ( self.A2ASystem and self.A2ASystem == "BRAA" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BRAA() ) end diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 44ca57d5f..470de56b4 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -205,18 +205,18 @@ function GROUP:GetPositionVec3() -- Overridden from POSITIONABLE:GetPositionVec3 return nil end ---- Returns if the Group is alive. +--- Returns if the group is alive. -- The Group must: -- -- * Exist at run-time. -- * Has at least one unit. -- --- When the first @{Wrapper.Unit} of the Group is active, it will return true. --- If the first @{Wrapper.Unit} of the Group is inactive, it will return false. +-- When the first @{Wrapper.Unit} of the group is active, it will return true. +-- If the first @{Wrapper.Unit} of the group is inactive, it will return false. -- -- @param #GROUP self --- @return #boolean true if the Group is alive and active. --- @return #boolean false if the Group is alive but inactive. +-- @return #boolean true if the group is alive and active. +-- @return #boolean false if the group is alive but inactive. -- @return #nil if the group does not exist anymore. function GROUP:IsAlive() self:F2( self.GroupName ) @@ -237,6 +237,26 @@ function GROUP:IsAlive() return nil end +--- Returns if the group is activated. +-- @param #GROUP self +-- @return #boolean true if group is activated. +-- @return #nil The group is not existing or alive. +function GROUP:IsActive() + self:F2( self.GroupName ) + + local DCSGroup = self:GetDCSObject() -- DCS#Group + + if DCSGroup then + + local GroupIsActive = DCSGroup:getUnit(1):isActive() + return GroupIsActive + end + + return nil +end + + + --- Destroys the DCS Group and all of its DCS Units. -- Note that this destroy method also can raise a destroy event at run-time. -- So all event listeners will catch the destroy event of this group for each unit in the group. From b7644efea5cec880a34a0b56a2f864770901765d Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Tue, 11 Sep 2018 23:51:18 +0200 Subject: [PATCH 345/420] Warehouse v0.4.2 Improved destroyed group handling. Many other fixes. --- .../Moose/Functional/Warehouse.lua | 1487 +++++++++-------- 1 file changed, 822 insertions(+), 665 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index abaa4d385..8aa6dbb82 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -991,13 +991,13 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.4.1alpha" +WAREHOUSE.version="0.4.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Case when all transports are killed and there is still cargo to be delivered. Put cargo back into warehouse. +-- DONE: Case when all transports are killed and there is still cargo to be delivered. Put cargo back into warehouse. -- TODO: Spawn assets only virtually, i.e. remove requested assets from stock but do NOT spawn them ==> Interface to A2A dispatcher! Maybe do a negative sign on asset number? -- TODO: Test capturing a neutral warehouse. -- TODO: Make more examples: ARTY, CAP, ... @@ -1111,16 +1111,16 @@ function WAREHOUSE:New(warehouse, alias) self:AddTransition("*", "Delivered", "*") -- All cargo groups of a request have been delivered to the requesting warehouse. self:AddTransition("Running", "SelfRequest", "*") -- Request to warehouse itself. Requested assets are only spawned but not delivered anywhere. self:AddTransition("Attacked", "SelfRequest", "*") -- Request to warehouse itself. Also possible when warehouse is under attack! - self:AddTransition("Running", "Pause", "Paused") -- DONE Pause the processing of new requests. Still possible to add assets and requests. - self:AddTransition("Paused", "Unpause", "Running") -- DONE Unpause the warehouse. Queued requests are processed again. - self:AddTransition("*", "Stop", "Stopped") -- DONE Stop the warehouse. + self:AddTransition("Running", "Pause", "Paused") -- Pause the processing of new requests. Still possible to add assets and requests. + self:AddTransition("Paused", "Unpause", "Running") -- Unpause the warehouse. Queued requests are processed again. + self:AddTransition("*", "Stop", "Stopped") -- Stop the warehouse. self:AddTransition("*", "Save", "*") -- TODO Save the warehouse state to disk. - self:AddTransition("*", "Attacked", "Attacked") -- DONE Warehouse is under attack by enemy coalition. - self:AddTransition("Attacked", "Defeated", "Running") -- DONE Attack by other coalition was defeated! - self:AddTransition("Attacked", "Captured", "Running") -- DONE Warehouse was captured by another coalition. It must have been attacked first. - self:AddTransition("*", "AirbaseCaptured", "*") -- DONE Airbase was captured by other coalition. - self:AddTransition("*", "AirbaseRecaptured", "*") -- DONE Airbase was re-captured from other coalition. - self:AddTransition("*", "Destroyed", "Destoyed") -- DONE Warehouse was destroyed. All assets in stock are gone and warehouse is stopped. + self:AddTransition("*", "Attacked", "Attacked") -- Warehouse is under attack by enemy coalition. + self:AddTransition("Attacked", "Defeated", "Running") -- Attack by other coalition was defeated! + self:AddTransition("Attacked", "Captured", "Running") -- Warehouse was captured by another coalition. It must have been attacked first. + self:AddTransition("*", "AirbaseCaptured", "*") -- Airbase was captured by other coalition. + self:AddTransition("*", "AirbaseRecaptured", "*") -- Airbase was re-captured from other coalition. + self:AddTransition("*", "Destroyed", "Destoyed") -- Warehouse was destroyed. All assets in stock are gone and warehouse is stopped. ------------------------ --- Pseudo Functions --- @@ -1876,7 +1876,7 @@ end -- @param #WAREHOUSE.Pendingitem request The request from which the assignment is extracted. -- @return #string The assignment text. function WAREHOUSE:GetAssignment(request) - return request.assignment + return tostring(request.assignment) end ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -2030,7 +2030,7 @@ function WAREHOUSE:onafterStatus(From, Event, To) -- Check if any pending jobs are done and can be deleted from the self:_JobDone() - + -- Print status. self:_DisplayStatus() @@ -2074,12 +2074,14 @@ function WAREHOUSE:onafterStatus(From, Event, To) end ---- Function that checks if a pending job is done and can be removed from queue +--- Function that checks if a pending job is done and can be removed from queue. -- @param #WAREHOUSE self function WAREHOUSE:_JobDone() - -- Loop over all pending requests of this warehouse. + -- For jobs that are done, i.e. all cargo and transport assets are delivered, home or dead! local done={} + + -- Loop over all pending requests of this warehouse. for _,request in pairs(self.pending) do local request=request --#WAREHOUSE.Pendingitem @@ -2093,84 +2095,146 @@ function WAREHOUSE:_JobDone() local ntransport=0 if request.transportgroupset then ntransport=request.transportgroupset:Count() - end + end - if ncargo==0 and ntransport==0 then + local ncargotot=request.nasset + local ncargodelivered=request.ndelivered - --------------- - -- Job done! -- - --------------- - - self:E(string.format("Request id=%d done! No more cargo and transport assets alive.", request.uid)) - table.insert(done, request) - - elseif ncargo>0 and ntransport==0 and request.ntransport>0 then + -- Dead cargo: Ndead=Ntot-Ndeliverd-Nalive, + local ncargodead=ncargotot-ncargodelivered-ncargo - ----------------------------------- - -- Still cargo but no transports -- - ----------------------------------- - - -- Info message. - self:_InfoMessage(string.format("Warehouse %s: All transports of request id=%s dead! Putting remaining %s cargo assets back into warehouse!", self.alias, request.uid, ncargo)) - -- All transports are dead but there is still cargo left ==> Put cargo back into stock. - for _,group in pairs(request.cargogroupset:GetSetObjects()) do - local group=group --Wrapper.Group#GROUP - - -- Check if group is alive. - if group and group:IsAlive() then - - if self.Debug then - group:SmokeRed() - end - - -- Add all assets back to stock - if group:IsPartlyOrCompletelyInZone(self.spawnzone) then - self:__AddAsset(5, group) - end - end - + local ntransporttot=request.ntransport + local ntransporthome=request.ntransporthome + + -- Dead transport: Ndead=Ntot-Nhome-Nalive. + local ntransportdead=ntransporttot-ntransporthome-ntransport + + local text=string.format("Request id=%d: Cargo: Ntot=%d, Nalive=%d, Ndelivered=%d, Ndead=%d | Transport: Ntot=%d, Nalive=%d, Nhome=%d, Ndead=%d", + request.uid, ncargotot, ncargo, ncargodelivered, ncargodead, ntransporttot, ntransport, ntransporthome, ntransportdead) + self:T(self.wid..text) + + + -- Handle different cases depending on what asset are still around. + if ncargo==0 then + --------------------- + -- Cargo delivered -- + --------------------- + + -- Trigger delivered event. + if not self.delivered[request.uid] then + self:Delivered(request) end - - elseif ncargo==0 and ntransport>0 then - - ----------------------------------- - -- No cargo but still transports -- - ----------------------------------- - - -- This is difficult! How do I know if transports were unused? They could also be just on their way back home. - - -- TODO: Handle this case. - if true then - return - end - - -- Info message. - self:_InfoMessage(string.format("Warehouse %s: No cargo assets left for request id=%s. Putting remaining %s transport assets back into warehouse!", self.alias, request.uid, ntransport)) - -- All transports are dead but there is still cargo left ==> Put cargo back into stock. - for _,group in pairs(request.transportgroupset:GetSetObjects()) do - local group=group --Wrapper.Group#GROUP + -- Check if transports are back home? + if ntransport==0 then + --------------- + -- Job done! -- + --------------- + + self:I(string.format("Request id=%d done! No more cargo or transport assets alive.", request.uid)) + table.insert(done, request) - -- Check if group is alive. - if group and group:IsAlive() then - - -- Assets back to stock. - if group:IsPartlyOrCompletelyInZone(self.spawnzone) then - - if self.Debug then - group:SmokeRed() + else + ----------------------------------- + -- No cargo but still transports -- + ----------------------------------- + + -- This is difficult! How do I know if transports were unused? They could also be just on their way back home. + + -- All transports are dead but there is still cargo left ==> Put cargo back into stock. + for _,_group in pairs(request.transportgroupset:GetSetObjects()) do + local group=_group --Wrapper.Group#GROUP + + -- Check if group is alive. + if group and group:IsAlive() then + + -- Check if group is in the spawn zone? + local category=group:GetCategory() + + -- Get current speed. + local speed=group:GetVelocityKMH() + local notmoving=speed<1 + + -- Closest airbase. + local airbase=group:GetCoordinate():GetClosestAirbase():GetName() + local athomebase=self.airbase and self.airbase:GetName()==airbase + + -- On ground + local onground=not group:InAir() + + -- In spawn zone. + local inspawnzone=group:IsPartlyOrCompletelyInZone(self.spawnzone) + + local ishome=false + if category==Group.Category.GROUND or category==Group.Category.HELICOPTER then + -- Units go back to the spawn zone, helicopters land and they should not move any more. + ishome=inspawnzone and onground and notmoving + elseif category==Group.Category.AIRPLANE then + -- Planes need to be on ground at their home airbase and should not move any more. + ishome=athomebase and onground and notmoving end - - self:__AddAsset(5, group) - end + + -- Debug text. + local text=string.format("Group %s: speed=%d km/h, onground=%s , airbase=%s, spawnzone=%s ==> ishome=%s", group:GetName(), speed, tostring(onground), airbase, tostring(inspawnzone), tostring(ishome)) + self:E(self.wid..text) + + if ishome then + + -- Info message. + self:_InfoMessage(string.format("Warehouse %s: No cargo assets left for request id=%s. Remaining %s transport assets go back into stock!", self.alias, request.uid, ntransport)) + + if self.Debug then + group:SmokeRed() + end + + -- Group arrived. + self:Arrived(group) + end + end end - + end - + + else + + if ntransport==0 and request.ntransport>0 then + ----------------------------------- + -- Still cargo but no transports -- + ----------------------------------- + + -- Info message. + self:_InfoMessage(string.format("Warehouse %s: All transports of request id=%s dead! Putting remaining %s cargo assets back into warehouse!", self.alias, request.uid, ncargo)) + + -- All transports are dead but there is still cargo left ==> Put cargo back into stock. + for _,_group in pairs(request.cargogroupset:GetSetObjects()) do + --local group=group --Wrapper.Group#GROUP + + -- These groups have been respawned as cargo, i.e. their name changed! + local groupname=_group:GetName() + local group=GROUP:FindByName(groupname.."#CARGO") + + -- Check if group is alive. + if group and group:IsAlive() then + + -- Check if group is in spawn zone? + if group:IsPartlyOrCompletelyInZone(self.spawnzone) then + -- Debug smoke. + if self.Debug then + group:SmokeBlue() + end + -- Add asset group back to stock. + --env.info(string.format("FF add asset group %s in function JobDone", group:GetName())) + self:AddAsset(group) + end + end + + end + end + end - end + end -- loop over requests -- Remove pending requests if done. for _,request in pairs(done) do @@ -2196,6 +2260,8 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu if type(group)=="string" then group=GROUP:FindByName(group) end + + --TODO: What happens if the name of the group is wrong? Saw strange behaviour! if group then @@ -2204,35 +2270,51 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu if wid and aid and rid then --------------------------- - -- This is a known asset -- + -- This is a KNOWN asset -- --------------------------- - if true then - - -- Get the asset from the global DB. - local asset=self:_FindAssetInDB(group) + -- Get the original warehouse this group belonged to. + local warehouse=self:_FindWarehouseInDB(wid) + if warehouse then + local request=warehouse:_GetRequestOfGroup(group, warehouse.pending) + if request then - -- Note the group is only added once, i.e. the ngroups parameter is ignored here. - -- This is because usually these request comes from an asset that has been transfered from another warehouse and hence should only be added once. - if asset~=nil then - self:_DebugMessage(string.format("Adding known asset uid=%d, attribute = %s to warehouse stock.", asset.uid, asset.attribute), 5) - table.insert(self.stock, asset) - else - self:_ErrorMessage(string.format("ERROR known asset could not be found in global warehouse db!"), 0) - end - - else - -- Request did not exist! - self:E("ERROR: Request does not exist in addAsset! This should not happen!") + -- Increase number of cargo delivered and transports home. + local istransport=warehouse:_GroupIsTransport(group,request) + if istransport==true then + request.ntransporthome=request.ntransporthome+1 + request.transportgroupset:Remove(group:GetName(), true) + self:I(warehouse.wid..string.format("FF Transport %d of %s returned home.", request.ntransporthome, tostring(request.ntransport))) + elseif istransport==false then + request.ndelivered=request.ndelivered+1 + request.cargogroupset:Remove(self:_GetNameWithOut(group), true) + self:I(warehouse.wid..string.format("FF Cargo %d of %s delivered.", request.ndelivered, tostring(request.nasset))) + else + self:E(warehouse.wid..string.format("ERROR: Group %s is neither cargo nor transport!", group:GetName())) + end + + end end + -- Get the asset from the global DB. + local asset=self:_FindAssetInDB(group) + + -- Note the group is only added once, i.e. the ngroups parameter is ignored here. + -- This is because usually these request comes from an asset that has been transfered from another warehouse and hence should only be added once. + if asset~=nil then + self:_DebugMessage(string.format("Warehouse %s: Adding KNOWN asset uid=%d with attribute=%s to stock.", self.alias, asset.uid, asset.attribute), 5) + table.insert(self.stock, asset) + else + self:_ErrorMessage(string.format("ERROR: Known asset could not be found in global warehouse db!"), 0) + end + else ------------------------- -- This is a NEW asset -- ------------------------- -- Debug info. - self:_DebugMessage(self.wid..string.format("Adding %d NEW assets of group %s to warehouse %s.", n, tostring(group:GetName()), self.alias), 5) + self:_DebugMessage(self.wid..string.format("Warehouse %s: Adding %d NEW assets of group %s to stock.", self.alias, n, tostring(group:GetName())), 5) -- This is a group that is not in the db yet. Add it n times. local assets=self:_RegisterAsset(group, n, forceattribute) @@ -2253,76 +2335,10 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu end -- Update status. - self:__Status(-1) + --self:__Status(-1) end ---- Update the pending requests by removing assets that have arrived. --- @param #WAREHOUSE self --- @param Wrapper.Group#GROUP group The group that has arrived at its destination. --- @return #WAREHOUSE.Pendingitem The updated request from the pending queue. --- @return #boolean If true, group is a cargo asset. If false, group is a transport asset. If nil, group is neither cargo nor transport. -function WAREHOUSE:_UpdatePending(group) - - -- Get request from group name. - local request=self:_GetRequestOfGroup(group, self.pending) - - -- Get the IDs for this group. In particular, we use the asset ID to figure out which group was delivered. - local wid,aid,rid=self:_GetIDsFromGroup(group) - - local isCargo=nil - - if request then - - -- If this request was already delivered. - if self.delivered[rid]==true then - - -- Loop over transport groups. - for _,_transportgroup in pairs(request.transportgroupset:GetSetObjects()) do - local transportgroup=_transportgroup --Wrapper.Group#GROUP - - -- IDs of cargo group. - local cwid,caid,crid=self:_GetIDsFromGroup(transportgroup) - - -- Remove group from transport group set and increase home counter. - if caid==aid then - request.transportgroupset:Remove(transportgroup:GetName()) - request.ntransporthome=request.ntransporthome+1 - env.info(string.format("Transport back home #%s", request.ntransporthome)) - isCargo=false - break - end - end - - else - - -- Loop over cargo groups. - for _,_cargogroup in pairs(request.cargogroupset:GetSetObjects()) do - local cargogroup=_cargogroup --Wrapper.Group#GROUP - - -- IDs of cargo group. - local cwid,caid,crid=self:_GetIDsFromGroup(cargogroup) - - -- Remove group from cargo group set and increase delivered counter. - if caid==aid then - request.cargogroupset:Remove(cargogroup:GetName()) - request.ndelivered=request.ndelivered+1 - env.info(string.format("FF delivered cargo # ", request.ndelivered)) - isCargo=true - break - end - end - - end - else - self:E(self.wid..string.format("WARNING: pending request could not be updated since request did not exist in pending queue!")) - end - - return request, isCargo -end - - - --- Find an asset in the the global warehouse db. -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group The group from which it is assumed that it has a registered asset. @@ -2478,246 +2494,6 @@ function WAREHOUSE:_AssetItemInfo(asset) self:T3({Template=asset.template}) end - ---- Spawn a ground or naval asset in the corresponding spawn zone of the warehouse. --- @param #WAREHOUSE self --- @param #string alias Alias name of the asset group. --- @param #WAREHOUSE.Assetitem asset Ground asset that will be spawned. --- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. --- @param Core.Zone#ZONE spawnzone Zone where the assets should be spawned. --- @param boolean aioff If true, AI of ground units are set to off. --- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned. -function WAREHOUSE:_SpawnAssetGroundNaval(alias, asset, request, spawnzone, aioff) - - if asset and (asset.category==Group.Category.GROUND or asset.category==Group.Category.SHIP) then - - -- Prepare spawn template. - local template=self:_SpawnAssetPrepareTemplate(asset, alias) - - -- Initial spawn point. - template.route.points[1]={} - - -- Get a random coordinate in the spawn zone. - local coord=spawnzone:GetRandomCoordinate() - - --spawnzone:SmokeZone(1, 30) - - -- Translate the position of the units. - for i=1,#template.units do - - -- Unit template. - local unit = template.units[i] - - -- Translate position. - local SX = unit.x or 0 - local SY = unit.y or 0 - local BX = asset.template.route.points[1].x - local BY = asset.template.route.points[1].y - local TX = coord.x + (SX-BX) - local TY = coord.z + (SY-BY) - - template.units[i].x = TX - template.units[i].y = TY - - end - - template.route.points[1].x = coord.x - template.route.points[1].y = coord.z - - template.x = coord.x - template.y = coord.z - template.alt = coord.y - - -- Spawn group. - local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP - - -- Activate group. Should only be necessary for late activated groups. - --group:Activate() - - -- Switch AI off if desired. This works only for ground and naval groups. - if aioff then - group:SetAIOff() - end - - return group - end - - return nil -end - ---- Spawn an aircraft asset (plane or helo) at the airbase associated with the warehouse. --- @param #WAREHOUSE self --- @param #string alias Alias name of the asset group. --- @param #WAREHOUSE.Assetitem asset Ground asset that will be spawned. --- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. --- @param #table parking Parking data for this asset. --- @param #boolean uncontrolled Spawn aircraft in uncontrolled state. --- @param #boolean hotstart Spawn aircraft with engines already on. Default is a cold start with engines off. --- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned. -function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrolled, hotstart) - - if asset and asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then - - -- Prepare the spawn template. - local template=self:_SpawnAssetPrepareTemplate(asset, alias) - - -- Set route points. - if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then - - -- Get flight path if the group goes to another warehouse by itself. - template.route.points=self:_GetFlightplan(asset, self.airbase, request.warehouse.airbase) - - else - - -- Cold start (default). - local _type=COORDINATE.WaypointType.TakeOffParking - local _action=COORDINATE.WaypointAction.FromParkingArea - - -- Hot start. - if hotstart then - _type=COORDINATE.WaypointType.TakeOffParkingHot - _action=COORDINATE.WaypointAction.FromParkingAreaHot - end - - -- First route point is the warehouse airbase. - template.route.points[1]=self.airbase:GetCoordinate():WaypointAir("BARO",_type,_action, 0, true, self.airbase, nil, "Spawnpoint") - - end - - -- Get airbase ID and category. - local AirbaseID = self.airbase:GetID() - local AirbaseCategory = self.category - - -- Check enough parking spots. - if AirbaseCategory==Airbase.Category.HELIPAD or AirbaseCategory==Airbase.Category.SHIP then - --TODO Figure out what's necessary in this case. - - else - - if #parking<#template.units then - local text=string.format("ERROR: Not enough parking! Free parking = %d < %d aircraft to be spawned.", #parking, #template.units) - self:_DebugMessage(text) - return nil - end - - end - - -- Position the units. - for i=1,#template.units do - - -- Unit template. - local unit = template.units[i] - - if AirbaseCategory == Airbase.Category.HELIPAD or AirbaseCategory == Airbase.Category.SHIP then - - -- Helipads we take the position of the airbase location, since the exact location of the spawn point does not make sense. - local coord=self.airbase:GetCoordinate() - - unit.x=coord.x - unit.y=coord.z - unit.alt=coord.y - - unit.parking_id = nil - unit.parking = nil - - else - - local coord=parking[i].Coordinate --Core.Point#COORDINATE - local terminal=parking[i].TerminalID --#number - - if self.Debug then - coord:MarkToAll(string.format("Spawnplace unit %s terminal %d.", unit.name, terminal)) - end - - unit.x=coord.x - unit.y=coord.z - unit.alt=coord.y - - unit.parking_id = nil - unit.parking = terminal - - end - end - - -- And template position. - template.x = template.units[1].x - template.y = template.units[1].y - - -- DCS bug workaround. Spawning helos in uncontrolled state on carriers causes a big spash! - -- See https://forums.eagle.ru/showthread.php?t=219550 - if AirbaseCategory == Airbase.Category.SHIP and asset.category==Group.Category.HELICOPTER then - uncontrolled=false - end - - -- Uncontrolled spawning. - template.uncontrolled=uncontrolled - - -- Debug info. - self:T2({airtemplate=template}) - - -- Spawn group. - local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP - - -- Activate group - should only be necessary for late activated groups. - --group:Activate() - - return group - end - - return nil -end - - ---- Prepare a spawn template for the asset. Deep copy of asset template, adjusting template and unit names, nillifying group and unit ids. --- @param #WAREHOUSE self --- @param #WAREHOUSE.Assetitem asset Ground asset that will be spawned. --- @param #string alias Alias name of the group. --- @return #table Prepared new spawn template. -function WAREHOUSE:_SpawnAssetPrepareTemplate(asset, alias) - - -- Create an own copy of the template! - local template=UTILS.DeepCopy(asset.template) - - -- Set unique name. - template.name=alias - - -- Set current(!) coalition and country. - template.CoalitionID=self.coalition - template.CountryID=self.country - - -- Nillify the group ID. - template.groupId=nil - - -- For group units, visible needs to be false. - if asset.category==Group.Category.GROUND then - --template.visible=false - end - - -- No late activation. - template.lateActivation=false - - -- Set and empty route. - template.route = {} - template.route.routeRelativeTOT=true - template.route.points = {} - - -- Handle units. - for i=1,#template.units do - - -- Unit template. - local unit = template.units[i] - - -- Nillify the unit ID. - unit.unitId=nil - - -- Set unit name: -01, -02, ... - unit.name=string.format("%s-%02d", template.name , i) - - end - - return template -end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- On before "AddRequest" event. Checks some basic properties of the given parameters. @@ -3075,7 +2851,8 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Spawn Transport Groups -- ---------------------------- - -- Spawn the transport groups. + -- Spawn the transport groups. + env.info("FF Request ntransport = "..Request.ntransport) for i=1,Request.ntransport do -- Get stock item. @@ -3226,35 +3003,56 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Dispatcher Event Functions -- -------------------------------- - function CargoTransport:OnAfterPickedUp(From,Event,To,CarrierGroup,PickupZone) - local text=string.format("FF Carrier group %s picked up event in pickup zone %s.", CarrierGroup:GetName(), PickupZone:GetName()) - env.info(text) + --- Function called after carrier picked up something. + function CargoTransport:OnAfterPickedUp(From, Event, To, Carrier, PickupZone) + + -- Get warehouse state. + local warehouse=Carrier:GetState(Carrier, "WAREHOUSE") --#WAREHOUSE + + -- Debug message. + local text=string.format("Carrier group %s picked up at pickup zone %s.", Carrier:GetName(), PickupZone:GetName()) + warehouse:T(warehouse.wid..text) + end - function CargoTransport:OnAfterDeployed(From,Event,To,CarrierGroup,DeployZone) - local text=string.format("FF Carrier group %s deployed event for deploy zone %s.", CarrierGroup:GetName(), DeployZone:GetName()) - env.info(text) + --- Function called if something was deployed. + function CargoTransport:OnAfterDeployed(From, Event, To, Carrier, DeployZone) + + -- Get warehouse state. + local warehouse=Carrier:GetState(Carrier, "WAREHOUSE") --#WAREHOUSE + + -- Debug message. + -- TODO: Depoloy zone is nil! + --local text=string.format("Carrier group %s deployed at deploy zone %s.", Carrier:GetName(), DeployZone:GetName()) + --warehouse:T(warehouse.wid..text) + end - function CargoTransport:OnAfterHome(From, Event, To, CarrierGroup, Coordinate, Speed, HomeZone) - local text=string.format("FF Carrier group %s going home to zone %s.", CarrierGroup:GetName(), HomeZone:GetName()) - env.info(text) + --- Function called if carrier group is going home. + function CargoTransport:OnAfterHome(From, Event, To, Carrier, Coordinate, Speed, HomeZone) + + -- Get warehouse state. + local warehouse=Carrier:GetState(Carrier, "WAREHOUSE") --#WAREHOUSE + + -- Debug message. + local text=string.format("Carrier group %s going home to zone %s.", Carrier:GetName(), HomeZone:GetName()) + warehouse:T(warehouse.wid..text) + end --- Function called when a carrier unit has loaded a cargo group. function CargoTransport:OnAfterLoaded(From, Event, To, Carrier, Cargo, CarrierUnit, PickupZone) - local text=string.format("FF Carrier group %s loaded cargo %s into unit %s in pickup zone %s", Carrier:GetName(), Cargo:GetObject():GetName(), CarrierUnit:GetName(), PickupZone:GetName()) - env.info(text) - - -- Get group object. - local group=Cargo:GetObject() --Wrapper.Group#GROUP - + -- Get warehouse state. local warehouse=Carrier:GetState(Carrier, "WAREHOUSE") --#WAREHOUSE - - local text=string.format("FF Group %s was loaded into carrier %s.", tostring(group:GetName()), tostring(Carrier:GetName())) - env.info(text) + + -- Debug message. + local text=string.format("Carrier group %s loaded cargo %s into unit %s in pickup zone %s", Carrier:GetName(), Cargo:GetObject():GetName(), CarrierUnit:GetName(), PickupZone:GetName()) + warehouse:T(warehouse.wid..text) + -- Get cargo group object. + local group=Cargo:GetObject() --Wrapper.Group#GROUP + -- Get request. local request=warehouse:_GetRequestOfGroup(group, warehouse.pending) @@ -3266,21 +3064,15 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) --- Function called when cargo has arrived and was unloaded. function CargoTransport:OnAfterUnloaded(From, Event, To, Carrier, Cargo, CarrierUnit, DeployZone) - self:I("FF OnAfterUnloaded:") - self:I({From=From}) - self:I({Event=Event}) - self:I({To=To}) - self:I({Carrier=Carrier}) - self:I({Cargo=Cargo}) - - -- Get group obejet. - local group=Cargo:GetObject() --Wrapper.Group#GROUP - -- Get warehouse state. local warehouse=Carrier:GetState(Carrier, "WAREHOUSE") --#WAREHOUSE - local text=string.format("FF Group %s was unloaded from carrier %s.", tostring(group:GetName()), tostring(Carrier:GetName())) - env.info(text) + -- Get group obejet. + local group=Cargo:GetObject() --Wrapper.Group#GROUP + + -- Debug message. + local text=string.format("Cargo group %s was unloaded from carrier unit %s.", tostring(group:GetName()), tostring(CarrierUnit:GetName())) + warehouse:T(warehouse.wid..text) -- Load the cargo in the warehouse. --Cargo:Load(warehouse.warehouse) @@ -3304,9 +3096,6 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) MESSAGE:New(text, 5):ToAllIf(warehouse.Debug) warehouse:I(warehouse.wid..text) - -- Add carrier back to warehouse stock. Actual unit is destroyed. - --warehouse:AddAsset(Carrier) - -- Call arrived event for carrier. warehouse:__Arrived(1, Carrier) @@ -3317,108 +3106,6 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) end - ---- Spawns requested assets at warehouse or associated airbase. --- @param #WAREHOUSE self --- @param #WAREHOUSE.Queueitem Request Information table of the request. --- @return Core.Set#SET_GROUP Set of groups that were spawned. -function WAREHOUSE:_SpawnAssetRequest(Request) - self:E({requestUID=Request.uid}) - - -- Shortcut to cargo assets. - local _assetstock=Request.cargoassets - - -- General type and category. - local _cargotype=Request.cargoattribute --#WAREHOUSE.Attribute - local _cargocategory=Request.cargocategory --DCS#Group.Category - - -- Now we try to find all parking spots for all cargo groups in advance. Due to the for loop, the parking spots do not get updated while spawning. - local Parking={} - if _cargocategory==Group.Category.AIRPLANE or _cargocategory==Group.Category.HELICOPTER then - Parking=self:_FindParkingForAssets(self.airbase,_assetstock) or {} - end - - -- Spawn aircraft in uncontrolled state if request comes from the same warehouse. - local UnControlled=false - local AIOnOff=true - if Request.toself then - UnControlled=true - AIOnOff=false - end - - -- Create an empty group set. - local _groupset=SET_GROUP:New() - - -- Table for all spawned assets. - local _assets={} - - -- Loop over cargo requests. - for i=1,#_assetstock do - - -- Get stock item. - local _assetitem=_assetstock[i] --#WAREHOUSE.Assetitem - - -- Alias of the group. - local _alias=self:_Alias(_assetitem, Request) - - -- Spawn an asset group. - local _group=nil --Wrapper.Group#GROUP - if _assetitem.category==Group.Category.GROUND then - - -- Spawn ground troops. - _group=self:_SpawnAssetGroundNaval(_alias,_assetitem, Request, self.spawnzone) - - elseif _assetitem.category==Group.Category.AIRPLANE or _assetitem.category==Group.Category.HELICOPTER then - - -- Spawn air units. - if Parking[_assetitem.uid] then - _group=self:_SpawnAssetAircraft(_alias,_assetitem, Request, Parking[_assetitem.uid], UnControlled) - else - _group=self:_SpawnAssetAircraft(_alias,_assetitem, Request, nil, UnControlled) - end - - elseif _assetitem.category==Group.Category.TRAIN then - - -- Spawn train. - if self.rail then - --TODO: Rail should only get one asset because they would spawn on top! - --_group=_spawn:SpawnFromCoordinate(self.rail) - end - - self:E(self.wid.."ERROR: Spawning of TRAIN assets not possible yet!") - - elseif _assetitem.category==Group.Category.SHIP then - - -- Spawn naval assets. - _group=self:_SpawnAssetGroundNaval(_alias,_assetitem, Request, self.portzone) - - else - self:E(self.wid.."ERROR: Unknown asset category!") - end - - -- Add group to group set and asset list. - if _group then - _groupset:AddGroup(_group) - table.insert(_assets, _assetitem) - else - self:E(self.wid.."ERROR: Cargo asset could not be spawned!") - end - - end - - -- Delete spawned items from warehouse stock. - for _,_asset in pairs(_assets) do - local asset=_asset --#WAREHOUSE.Assetitem - Request.assets[asset.uid]=asset - self:_DeleteStockItem(asset) - end - - -- Overwrite the assets with the actually spawned ones. - Request.cargoassets=_assets - - return _groupset -end - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- On after "Unloaded" event. Triggered when a group was unloaded from the carrier. @@ -3486,8 +3173,14 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) -- Get the right warehouse to put the asset into -- Transports go back to the warehouse which called this function while cargo goes into the receiving warehouse. local warehouse=request.warehouse - if self:_GroupIsTransport(group,request) then + local istransport=self:_GroupIsTransport(group,request) + if istransport==true then warehouse=self + elseif istransport==false then + warehouse=request.warehouse + else + self:E(self.wid..string.format("ERROR: Group %s is neither cargo nor transport", group:GetName())) + return end -- Debug message. @@ -3497,9 +3190,25 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) if group:IsGround() and group:GetSpeedMax()>1 then group:RouteGroundTo(warehouse.coordinate, group:GetSpeedMax()*0.3, "Off Road") end + + -- Increase number of cargo delivered and transports home. + local istransport=warehouse:_GroupIsTransport(group,request) + if istransport==true then + request.ntransporthome=request.ntransporthome+1 + request.transportgroupset:Remove(group:GetName(), true) + self:I(warehouse.wid..string.format("FF Transport %d of %s returned home.", request.ntransporthome, tostring(request.ntransport))) + elseif istransport==false then + request.ndelivered=request.ndelivered+1 + request.cargogroupset:Remove(self:_GetNameWithOut(group), true) + self:I(warehouse.wid..string.format("FF Cargo %d of %s delivered.", request.ndelivered, tostring(request.nasset))) + else + self:E(warehouse.wid..string.format("ERROR: Group %s is neither cargo nor transport!", group:GetName())) + end -- Move asset from pending queue into new warehouse. warehouse:__AddAsset(60, group) + env.info(string.format("FF add asset group %s in function onafterArrived in 60 seconds", group:GetName())) + --warehouse:AddAsset(group) end @@ -3507,7 +3216,7 @@ end --- Get asset from group and request. -- @param #WAREHOUSE self --- @param Wrapper.Group#GROUP group The group that has arrived at its destination. +-- @param Wrapper.Group#GROUP group The group for which the asset should be obtained. -- @param #WAREHOUSE.Pendingitem request Pending request. -- @return #WAREHOUSE.Assetitem The asset. function WAREHOUSE:_GetAssetFromGroupRequest(group,request) @@ -3787,6 +3496,352 @@ function WAREHOUSE:onafterDestroyed(From, Event, To) self:__Stop(60) end +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Spawn functions +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Spawns requested assets at warehouse or associated airbase. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE.Queueitem Request Information table of the request. +-- @return Core.Set#SET_GROUP Set of groups that were spawned. +function WAREHOUSE:_SpawnAssetRequest(Request) + self:E({requestUID=Request.uid}) + + -- Shortcut to cargo assets. + local _assetstock=Request.cargoassets + + -- General type and category. + local _cargotype=Request.cargoattribute --#WAREHOUSE.Attribute + local _cargocategory=Request.cargocategory --DCS#Group.Category + + -- Now we try to find all parking spots for all cargo groups in advance. Due to the for loop, the parking spots do not get updated while spawning. + local Parking={} + if _cargocategory==Group.Category.AIRPLANE or _cargocategory==Group.Category.HELICOPTER then + Parking=self:_FindParkingForAssets(self.airbase,_assetstock) or {} + end + + -- Spawn aircraft in uncontrolled state if request comes from the same warehouse. + local UnControlled=false + local AIOnOff=true + if Request.toself then + UnControlled=true + AIOnOff=false + end + + -- Create an empty group set. + local _groupset=SET_GROUP:New() + + -- Table for all spawned assets. + local _assets={} + + -- Loop over cargo requests. + for i=1,#_assetstock do + + -- Get stock item. + local _assetitem=_assetstock[i] --#WAREHOUSE.Assetitem + + -- Alias of the group. + local _alias=self:_Alias(_assetitem, Request) + + -- Spawn an asset group. + local _group=nil --Wrapper.Group#GROUP + if _assetitem.category==Group.Category.GROUND then + + -- Spawn ground troops. + _group=self:_SpawnAssetGroundNaval(_alias,_assetitem, Request, self.spawnzone) + + elseif _assetitem.category==Group.Category.AIRPLANE or _assetitem.category==Group.Category.HELICOPTER then + + -- Spawn air units. + if Parking[_assetitem.uid] then + _group=self:_SpawnAssetAircraft(_alias,_assetitem, Request, Parking[_assetitem.uid], UnControlled) + else + _group=self:_SpawnAssetAircraft(_alias,_assetitem, Request, nil, UnControlled) + end + + elseif _assetitem.category==Group.Category.TRAIN then + + -- Spawn train. + if self.rail then + --TODO: Rail should only get one asset because they would spawn on top! + --_group=_spawn:SpawnFromCoordinate(self.rail) + end + + self:E(self.wid.."ERROR: Spawning of TRAIN assets not possible yet!") + + elseif _assetitem.category==Group.Category.SHIP then + + -- Spawn naval assets. + _group=self:_SpawnAssetGroundNaval(_alias,_assetitem, Request, self.portzone) + + else + self:E(self.wid.."ERROR: Unknown asset category!") + end + + -- Add group to group set and asset list. + if _group then + _groupset:AddGroup(_group) + table.insert(_assets, _assetitem) + else + self:E(self.wid.."ERROR: Cargo asset could not be spawned!") + end + + end + + -- Delete spawned items from warehouse stock. + for _,_asset in pairs(_assets) do + local asset=_asset --#WAREHOUSE.Assetitem + Request.assets[asset.uid]=asset + self:_DeleteStockItem(asset) + end + + -- Overwrite the assets with the actually spawned ones. + Request.cargoassets=_assets + + return _groupset +end + + +--- Spawn a ground or naval asset in the corresponding spawn zone of the warehouse. +-- @param #WAREHOUSE self +-- @param #string alias Alias name of the asset group. +-- @param #WAREHOUSE.Assetitem asset Ground asset that will be spawned. +-- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. +-- @param Core.Zone#ZONE spawnzone Zone where the assets should be spawned. +-- @param boolean aioff If true, AI of ground units are set to off. +-- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned. +function WAREHOUSE:_SpawnAssetGroundNaval(alias, asset, request, spawnzone, aioff) + + if asset and (asset.category==Group.Category.GROUND or asset.category==Group.Category.SHIP) then + + -- Prepare spawn template. + local template=self:_SpawnAssetPrepareTemplate(asset, alias) + + -- Initial spawn point. + template.route.points[1]={} + + -- Get a random coordinate in the spawn zone. + local coord=spawnzone:GetRandomCoordinate() + + --spawnzone:SmokeZone(1, 30) + + -- Translate the position of the units. + for i=1,#template.units do + + -- Unit template. + local unit = template.units[i] + + -- Translate position. + local SX = unit.x or 0 + local SY = unit.y or 0 + local BX = asset.template.route.points[1].x + local BY = asset.template.route.points[1].y + local TX = coord.x + (SX-BX) + local TY = coord.z + (SY-BY) + + template.units[i].x = TX + template.units[i].y = TY + + end + + template.route.points[1].x = coord.x + template.route.points[1].y = coord.z + + template.x = coord.x + template.y = coord.z + template.alt = coord.y + + -- Spawn group. + local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP + + -- Activate group. Should only be necessary for late activated groups. + --group:Activate() + + -- Switch AI off if desired. This works only for ground and naval groups. + if aioff then + group:SetAIOff() + end + + return group + end + + return nil +end + +--- Spawn an aircraft asset (plane or helo) at the airbase associated with the warehouse. +-- @param #WAREHOUSE self +-- @param #string alias Alias name of the asset group. +-- @param #WAREHOUSE.Assetitem asset Ground asset that will be spawned. +-- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. +-- @param #table parking Parking data for this asset. +-- @param #boolean uncontrolled Spawn aircraft in uncontrolled state. +-- @param #boolean hotstart Spawn aircraft with engines already on. Default is a cold start with engines off. +-- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned. +function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrolled, hotstart) + + if asset and asset.category==Group.Category.AIRPLANE or asset.category==Group.Category.HELICOPTER then + + -- Prepare the spawn template. + local template=self:_SpawnAssetPrepareTemplate(asset, alias) + + -- Set route points. + if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then + + -- Get flight path if the group goes to another warehouse by itself. + template.route.points=self:_GetFlightplan(asset, self.airbase, request.warehouse.airbase) + + else + + -- Cold start (default). + local _type=COORDINATE.WaypointType.TakeOffParking + local _action=COORDINATE.WaypointAction.FromParkingArea + + -- Hot start. + if hotstart then + _type=COORDINATE.WaypointType.TakeOffParkingHot + _action=COORDINATE.WaypointAction.FromParkingAreaHot + end + + -- First route point is the warehouse airbase. + template.route.points[1]=self.airbase:GetCoordinate():WaypointAir("BARO",_type,_action, 0, true, self.airbase, nil, "Spawnpoint") + + end + + -- Get airbase ID and category. + local AirbaseID = self.airbase:GetID() + local AirbaseCategory = self.category + + -- Check enough parking spots. + if AirbaseCategory==Airbase.Category.HELIPAD or AirbaseCategory==Airbase.Category.SHIP then + --TODO Figure out what's necessary in this case. + + else + + if #parking<#template.units then + local text=string.format("ERROR: Not enough parking! Free parking = %d < %d aircraft to be spawned.", #parking, #template.units) + self:_DebugMessage(text) + return nil + end + + end + + -- Position the units. + for i=1,#template.units do + + -- Unit template. + local unit = template.units[i] + + if AirbaseCategory == Airbase.Category.HELIPAD or AirbaseCategory == Airbase.Category.SHIP then + + -- Helipads we take the position of the airbase location, since the exact location of the spawn point does not make sense. + local coord=self.airbase:GetCoordinate() + + unit.x=coord.x + unit.y=coord.z + unit.alt=coord.y + + unit.parking_id = nil + unit.parking = nil + + else + + local coord=parking[i].Coordinate --Core.Point#COORDINATE + local terminal=parking[i].TerminalID --#number + + if self.Debug then + coord:MarkToAll(string.format("Spawnplace unit %s terminal %d.", unit.name, terminal)) + end + + unit.x=coord.x + unit.y=coord.z + unit.alt=coord.y + + unit.parking_id = nil + unit.parking = terminal + + end + end + + -- And template position. + template.x = template.units[1].x + template.y = template.units[1].y + + -- DCS bug workaround. Spawning helos in uncontrolled state on carriers causes a big spash! + -- See https://forums.eagle.ru/showthread.php?t=219550 + if AirbaseCategory == Airbase.Category.SHIP and asset.category==Group.Category.HELICOPTER then + uncontrolled=false + end + + -- Uncontrolled spawning. + template.uncontrolled=uncontrolled + + -- Debug info. + self:T2({airtemplate=template}) + + -- Spawn group. + local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP + + -- Activate group - should only be necessary for late activated groups. + --group:Activate() + + return group + end + + return nil +end + + +--- Prepare a spawn template for the asset. Deep copy of asset template, adjusting template and unit names, nillifying group and unit ids. +-- @param #WAREHOUSE self +-- @param #WAREHOUSE.Assetitem asset Ground asset that will be spawned. +-- @param #string alias Alias name of the group. +-- @return #table Prepared new spawn template. +function WAREHOUSE:_SpawnAssetPrepareTemplate(asset, alias) + + -- Create an own copy of the template! + local template=UTILS.DeepCopy(asset.template) + + -- Set unique name. + template.name=alias + + -- Set current(!) coalition and country. + template.CoalitionID=self.coalition + template.CountryID=self.country + + -- Nillify the group ID. + template.groupId=nil + + -- For group units, visible needs to be false. + if asset.category==Group.Category.GROUND then + --template.visible=false + end + + -- No late activation. + template.lateActivation=false + + -- Set and empty route. + template.route = {} + template.route.routeRelativeTOT=true + template.route.points = {} + + -- Handle units. + for i=1,#template.units do + + -- Unit template. + local unit = template.units[i] + + -- Nillify the unit ID. + unit.unitId=nil + + -- Set unit name: -01, -02, ... + unit.name=string.format("%s-%02d", template.name , i) + + end + + return template +end + + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- Routing functions ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -4061,6 +4116,7 @@ function WAREHOUSE:_OnEventLanding(EventData) -- Debug info. self:T(self.wid..string.format("Warehouse %s captured event landing of its asset unit %s.", self.alias, EventData.IniUnitName)) + --[[ -- Check if all cargo was delivered. if self.delivered[rid]==true then @@ -4081,6 +4137,7 @@ function WAREHOUSE:_OnEventLanding(EventData) end end + ]] end end @@ -4157,75 +4214,51 @@ end -- @param #WAREHOUSE self -- @param Core.Event#EVENTDATA EventData Event data. function WAREHOUSE:_OnEventCrashOrDead(EventData) - self:T3(self.wid..string.format("Warehouse %s captured event dead or crash!",self.alias)) + self:T3(self.wid..string.format("Warehouse %s captured event dead or crash!", self.alias)) - if EventData and EventData.IniUnit and EventData.IniGroup then + if EventData then - -- Check if warehouse was destroyed. - local warehousename=self.warehouse:GetName() - if EventData.IniUnitName==warehousename then - self:_DebugMessage(string.format("Warehouse %s alias %s was destroyed!", warehousename, self.alias)) - - -- Trigger Destroyed event. - self:Destroyed() + -- Check if warehouse was destroyed. We compare the name of the destroyed unit. + if EventData.IniUnitName then + local warehousename=self.warehouse:GetName() + if EventData.IniUnitName==warehousename then + self:_DebugMessage(string.format("Warehouse %s alias %s was destroyed!", warehousename, self.alias)) + + -- Trigger Destroyed event. + self:Destroyed() + end end - -- Check if an asset unit was destroyed. - local group=EventData.IniGroup - - -- Get warehouse, asset and request IDs from the group name. - local wid,aid,rid=self:_GetIDsFromGroup(group) - - -- Check that we have the right warehouse. - if wid==self.uid then - - -- Debug message. - self:T(self.wid..string.format("Warehouse %s captured event dead or crash of its asset unit %s.", self.alias, EventData.IniUnitName)) + --self:I(self.wid..string.format("Warehouse %s captured event dead or crash or unit %s.", self.alias, tostring(EventData.IniUnitName))) - -- Loop over all pending requests and get the one belonging to this unit. - for _,request in pairs(self.pending) do - local request=request --#WAREHOUSE.Pendingitem - - -- This is the right request. - if request.uid==rid then - - -- Update cargo and transport group sets of this request. We need to know if this job is finished. - self:_UnitDead(EventData.IniUnit, request) + -- Check if an asset unit was destroyed. + if EventData.IniGroup then - -- Update pending request. Increase ndelivered/ntransporthome and delete group from corresponding group set. - self:_UpdatePending(group) - - -- Number of cargo assets still in group set. - if isCargo==true then + -- Group initiating the event. + local group=EventData.IniGroup + + -- Get warehouse, asset and request IDs from the group name. + local wid,aid,rid=self:_GetIDsFromGroup(group) + + -- Check that we have the right warehouse. + if wid==self.uid then + + -- Debug message. + self:T(self.wid..string.format("Warehouse %s captured event dead or crash of its asset unit %s.", self.alias, EventData.IniUnitName)) - -- Current size of cargo group set. - local ncargo=request.cargogroupset:Count() - - -- Debug message. - local text=string.format("Cargo %d of %s added to warehouse %s stock. Assets still to deliver %d.", - request.ndelivered, tostring(request.nasset), request.warehouse.alias, ncargo) - self:_DebugMessage(text, 5) - - -- All cargo delivered. - if ncargo==0 then - self:Delivered(request) - end + -- Loop over all pending requests and get the one belonging to this unit. + for _,request in pairs(self.pending) do + local request=request --#WAREHOUSE.Pendingitem - elseif isCargo==false then - - -- Current size of cargo group set. - local ntransport=request.transportgroupset:Count() - - -- Debug message. - local text=string.format("Transport %d of %s added to warehouse %s stock. Transports still missing %d.", - request.ntransporthome, tostring(request.ntransport), request.warehouse.alias, ntransport) - self:_DebugMessage(text, 5) - - end + -- This is the right request. + if request.uid==rid then + -- Update cargo and transport group sets of this request. We need to know if this job is finished. + self:_UnitDead(EventData.IniUnit, request) + + end end - - end + end end end end @@ -4236,24 +4269,49 @@ end -- @param Wrapper.Unit#UNIT deadunit Unit that died. -- @param #WAREHOUSE.Pendingitem request Request that needs to be updated. function WAREHOUSE:_UnitDead(deadunit, request) + + -- Flare unit + deadunit:FlareRed() -- Group the dead unit belongs to. local group=deadunit:GetGroup() -- Check if this was the last unit of the group ==> whole group dead. - local isgroupdead=false - local nunits=0 + local groupdead=true + local nunits=0 + local nunits0=0 if group then - local nunits=group:GetSize() - -- One (or less) units in group. - if nunits<=1 then - isgroupdead=true + -- Get current size of group and substract the unit that just died because it is not counted yet! + nunits=group:GetSize()-1 + nunits0=group:GetInitialSize() + + if nunits > 0 then + groupdead=false end end - local text=string.format("Unit %s died! Group %s: #units=%d, IsAlive=%s", deadunit:GetName(), group:GetName(), nunits, tostring(group:IsAlive())) - self:E(self.wid..text) + -- Here I need to get rid of the #CARGO at the end to obtain the original name again! + local unitname=self:_GetNameWithOut(deadunit) + local groupname=self:_GetNameWithOut(group) + + + -- Debug message. + local text=string.format("Unit %s died! #units=%d/%d ==> Group dead=%s (IsAlive=%s).", unitname, nunits, nunits0, tostring(groupdead), tostring(group:IsAlive())) + self:T2(self.wid..text) + + -- Check if this really works as expected! + if nunits<0 then + self:E(self.wid.."ERROR: Number of units negative! This should not happen.") + end + + if groupdead then + self:T(self.wid..string.format("Group %s (transport=%s) is dead!", groupname, tostring(self:_GroupIsTransport(group,request)))) + group:SmokeWhite() + end + + + -- Not sure what this does actually and if it would be better to set it to true. local NoTriggerEvent=false if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then @@ -4263,8 +4321,9 @@ function WAREHOUSE:_UnitDead(deadunit, request) --- -- Remove dead group from carg group set. - if isgroupdead==true then - request.cargogroupset:Remove(group:GetName(), NoTriggerEvent) + if groupdead==true then + request.cargogroupset:Remove(groupname, NoTriggerEvent) + self:T(self.wid..string.format("Removed selfpropelled cargo %s: ncargo=%d.", groupname, request.cargogroupset:Count())) end else @@ -4274,44 +4333,59 @@ function WAREHOUSE:_UnitDead(deadunit, request) -- 1.) A Cargo unit (e.g. waiting to be picked up). -- 2.) A Transport unit which itself holds cargo groups. --- - - if self:_GroupIsTransport(group,request) then + + -- Check if this a cargo or transport group. + local istransport=self:_GroupIsTransport(group,request) + + if istransport==true then -- Get the carrier unit table holding the cargo groups inside this carrier. - local carrierunit=request.carriercargo[deadunit:GetName()] + local cargogroups=request.carriercargo[unitname] - if carrierunit then + if cargogroups then - -- Loop over all groups inside the carrier ==> all dead. - for _,cargogroup in pairs(carrierunit) do - request.cargogroupset:Remove(cargogroup:GetName(),NoTriggerEvent) + -- Loop over all groups inside the destroyed carrier ==> all dead. + for _,cargogroup in pairs(cargogroups) do + local cargoname=self:_GetNameWithOut(cargogroup) + request.cargogroupset:Remove(cargoname, NoTriggerEvent) + self:T(self.wid..string.format("Removed transported cargo %s inside dead carrier %s: ncargo=%d", cargoname, unitname, request.cargogroupset:Count())) end end -- Whole carrier group is dead. Remove it from the carrier group set. - if isgroupdead then - request.transportcargoset:Remove(group:GetName(), NoTriggerEvent) + if groupdead then + request.transportgroupset:Remove(groupname, NoTriggerEvent) + self:T(self.wid..string.format("Removed transport %s: ntransport=%d", groupname, request.transportgroupset:Count())) end - else + elseif istransport==false then -- This must have been an alive cargo group that was killed outside the carrier, e.g. waiting to be transported or waiting to be put back. -- Remove dead group from cargo group set. - if isgroupdead==true then - - request.cargogroupset:Remove(group:GetName(), NoTriggerEvent) - - -- TODO: This as well? + if groupdead==true then + request.cargogroupset:Remove(groupname, NoTriggerEvent) + self:T(self.wid..string.format("Removed transported cargo %s outside carrier: ncargo=%d", groupname, request.cargogroupset:Count())) + -- This as well? --request.transportcargoset:RemoveCargosByName(RemoveCargoNames) - end + end + else + self:E(self.wid..string.format("ERROR: Group %s is neither cargo nor transport!", group:GetName())) end end + end +--- Remove group. +-- @param #WAREHOUSE self +-- @param Wrapper.Group#GROUP group The group to be removed. +function WAREHOUSE:_RemoveGroup(group) +end + +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- Warehouse event handling function. -- Handles the case when the airbase associated with the warehous is captured. @@ -4788,15 +4862,18 @@ function WAREHOUSE:_CheckRequestNow(request) return false end + -- If no transport is requested, assets need to be mobile unless it is a self request. local onlymobile=false - if request.ntransport==0 and not request.toself then + if type(request.transport)=="number" and request.ntransport==0 and not request.toself then onlymobile=true end -- Check if number of requested assets is in stock. local _assets,_nassets,_enough=self:_FilterStock(self.stock, request.assetdesc, request.assetdescval, request.nasset, onlymobile) + local _transports + -- Check if enough assets are in stock. if not _enough then local text=string.format("Warehouse %s: Request denied! Not enough (cargo) assets currently available.", self.alias) @@ -4827,18 +4904,8 @@ function WAREHOUSE:_CheckRequestNow(request) end - -- Set chosen assets. + -- Add this here or gettransport fails request.cargoassets=_assets - request.cargoattribute=_assetattribute - request.cargocategory=_assetcategory - - -- Debug info: - local text=string.format("Selected cargo assets, attibute=%s, category=%d:\n", request.cargoattribute, request.cargocategory) - for _i,_asset in pairs(_assets) do - local asset=_asset --#WAREHOUSE.Assetitem - text=text..string.format("%d) asset name=%s, type=%s, category=%d, #units=%d",_i, asset.templatename, asset.unittype, asset.category, asset.nunits) - end - self:T(self.wid..text) end @@ -4846,7 +4913,7 @@ function WAREHOUSE:_CheckRequestNow(request) if request.transporttype ~= WAREHOUSE.TransportType.SELFPROPELLED then -- Get best transports for this asset pack. - local _transports=self:_GetTransportsForAssets(request) + _transports=self:_GetTransportsForAssets(request) -- Check if at least one transport asset is available. if #_transports>0 then @@ -4865,19 +4932,6 @@ function WAREHOUSE:_CheckRequestNow(request) return false end end - - -- Set chosen assets. - request.transportassets=_transports - request.transportattribute=_transportattribute - request.transportcategory=_transportcategory - - -- Debug info: - local text=string.format("Selected transport assets, attibute=%s, category=%d:\n", request.transportattribute, request.transportcategory) - for _i,_asset in pairs(_assets) do - local asset=_asset --#WAREHOUSE.Assetitem - text=text..string.format("%d) asset name=%s, type=%s, category=%d, #units=%d",_i, asset.templatename, asset.unittype, asset.category, asset.nunits) - end - self:T(self.wid..text) else @@ -4893,6 +4947,39 @@ function WAREHOUSE:_CheckRequestNow(request) -- Self propelled case. Nothing to do for now. end + + + -- Set chosen cargo assets. + request.cargoassets=_assets + request.cargoattribute=_assets[1].attribute + request.cargocategory=_assets[1].category + request.nasset=#_assets + + -- Debug info: + local text=string.format("Selected cargo assets, attibute=%s, category=%d:\n", request.cargoattribute, request.cargocategory) + for _i,_asset in pairs(_assets) do + local asset=_asset --#WAREHOUSE.Assetitem + text=text..string.format("%d) name=%s, type=%s, category=%d, #units=%d",_i, asset.templatename, asset.unittype, asset.category, asset.nunits) + end + self:T(self.wid..text) + + if request.transporttype ~= WAREHOUSE.TransportType.SELFPROPELLED then + + -- Set chosen transport assets. + request.transportassets=_transports + request.transportattribute=_transports[1].attribute + request.transportcategory=_transports[1].category + request.ntransport=#_transports + + -- Debug info: + local text=string.format("Selected transport assets, attibute=%s, category=%d:\n", request.transportattribute, request.transportcategory) + for _i,_asset in pairs(_transports) do + local asset=_asset --#WAREHOUSE.Assetitem + text=text..string.format("%d) name=%s, type=%s, category=%d, #units=%d\n",_i, asset.templatename, asset.unittype, asset.category, asset.nunits) + end + self:T(self.wid..text) + + end return true end @@ -4969,10 +5056,12 @@ function WAREHOUSE:_GetTransportsForAssets(request) for j,asset in pairs(cargoassets) do -- How many times does the cargo fit into the carrier? - local n=cargobay/asset.weight + local delta=cargobay-asset.weight + + --self:E(self.wid..string.format("%s unit %d loads cargo uid=%d: bayempty=%02d, bayloaded = %02d - weight=%02d", transport.templatename, k, asset.uid, transport.cargobay[k], cargobay, asset.weight)) -- Cargo fits into carrier - if n>=1 then + if delta>0 then -- Reduce remaining cargobay. cargobay=cargobay-asset.weight self:T3(self.wid..string.format("%s unit %d loads cargo uid=%d: bayempty=%02d, bayloaded = %02d - weight=%02d", transport.templatename, k, asset.uid, transport.cargobay[k], cargobay, asset.weight)) @@ -4982,6 +5071,8 @@ function WAREHOUSE:_GetTransportsForAssets(request) -- This transport group is used. used=true + else + env.info("FF not used! n="..delta) end end -- loop over assets @@ -4992,7 +5083,7 @@ function WAREHOUSE:_GetTransportsForAssets(request) local nput=putintocarrier[j] local cargo=cargoassets[nput] - self:T2(self.wid..string.format("Cargo id=%d assigned for carrier id=%d", cargo.uid, transport.uid)) + self:T(self.wid..string.format("Cargo id=%d assigned for carrier id=%d", cargo.uid, transport.uid)) table.remove(cargoassets, nput) end @@ -5002,8 +5093,14 @@ function WAREHOUSE:_GetTransportsForAssets(request) table.insert(used_transports, transport) end + -- Convert relative quantity (all, half) to absolute number if necessary. + local ntrans=self:_Rel2AbsQuantity(request.ntransport, #transports) + env.info("FF ntrans = "..ntrans) + env.info("FF #trans = "..#transports) + -- Max number of transport groups reached? - if #used_transports >= request.ntransport then + if #used_transports >= ntrans then + request.ntransport=#used_transports break end end @@ -5027,6 +5124,37 @@ function WAREHOUSE:_GetTransportsForAssets(request) return used_transports end +---Relative to absolute quantity. +-- @param #WAREHOUSE self +-- @param #string relative Relative number in terms of @{#WAREHOUSE.Quantity}. +-- @param #number ntot Total number. +-- @return #number Absolute number. +function WAREHOUSE:_Rel2AbsQuantity(relative, ntot) + + local nabs=0 + + -- Handle string input for nmax. + if type(relative)=="string" then + if relative==WAREHOUSE.Quantity.ALL then + nabs=ntot + elseif relative==WAREHOUSE.Quantity.THREEQUARTERS then + nabs=ntot*3/4 + elseif relative==WAREHOUSE.Quantity.HALF then + nabs=ntot/2 + elseif relative==WAREHOUSE.Quantity.THIRD then + nabs=ntot/3 + elseif relative==WAREHOUSE.Quantity.QUARTER then + nabs=ntot/4 + else + nabs=math.min(1, ntot) + end + else + nabs=relative + end + + return nabs +end + ---Sorts the queue and checks if the request can be fulfilled. -- @param #WAREHOUSE self -- @return #WAREHOUSE.Queueitem Chosen request. @@ -5316,15 +5444,16 @@ end --- Is the group a used as transporter for a given request? -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group The group from which the info is gathered. --- @param #WAREHOUSE.Pendingitem request Request --- @return #WAREHOUSE.Pendingitem The request belonging to this group. +-- @param #WAREHOUSE.Pendingitem request Request. +-- @return #boolean True if group is transport, false if group is cargo and nil otherwise. function WAREHOUSE:_GroupIsTransport(group, request) + -- Name of the group under question. + local groupname=self:_GetNameWithOut(group) + if request.transportgroupset then - local transporters=request.transportgroupset:GetSetObjects() - local groupname=group:GetName() for _,transport in pairs(transporters) do if transport:GetName()==groupname then return true @@ -5332,7 +5461,17 @@ function WAREHOUSE:_GroupIsTransport(group, request) end end - return false + if request.cargogroupset then + local cargos=request.cargogroupset:GetSetObjects() + + for _,cargo in pairs(cargos) do + if self:_GetNameWithOut(cargo)==groupname then + return false + end + end + end + + return nil end @@ -5360,6 +5499,24 @@ function WAREHOUSE:_alias(unittype, wid, aid, qid) return _alias end +--- Get group name without any spawn or cargo suffix #CARGO etc. +-- @param #WAREHOUSE self +-- @param Wrapper.Group#GROUP group The group from which the info is gathered. +-- @return #string Name of the object without trailing #... +function WAREHOUSE:_GetNameWithOut(group) + if group then + local name=group:GetName() + local namewithout=UTILS.Split(name, "#")[1] + if namewithout then + return namewithout + else + return name + end + end + return group:GetName() +end + + --- Get warehouse id, asset id and request id from group name (alias). -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group The group from which the info is gathered. @@ -5758,7 +5915,7 @@ function WAREHOUSE:_PrintQueue(queue, name) -- Output text: text=text..string.format( - "\n%d) UID=%d, Prio=%d, Clock=%s, Assignment=%s | Requestor=%s [Airbase=%s, category=%d] | Assets(%s)=%s: #requested=%s / #alive=%s / #delivered=%s | Transport=%s: #requested=%d / #alive=%s / #home=%s", + "\n%d) UID=%d, Prio=%d, Clock=%s, Assignment=%s | Requestor=%s [Airbase=%s, category=%d] | Assets(%s)=%s: #requested=%s / #alive=%s / #delivered=%s | Transport=%s: #requested=%s / #alive=%s / #home=%s", i, uid, prio, clock, assignment, requestor, airbasename, requestorAirbaseCat, assetdesc, assetdescval, nasset, ncargogroupset, ndelivered, transporttype, ntransport, ntransportalive, ntransporthome) end From a0ac366bec8864715c5203361b57238ef4fdcea9 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Wed, 12 Sep 2018 19:08:46 +0200 Subject: [PATCH 346/420] Fix for respawning cargo (should not go dead). --- Moose Development/Moose/Cargo/CargoGroup.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 7c3a2530c..2ccec351e 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -138,7 +138,7 @@ do -- CARGO_GROUP for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do local Cargo = CargoData -- Cargo.Cargo#CARGO - Cargo:Destroy() -- Destroy the cargo and generate a remove unit event to update the sets. + Cargo:Destroy(false) -- Destroy the cargo and generate a remove unit event to update the sets. Cargo:SetStartState( "UnLoaded" ) end From 3d0f1faadc0326701ff4464c3d2b588da1b9023b Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 12 Sep 2018 20:43:24 +0200 Subject: [PATCH 347/420] Warehouse v0.4.3 - Cleaned up tracing output. - Added home zone for airplane dispatcher. - Fixed bugs in relative quantities. - Added parking check for transports. --- Moose Development/Moose/Core/Event.lua | 2 +- .../Moose/Functional/Warehouse.lua | 225 +++++++++++------- 2 files changed, 144 insertions(+), 83 deletions(-) diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 765e3b2ff..ec43201e1 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -979,7 +979,7 @@ function EVENT:onEvent( Event ) local PriorityEnd = PriorityOrder == -1 and 1 or 5 if Event.IniObjectCategory ~= Object.Category.STATIC then - self:E( { EventMeta.Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } ) + self:T( { EventMeta.Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } ) end for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 8aa6dbb82..d5793b360 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -991,21 +991,22 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.4.2" +WAREHOUSE.version="0.4.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- DONE: Case when all transports are killed and there is still cargo to be delivered. Put cargo back into warehouse. +-- TODO: Check overlapping aircraft sometimes. -- TODO: Spawn assets only virtually, i.e. remove requested assets from stock but do NOT spawn them ==> Interface to A2A dispatcher! Maybe do a negative sign on asset number? -- TODO: Test capturing a neutral warehouse. -- TODO: Make more examples: ARTY, CAP, ... -- TODO: Check also general requests like all ground. Is this a problem for self propelled if immobile units are among the assets? Check if transport. --- TODO: Add transport units from dispatchers back to warehouse stock once they completed their mission. -- TODO: Handle the case when units of a group die during the transfer. -- TODO: Added habours as interface for transport to from warehouses? -- TODO: Add save/load capability of warehouse <==> percistance after mission restart. Difficult in lua! +-- DONE: Case when all transports are killed and there is still cargo to be delivered. Put cargo back into warehouse. Should be done now! +-- DONE: Add transport units from dispatchers back to warehouse stock once they completed their mission. -- DONE: Write documentation. -- DONE: Add AAA, SAMs and UAVs to generalized attributes. -- DONE: Add warehouse quantity enumerator. @@ -2131,8 +2132,16 @@ function WAREHOUSE:_JobDone() --------------- -- Job done! -- --------------- - - self:I(string.format("Request id=%d done! No more cargo or transport assets alive.", request.uid)) + + -- Info on job. + local text=string.format("Warehouse %s: Job on request id=%d done!\n", self.alias, request.uid) + text=text..string.format("- %d of %d assets delivered to %s. Casualties %d.", ncargodelivered, ncargotot, request.warehouse.alias, ncargodead) + if request.ntransport>0 then + text=text..string.format("\n- %d of %d transports returned home. Casualties %d.", ntransporthome, ntransporttot, ntransportdead) + end + self:_InfoMessage(text, 20) + + -- Mark request for deletion. table.insert(done, request) else @@ -2141,6 +2150,7 @@ function WAREHOUSE:_JobDone() ----------------------------------- -- This is difficult! How do I know if transports were unused? They could also be just on their way back home. + -- ==> Need to do a lot of checks. -- All transports are dead but there is still cargo left ==> Put cargo back into stock. for _,_group in pairs(request.transportgroupset:GetSetObjects()) do @@ -2224,7 +2234,6 @@ function WAREHOUSE:_JobDone() group:SmokeBlue() end -- Add asset group back to stock. - --env.info(string.format("FF add asset group %s in function JobDone", group:GetName())) self:AddAsset(group) end end @@ -2261,7 +2270,6 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu group=GROUP:FindByName(group) end - --TODO: What happens if the name of the group is wrong? Saw strange behaviour! if group then @@ -2284,13 +2292,13 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu if istransport==true then request.ntransporthome=request.ntransporthome+1 request.transportgroupset:Remove(group:GetName(), true) - self:I(warehouse.wid..string.format("FF Transport %d of %s returned home.", request.ntransporthome, tostring(request.ntransport))) + self:T3(warehouse.wid..string.format("Transport %d of %s returned home.", request.ntransporthome, tostring(request.ntransport))) elseif istransport==false then request.ndelivered=request.ndelivered+1 request.cargogroupset:Remove(self:_GetNameWithOut(group), true) - self:I(warehouse.wid..string.format("FF Cargo %d of %s delivered.", request.ndelivered, tostring(request.nasset))) + self:T3(warehouse.wid..string.format("Cargo %d of %s delivered.", request.ndelivered, tostring(request.nasset))) else - self:E(warehouse.wid..string.format("ERROR: Group %s is neither cargo nor transport!", group:GetName())) + self:T(warehouse.wid..string.format("WARNING: Group %s is neither cargo nor transport!", group:GetName())) end end @@ -2329,13 +2337,15 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu -- Destroy group if it is alive. if group:IsAlive()==true then self:_DebugMessage(string.format("Destroying group %s.", group:GetName()), 5) - group:Destroy(true) + group:Destroy() end - + + else + self:E(self.wid.."ERROR: Unknown group added as asset!") end -- Update status. - --self:__Status(-1) + self:__Status(-1) end @@ -2666,7 +2676,7 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request) -- Delete request from queue because it will never be possible. --TODO: Unless(!) this is a moving warehouse which could, e.g., be an aircraft carrier. - self:_DeleteQueueItem(Request, self.queue) + --self:_DeleteQueueItem(Request, self.queue) return false end @@ -2852,7 +2862,6 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) ---------------------------- -- Spawn the transport groups. - env.info("FF Request ntransport = "..Request.ntransport) for i=1,Request.ntransport do -- Get stock item. @@ -2949,20 +2958,19 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then - -- Pickup and deloay zones. + -- Pickup and deploy zones. local PickupAirbaseSet = SET_ZONE:New():AddZone(ZONE_AIRBASE:New(self.airbase:GetName())) local DeployAirbaseSet = SET_ZONE:New():AddZone(ZONE_AIRBASE:New(Request.airbase:GetName())) -- Define dispatcher for this task. CargoTransport = AI_CARGO_DISPATCHER_AIRPLANE:New(TransportSet, CargoGroups, PickupAirbaseSet, DeployAirbaseSet) + + -- Set home zone. + CargoTransport:SetHomeZone(ZONE_AIRBASE:New(self.airbase:GetName())) elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then - -- Pickup and deloay zones. - --local PickupAirbaseSet = SET_ZONE:New():AddZone(ZONE_AIRBASE:New(self.airbase:GetName())) - --local DeployAirbaseSet = SET_ZONE:New():AddZone(ZONE_AIRBASE:New(Request.airbase:GetName())) - - -- Pickup and deloay zones. + -- Pickup and deploy zones. local PickupZoneSet = SET_ZONE:New():AddZone(self.spawnzone) local DeployZoneSet = SET_ZONE:New():AddZone(Request.warehouse.spawnzone) @@ -2979,7 +2987,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) elseif Request.transporttype==WAREHOUSE.TransportType.APC then - -- Pickup and deloay zones. + -- Pickup and deploy zones. local PickupZoneSet = SET_ZONE:New():AddZone(self.spawnzone) local DeployZoneSet = SET_ZONE:New():AddZone(Request.warehouse.spawnzone) @@ -2994,7 +3002,6 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) CargoTransport:SetPickupRadius(self.spawnzone:GetRadius(), 20) CargoTransport:SetDeployRadius(Request.warehouse.spawnzone:GetRadius(), 20) - else self:E(self.wid.."ERROR: Unknown transporttype!") end @@ -3196,20 +3203,17 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) if istransport==true then request.ntransporthome=request.ntransporthome+1 request.transportgroupset:Remove(group:GetName(), true) - self:I(warehouse.wid..string.format("FF Transport %d of %s returned home.", request.ntransporthome, tostring(request.ntransport))) + self:T2(warehouse.wid..string.format("Transport %d of %s returned home.", request.ntransporthome, tostring(request.ntransport))) elseif istransport==false then request.ndelivered=request.ndelivered+1 request.cargogroupset:Remove(self:_GetNameWithOut(group), true) - self:I(warehouse.wid..string.format("FF Cargo %d of %s delivered.", request.ndelivered, tostring(request.nasset))) + self:T2(warehouse.wid..string.format("Cargo %d of %s delivered.", request.ndelivered, tostring(request.nasset))) else self:E(warehouse.wid..string.format("ERROR: Group %s is neither cargo nor transport!", group:GetName())) end -- Move asset from pending queue into new warehouse. warehouse:__AddAsset(60, group) - env.info(string.format("FF add asset group %s in function onafterArrived in 60 seconds", group:GetName())) - --warehouse:AddAsset(group) - end end @@ -4183,20 +4187,28 @@ function WAREHOUSE:_OnEventArrived(EventData) -- If all IDs are good we can assume it is a warehouse asset. if wid~=nil and aid~=nil and rid~=nil then - + -- Check that warehouse ID is right. if self.uid==wid then + + local request=self:_GetRequestOfGroup(group, self.pending) + local istransport=self:_GroupIsTransport(group,request) + + -- Check that group is cargo and not transport. + if istransport==false then - -- Debug info. - local text=string.format("Air asset group %s from warehouse %s arrived at its destination.", group:GetName(), self.alias) - self:_InfoMessage(text) - - -- Trigger arrived event for this group. Note that each unit of a group will trigger this event. So the onafterArrived function needs to take care of that. - -- Actually, we only take the first unit of the group that arrives. If it does, we assume the whole group arrived, which might not be the case, since - -- some units might still be taxiing or whatever. Therefore, we add 10 seconds for each additional unit of the group until the first arrived event is triggered. - local nunits=#group:GetUnits() - local dt=10*(nunits-1)+1 -- one unit = 1 sec, two units = 11 sec, three units = 21 sec before we call the group arrived. - self:__Arrived(dt, group) + -- Debug info. + local text=string.format("Air asset group %s from warehouse %s arrived at its destination.", group:GetName(), self.alias) + self:_InfoMessage(text) + + -- Trigger arrived event for this group. Note that each unit of a group will trigger this event. So the onafterArrived function needs to take care of that. + -- Actually, we only take the first unit of the group that arrives. If it does, we assume the whole group arrived, which might not be the case, since + -- some units might still be taxiing or whatever. Therefore, we add 10 seconds for each additional unit of the group until the first arrived event is triggered. + local nunits=#group:GetUnits() + local dt=10*(nunits-1)+1 -- one unit = 1 sec, two units = 11 sec, three units = 21 sec before we call the group arrived. + self:__Arrived(dt, group) + + end end @@ -4445,8 +4457,6 @@ function WAREHOUSE:_CheckConquered() local radius=self.zone:GetRadius() -- Scan units in zone. - --TODO: need to check if scan radius does what it should! - -- It seems to return units that are further away than the radius. local gotunits,_,_,units,_,_=coord:ScanObjects(radius, true, false, false) local Nblue=0 @@ -4639,12 +4649,22 @@ function WAREHOUSE:_CheckRequestValid(request) -- Check if number of requested assets is in stock. local _assets,_nassets,_enough=self:_FilterStock(self.stock, request.assetdesc, request.assetdescval, request.nasset) - + -- No assets in stock? Checks cannot be performed. if #_assets==0 then return true end + -- Convert relative to absolute number if necessary. + local nasset=request.nasset + if type(request.nasset)=="string" then + nasset=self:_QuantityRel2Abs(request.nasset,_nassets) + end + + -- Debug check, request.nasset might be a string Quantity enumerator. + local text=string.format("Request valid? Number of assets: requested=%s=%d, selected=%d, total=%d, enough=%s.", tostring(request.nasset), nasset,#_assets,_nassets, tostring(_enough)) + self:T(text) + -- First asset. Is representative for all filtered items in stock. local asset=_assets[1] --#WAREHOUSE.Assetitem @@ -4714,15 +4734,14 @@ function WAREHOUSE:_CheckRequestValid(request) -- Not enough parking at sending warehouse. --if (np_departure < request.nasset) and not (self.category==Airbase.Category.SHIP or self.category==Airbase.Category.HELIPAD) then - if np_departure < request.nasset then - self:E(string.format("ERROR: Incorrect request. Not enough parking spots of terminal type %d at warehouse. Available spots = %d.", termtype, np_departure)) + if np_departure < nasset then + self:E(string.format("ERROR: Incorrect request. Not enough parking spots of terminal type %d at warehouse. Available spots %d < %d necessary.", termtype, np_departure, nasset)) valid=false end - -- Not enough parking at requesting warehouse. - --if np_destination < request.nasset then - if np_destination == 0 then -- TODO: maybe this is just right for FAPS/SHIPS - self:E(string.format("ERROR: Incorrect request. Not enough parking spots of terminal type %d at requesting warehouse. Available spots = %d.", termtype, np_destination)) + -- No parking at requesting warehouse. + if np_destination == 0 then + self:E(string.format("ERROR: Incorrect request. No parking spots of terminal type %d at requesting warehouse. Available spots = %d!", termtype, np_destination)) valid=false end @@ -4834,6 +4853,57 @@ function WAREHOUSE:_CheckRequestValid(request) self:E("ERROR: Incorrect request. Transport type unknown!") valid=false end + + -- Airborne assets: check parking situation. + if request.transporttype==WAREHOUSE.TransportType.AIRPLANE or request.transporttype==WAREHOUSE.TransportType.HELICOPTER then + + -- Check if number of requested assets is in stock. + local _assets,_nassets,_enough=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, request.transporttype, request.ntransport) + + -- Convert relative to absolute number if necessary. + local nasset=request.ntransport + if type(request.nasset)=="string" then + nasset=self:_QuantityRel2Abs(request.ntransport,_nassets) + end + + -- Debug check, request.nasset might be a string Quantity enumerator. + local text=string.format("Request valid? Number of transports: requested=%s=%d, selected=%d, total=%d, enough=%s.", tostring(request.ntransport), nasset,#_assets,_nassets, tostring(_enough)) + self:T(text) + + -- Get necessary terminal type for helos or transport aircraft. + local termtype=self:_GetTerminal(request.transporttype) + + -- Get number of parking spots. + local np_departure=self.airbase:GetParkingSpotsNumber(termtype) + + -- Debug info. + self:T(self.wid..string.format("Transport attribute = %s, terminal type = %d, spots at departure = %d.", request.transporttype, termtype, np_departure)) + + -- Not enough parking at sending warehouse. + --if (np_departure < request.nasset) and not (self.category==Airbase.Category.SHIP or self.category==Airbase.Category.HELIPAD) then + if np_departure < nasset then + self:E(self.wid..string.format("ERROR: Incorrect request. Not enough parking spots of terminal type %d at warehouse. Available spots %d < %d necessary.", termtype, np_departure, nasset)) + valid=false + end + + -- Planes also need parking at the receiving warehouse. + if request.transporttype==WAREHOUSE.TransportType.AIRPLANE then + + -- Total number of parking spots for transport planes at destination. + local np_destination=request.airbase:GetParkingSpotsNumber(termtype) + + -- Debug info. + self:T(self.wid..string.format("Transport attribute = %s: total # of spots (type=%d) at destination = %d.", asset.attribute, termtype, np_destination)) + + -- No parking at requesting warehouse. + if np_destination == 0 then + self:E(string.format("ERROR: Incorrect request. No parking spots of terminal type %d at requesting warehouse for transports. Available spots = %d!", termtype, np_destination)) + valid=false + end + end + + end + end @@ -4841,7 +4911,7 @@ function WAREHOUSE:_CheckRequestValid(request) if valid==false then self:E(self.wid..string.format("ERROR: Got invalid request id=%d.", request.uid)) else - self:T3(self.wid..string.format("Got valid request id=%d.", request.uid)) + self:T3(self.wid..string.format("Request id=%d valid :)", request.uid)) end return valid @@ -5054,6 +5124,7 @@ function WAREHOUSE:_GetTransportsForAssets(request) -- Loop over cargo assets. for j,asset in pairs(cargoassets) do + local asset=asset --#WAREHOUSE.Assetitem -- How many times does the cargo fit into the carrier? local delta=cargobay-asset.weight @@ -5071,21 +5142,26 @@ function WAREHOUSE:_GetTransportsForAssets(request) -- This transport group is used. used=true - else - env.info("FF not used! n="..delta) + else + self:T2(self.wid..string.format("Carrier unit %s too small for cargo asset %s ==> cannot be not used! Cargo bay - asset weight = %d kg", transport.templatename, asset.templatename, delta)) end end -- loop over assets end -- loop over units - -- Remove cargo assets from list. Needs to be done back-to-front in oder not to confuse the loop. + -- Remove cargo assets from list. Needs to be done back-to-front in order not to confuse the loop. for j=#putintocarrier,1, -1 do + local nput=putintocarrier[j] - local cargo=cargoassets[nput] - self:T(self.wid..string.format("Cargo id=%d assigned for carrier id=%d", cargo.uid, transport.uid)) - table.remove(cargoassets, nput) + -- Need to check if multiple units in a group and the group has already been removed! + -- TODO: This might need to be improved but is working okay so far. + if cargo then + -- Remove this group because it was used. + self:T2(self.wid..string.format("Cargo id=%d assigned for carrier id=%d", cargo.uid, transport.uid)) + table.remove(cargoassets, nput) + end end -- Cargo was assined for this carrier. @@ -5094,9 +5170,7 @@ function WAREHOUSE:_GetTransportsForAssets(request) end -- Convert relative quantity (all, half) to absolute number if necessary. - local ntrans=self:_Rel2AbsQuantity(request.ntransport, #transports) - env.info("FF ntrans = "..ntrans) - env.info("FF #trans = "..#transports) + local ntrans=self:_QuantityRel2Abs(request.ntransport, #transports) -- Max number of transport groups reached? if #used_transports >= ntrans then @@ -5129,7 +5203,7 @@ end -- @param #string relative Relative number in terms of @{#WAREHOUSE.Quantity}. -- @param #number ntot Total number. -- @return #number Absolute number. -function WAREHOUSE:_Rel2AbsQuantity(relative, ntot) +function WAREHOUSE:_QuantityRel2Abs(relative, ntot) local nabs=0 @@ -5138,19 +5212,21 @@ function WAREHOUSE:_Rel2AbsQuantity(relative, ntot) if relative==WAREHOUSE.Quantity.ALL then nabs=ntot elseif relative==WAREHOUSE.Quantity.THREEQUARTERS then - nabs=ntot*3/4 + nabs=UTILS.Round(ntot*3/4) elseif relative==WAREHOUSE.Quantity.HALF then - nabs=ntot/2 + nabs=UTILS.Round(ntot/2) elseif relative==WAREHOUSE.Quantity.THIRD then - nabs=ntot/3 + nabs=UTILS.Round(ntot/3) elseif relative==WAREHOUSE.Quantity.QUARTER then - nabs=ntot/4 + nabs=UTILS.Round(ntot/4) else nabs=math.min(1, ntot) end else nabs=relative end + + self:T2(self.wid..string.format("Relative %s: tot=%d, abs=%.2f", tostring(relative), ntot, nabs)) return nabs end @@ -5187,7 +5263,7 @@ function WAREHOUSE:_CheckQueue() if okay and valid and not gotit then request=qitem gotit=true - --break + break end end @@ -5282,7 +5358,7 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) local obstacles={} -- Loop over all parking spots and get the obstacles. - -- TODO: How long does this take on very large airbases, i.e. those with hundereds of parking spots? + -- How long does this take on very large airbases, i.e. those with hundereds of parking spots? Seems to be okay! for _,parkingspot in pairs(parkingdata) do -- Coordinate of the parking spot. @@ -5309,7 +5385,6 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) local _vec3=static:getPoint() local _coord=COORDINATE:NewFromVec3(_vec3) local _name=static:getName() - --env.info("FF static name = "..tostring(_name)) local _size=self:_GetObjectSize(static) table.insert(obstacles[_termid],{coord=_coord, size=_size, name=_name, type="static"}) end @@ -5613,22 +5688,8 @@ function WAREHOUSE:_FilterStock(stock, descriptor, attribute, nmax, mobile) return filtered, ntot, false end - -- Handle string input for nmax. - if type(nmax)=="string" then - if nmax:lower()==WAREHOUSE.Quantity.ALL then - nmax=ntot - elseif nmax==WAREHOUSE.Quantity.THREEQUARTERS then - nmax=ntot*3/4 - elseif nmax==WAREHOUSE.Quantity.HALF then - nmax=ntot/2 - elseif nmax==WAREHOUSE.Quantity.THIRD then - nmax=ntot/3 - elseif nmax==WAREHOUSE.Quantity.QUARTER then - nmax=ntot/4 - else - nmax=math.min(1, ntot) - end - end + -- Convert relative to absolute number if necessary. + nmax=self:_QuantityRel2Abs(nmax,ntot) -- Loop over stock items. for _i,_asset in ipairs(stock) do From 54ae3ed62bdd0c4082319f51a11f90332a9c82ff Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Wed, 12 Sep 2018 23:04:39 +0200 Subject: [PATCH 348/420] Warehouse v0.4.4 - Fixed little bug in warehouse. - Added home event function to cargo airplane. Planes are now going home after job is done. - Added false option for :Destroy() to generate no event. - Added false parameter to respawn function. --- .../Moose/AI/AI_Cargo_Airplane.lua | 44 +++++++++++++++---- .../Moose/Functional/Warehouse.lua | 4 +- Moose Development/Moose/Wrapper/Group.lua | 4 +- 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 8081a88de..afbd3bdda 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -251,7 +251,6 @@ function AI_CARGO_AIRPLANE:onafterLanded( Airplane, From, Event, To ) -- Aircraft was sent to this airbase to pickup troops. Initiate loadling. if self.RoutePickup == true then - env.info("FF load airplane "..Airplane:GetName()) self:Load( self.PickupZone ) self.RoutePickup = false self.Relocating = true @@ -281,15 +280,15 @@ end -- @param Core.Zone#ZONE_AIRBASE PickupZone function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate, Speed, PickupZone ) - if Airplane and Airplane:IsAlive()~=nil then - env.info("FF onafterpick aircraft alive") + if Airplane and Airplane:IsAlive() then + --env.info("FF onafterpick aircraft alive") self.PickupZone = PickupZone -- Get closest airbase of current position. local ClosestAirbase, DistToAirbase=Airplane:GetCoordinate():GetClosestAirbase() - env.info("FF onafterpickup closest airbase "..ClosestAirbase:GetName()) + --env.info("FF onafterpickup closest airbase "..ClosestAirbase:GetName()) -- Two cases. Aircraft spawned in air or at an airbase. if Airplane:InAir() then @@ -298,15 +297,16 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate, self.Airbase=ClosestAirbase end + -- Set pickup airbase. local Airbase = PickupZone:GetAirbase() -- Distance from closest to pickup airbase ==> we need to know if we are already at the pickup airbase. local Dist = Airbase:GetCoordinate():Get2DDistance(ClosestAirbase:GetCoordinate()) - env.info("Distance closest to pickup airbase = "..Dist) + --env.info("Distance closest to pickup airbase = "..Dist) if Airplane:InAir() or Dist>500 then - env.info("FF onafterpickup routing to airbase "..ClosestAirbase:GetName()) + --env.info("FF onafterpickup routing to airbase "..ClosestAirbase:GetName()) -- Route aircraft to pickup airbase. self:Route( Airplane, Airbase, Speed ) @@ -318,7 +318,7 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate, self.RoutePickup = true else - env.info("FF onafterpick calling landed") + --env.info("FF onafterpick calling landed") -- We are already at the right airbase ==> Landed ==> triggers loading of troops. Is usually called at engine shutdown event. self.RoutePickup=true @@ -329,7 +329,7 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate, self.Transporting = false self.Relocating = true else - env.info("FF onafterpick aircraft not alive") + --env.info("FF onafterpick aircraft not alive") end @@ -447,7 +447,7 @@ end -- @param #boolean Uncontrolled If true, spawn group in uncontrolled state. function AI_CARGO_AIRPLANE:Route( Airplane, Airbase, Speed, Uncontrolled ) - if Airplane and Airplane:IsAlive()~=nil then + if Airplane and Airplane:IsAlive() then -- Set takeoff type. local Takeoff = SPAWN.Takeoff.Cold @@ -510,3 +510,29 @@ function AI_CARGO_AIRPLANE:Route( Airplane, Airbase, Speed, Uncontrolled ) end end end + +--- On after Home event. Aircraft will be routed to their home base. +-- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Group#GROUP Airplane The cargo plane. +-- @param From From state. +-- @param Event Event. +-- @param To To State. +-- @param Core.Point#COORDINATE Coordinate Home place (not used). +-- @param #number Speed Speed in km/h to fly to the home airbase (zone). Default is 80% of max possible speed the unit can go. +-- @param Core.Zone#ZONE_AIRBASE HomeZone The home airbase (zone) where the plane should return to. +function AI_CARGO_AIRPLANE:onafterHome(Airplane, From, Event, To, Coordinate, Speed, HomeZone ) + if Airplane and Airplane:IsAlive() then + + -- We are going home! + self.RouteHome = true + + -- Home Base. + local HomeBase=HomeZone:GetAirbase() + self.Airbase=HomeBase + + -- Now route the airplane home + self:Route(Airplane, HomeBase, Speed) + + end + +end diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index d5793b360..8c6bf8909 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -991,7 +991,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.4.3" +WAREHOUSE.version="0.4.4" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -4862,7 +4862,7 @@ function WAREHOUSE:_CheckRequestValid(request) -- Convert relative to absolute number if necessary. local nasset=request.ntransport - if type(request.nasset)=="string" then + if type(request.ntransport)=="string" then nasset=self:_QuantityRel2Abs(request.ntransport,_nassets) end diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index d65f6e86c..d839315cb 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -288,6 +288,8 @@ function GROUP:Destroy( GenerateEvent ) else self:CreateEventDead( timer.getTime(), UnitData ) end + elseif GenerateEvent==false then + -- Do nothing! else self:CreateEventRemoveUnit( timer.getTime(), UnitData ) end @@ -1524,7 +1526,7 @@ function GROUP:RespawnAtCurrentAirbase(SpawnTemplate, Takeoff, Uncontrolled) -- SpawnTemplate.uncontrolled=Uncontrolled -- Destroy old group. - self:Destroy() + self:Destroy(false) --SCHEDULER:New(nil, DATABASE.Spawn, {_DATABASE, SpawnTemplate}, 0.00001) From 956d4fa248b8a67df1f1b4860352661a853c1407 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 13 Sep 2018 16:16:33 +0200 Subject: [PATCH 349/420] Warehouse v0.4.4w --- .../Moose/Functional/Warehouse.lua | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 8c6bf8909..61bff02e4 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -506,7 +506,7 @@ -- -- Start Warehouse Berlin. -- warehouse.Berlin:Start() -- --- -- Warehouse Berlin requests 10 infantry groups and 3 APCs from warehouse Batumi. +-- -- Warehouse Berlin requests 10 infantry groups and 5 APCs from warehouse Batumi. -- warehouse.Batumi:AddRequest(warehouse.Berlin, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 10) -- warehouse.Batumi:AddRequest(warehouse.Berlin, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_APC, 5) -- @@ -991,7 +991,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.4.4" +WAREHOUSE.version="0.4.4w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -1243,7 +1243,7 @@ function WAREHOUSE:New(warehouse, alias) -- @param #number delay Delay in seconds. -- @param Wrapper.Group#GROUP group Group that has arrived. - --- On after "Arrived" event user function. Called when a groups has arrived at its destination. + --- On after "Arrived" event user function. Called when a group has arrived at its destination. -- @function [parent=#WAREHOUSE] OnAfterArrived -- @param #WAREHOUSE self -- @param #string From From state. @@ -1302,15 +1302,15 @@ function WAREHOUSE:New(warehouse, alias) --- Triggers the FSM event "Attacked" when a warehouse is under attack by an another coalition. -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] Attacked - -- @param DCS#coalition.side Coalition which is attacking the warehouse. - -- @param DCS#country.id Country which is attacking the warehouse. + -- @param DCS#coalition.side Coalition Coalition side which is attacking the warehouse, i.e. a number of @{DCS#coalition.side} enumerator. + -- @param DCS#country.id Country Country ID, which is attacking the warehouse, i.e. a number @{DCS#country.id} enumerator. --- Triggers the FSM event "Attacked" with a delay when a warehouse is under attack by an another coalition. -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] __Attacked -- @param #number delay Delay in seconds. - -- @param DCS#coalition.side Coalition which is attacking the warehouse. - -- @param DCS#country.id Country which is attacking the warehouse. + -- @param DCS#coalition.side Coalition Coalition side which is attacking the warehouse, i.e. a number of @{DCS#coalition.side} enumerator. + -- @param DCS#country.id Country Country ID, which is attacking the warehouse, i.e. a number @{DCS#country.id} enumerator. --- On after "Attacked" event user function. Called when a warehouse (zone) is under attack by an enemy. -- @param #WAREHOUSE self @@ -1318,8 +1318,8 @@ function WAREHOUSE:New(warehouse, alias) -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. - -- @param DCS#coalition.side Coalition which is attacking the warehouse. - -- @param DCS#country.id Country which is attacking the warehouse. + -- @param DCS#coalition.side Coalition Coalition side which is attacking the warehouse, i.e. a number of @{DCS#coalition.side} enumerator. + -- @param DCS#country.id Country Country ID, which is attacking the warehouse, i.e. a number @{DCS#country.id} enumerator. --- Triggers the FSM event "Defeated" when an attack from an enemy was defeated. From 80545fa20816abf26ce3a966fcd632f22f49c255 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Thu, 13 Sep 2018 18:00:48 +0200 Subject: [PATCH 350/420] Pushing a fix for the cargo destroys after respawn. --- Moose Development/Moose/Cargo/CargoGroup.lua | 2 +- Moose Development/Moose/Wrapper/Group.lua | 10 +++++++++- .../Moose/Wrapper/Positionable.lua | 20 +++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 2ccec351e..90e656d86 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -138,7 +138,7 @@ do -- CARGO_GROUP for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do local Cargo = CargoData -- Cargo.Cargo#CARGO - Cargo:Destroy(false) -- Destroy the cargo and generate a remove unit event to update the sets. + Cargo:Destroy( false ) -- Destroy the cargo and generate a remove unit event to update the sets. Cargo:SetStartState( "UnLoaded" ) end diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 470de56b4..e8e4e949d 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -274,7 +274,13 @@ end -- @usage -- -- Ship unit example: destroy the Ship silently. -- Ship = GROUP:FindByName( "Ship" ) --- Ship:Destroy( true ) +-- Ship:Destroy() +-- +-- @usage +-- -- Destroy without event generation example. +-- Ship = GROUP:FindByName( "Boat" ) +-- Ship:Destroy( false ) -- Don't generate an event upon destruction. +-- function GROUP:Destroy( GenerateEvent ) self:F2( self.GroupName ) @@ -288,6 +294,8 @@ function GROUP:Destroy( GenerateEvent ) else self:CreateEventDead( timer.getTime(), UnitData ) end + elseif GenerateEvent == false then + -- Do nothing! else self:CreateEventRemoveUnit( timer.getTime(), UnitData ) end diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 20bbfe9d2..fc84e5f54 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -73,6 +73,24 @@ end -- @param #POSITIONABLE self -- @param #boolean GenerateEvent (Optional) true if you want to generate a crash or dead event for the unit. -- @return #nil The DCS Unit is not existing or alive. +-- @usage +-- -- Air unit example: destroy the Helicopter and generate a S_EVENT_CRASH for each unit in the Helicopter group. +-- Helicopter = UNIT:FindByName( "Helicopter" ) +-- Helicopter:Destroy( true ) +-- @usage +-- -- Ground unit example: destroy the Tanks and generate a S_EVENT_DEAD for each unit in the Tanks group. +-- Tanks = UNIT:FindByName( "Tanks" ) +-- Tanks:Destroy( true ) +-- @usage +-- -- Ship unit example: destroy the Ship silently. +-- Ship = STATIC:FindByName( "Ship" ) +-- Ship:Destroy() +-- +-- @usage +-- -- Destroy without event generation example. +-- Ship = STATIC:FindByName( "Boat" ) +-- Ship:Destroy( false ) -- Don't generate an event upon destruction. +-- function POSITIONABLE:Destroy( GenerateEvent ) self:F2( self.ObjectName ) @@ -90,6 +108,8 @@ function POSITIONABLE:Destroy( GenerateEvent ) else self:CreateEventDead( timer.getTime(), DCSObject ) end + elseif GenerateEvent == false then + -- Do nothing! else self:CreateEventRemoveUnit( timer.getTime(), DCSObject ) end From 44866e6d58906cdbe4510441926038405fc1b575 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Thu, 13 Sep 2018 18:04:44 +0200 Subject: [PATCH 351/420] Communicate in the trace the GCICAP setup correctly. --- .../Moose/AI/AI_A2A_Dispatcher.lua | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index d930bad33..79008c40e 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -3556,23 +3556,23 @@ do -- Setup squadrons - self:F( { Airbases = AirbaseNames } ) + self:I( { Airbases = AirbaseNames } ) - self:F( "Defining Templates for Airbases ..." ) + self:I( "Defining Templates for Airbases ..." ) for AirbaseID, AirbaseName in pairs( AirbaseNames ) do local Airbase = _DATABASE:FindAirbase( AirbaseName ) -- Wrapper.Airbase#AIRBASE local AirbaseName = Airbase:GetName() local AirbaseCoord = Airbase:GetCoordinate() local AirbaseZone = ZONE_RADIUS:New( "Airbase", AirbaseCoord:GetVec2(), 3000 ) local Templates = nil - self:F( { Airbase = AirbaseName } ) + self:I( { Airbase = AirbaseName } ) for TemplateID, Template in pairs( self.Templates:GetSet() ) do local Template = Template -- Wrapper.Group#GROUP local TemplateCoord = Template:GetCoordinate() if AirbaseZone:IsVec2InZone( TemplateCoord:GetVec2() ) then Templates = Templates or {} table.insert( Templates, Template:GetName() ) - self:F( { Template = Template:GetName() } ) + self:I( { Template = Template:GetName() } ) end end if Templates then @@ -3588,13 +3588,13 @@ do self.CAPTemplates:FilterPrefixes( CapPrefixes ) self.CAPTemplates:FilterOnce() - self:F( "Setting up CAP ..." ) + self:I( "Setting up CAP ..." ) for CAPID, CAPTemplate in pairs( self.CAPTemplates:GetSet() ) do local CAPZone = ZONE_POLYGON:New( CAPTemplate:GetName(), CAPTemplate ) -- Now find the closest airbase from the ZONE (start or center) local AirbaseDistance = 99999999 local AirbaseClosest = nil -- Wrapper.Airbase#AIRBASE - self:F( { CAPZoneGroup = CAPID } ) + self:I( { CAPZoneGroup = CAPID } ) for AirbaseID, AirbaseName in pairs( AirbaseNames ) do local Airbase = _DATABASE:FindAirbase( AirbaseName ) -- Wrapper.Airbase#AIRBASE local AirbaseName = Airbase:GetName() @@ -3602,7 +3602,7 @@ do local Squadron = self.DefenderSquadrons[AirbaseName] if Squadron then local Distance = AirbaseCoord:Get2DDistance( CAPZone:GetCoordinate() ) - self:F( { AirbaseDistance = Distance } ) + self:I( { AirbaseDistance = Distance } ) if Distance < AirbaseDistance then AirbaseDistance = Distance AirbaseClosest = Airbase @@ -3610,7 +3610,7 @@ do end end if AirbaseClosest then - self:F( { CAPAirbase = AirbaseClosest:GetName() } ) + self:I( { CAPAirbase = AirbaseClosest:GetName() } ) self:SetSquadronCap( AirbaseClosest:GetName(), CAPZone, 6000, 10000, 500, 800, 800, 1200, "RADIO" ) self:SetSquadronCapInterval( AirbaseClosest:GetName(), CapLimit, 300, 600, 1 ) end @@ -3618,14 +3618,14 @@ do -- Setup GCI. -- GCI is setup for all Squadrons. - self:F( "Setting up GCI ..." ) + self:I( "Setting up GCI ..." ) for AirbaseID, AirbaseName in pairs( AirbaseNames ) do local Airbase = _DATABASE:FindAirbase( AirbaseName ) -- Wrapper.Airbase#AIRBASE local AirbaseName = Airbase:GetName() local Squadron = self.DefenderSquadrons[AirbaseName] self:F( { Airbase = AirbaseName } ) if Squadron then - self:F( { GCIAirbase = AirbaseName } ) + self:I( { GCIAirbase = AirbaseName } ) self:SetSquadronGci( AirbaseName, 800, 1200 ) end end From 337b7a69b2ffb22d45b4dc8637d883fb51272f2c Mon Sep 17 00:00:00 2001 From: FlightControl Date: Thu, 13 Sep 2018 20:48:38 +0200 Subject: [PATCH 352/420] Pushing some big fixes for DETECTION, SPAWN and AI_A2A_DISPATCHER. Fingers crossed. --- .../Moose/AI/AI_A2A_Dispatcher.lua | 62 +++++++----- Moose Development/Moose/Core/Spawn.lua | 2 + .../Moose/Functional/Detection.lua | 99 ++++++++++++++----- 3 files changed, 116 insertions(+), 47 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index 79008c40e..135a9a0fc 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -993,6 +993,7 @@ do -- AI_A2A_DISPATCHER self:HandleEvent( EVENTS.Crash, self.OnEventCrashOrDead ) self:HandleEvent( EVENTS.Dead, self.OnEventCrashOrDead ) + --self:HandleEvent( EVENTS.RemoveUnit, self.OnEventCrashOrDead ) self:HandleEvent( EVENTS.Land ) self:HandleEvent( EVENTS.EngineShutdown ) @@ -2548,8 +2549,12 @@ do -- AI_A2A_DISPATCHER local SquadronOverhead = Squadron.Overhead or self.DefenderDefault.Overhead local DefenderSize = Defender:GetInitialSize() - DefenderCount = DefenderCount + DefenderSize / SquadronOverhead - self:F( "Defender Group Name: " .. Defender:GetName() .. ", Size: " .. DefenderSize ) + if DefenderSize then + DefenderCount = DefenderCount + DefenderSize / SquadronOverhead + self:F( "Defender Group Name: " .. Defender:GetName() .. ", Size: " .. DefenderSize ) + else + DefenderCount = 0 + end end end @@ -2991,6 +2996,8 @@ do -- AI_A2A_DISPATCHER local Report = REPORT:New( "\nTactical Overview" ) + local DefenderGroupCount = 0 + -- Now that all obsolete tasks are removed, loop through the detected targets. for DetectedItemID, DetectedItem in pairs( Detection:GetDetectedItems() ) do @@ -3028,16 +3035,19 @@ do -- AI_A2A_DISPATCHER for Defender, DefenderTask in pairs( self:GetDefenderTasks() ) do local Defender = Defender -- Wrapper.Group#GROUP if DefenderTask.Target and DefenderTask.Target.Index == DetectedItem.Index then - local Fuel = Defender:GetFuelMin() * 100 - local Damage = Defender:GetLife() / Defender:GetLife0() * 100 - Report:Add( string.format( " - %s ( %s - %s ): ( #%d ) F: %3d, D:%3d - %s", - Defender:GetName(), - DefenderTask.Type, - DefenderTask.Fsm:GetState(), - Defender:GetSize(), - Fuel, - Damage, - Defender:HasTask() == true and "Executing" or "Idle" ) ) + if Defender:IsAlive() then + DefenderGroupCount = DefenderGroupCount + 1 + local Fuel = Defender:GetFuelMin() * 100 + local Damage = Defender:GetLife() / Defender:GetLife0() * 100 + Report:Add( string.format( " - %s ( %s - %s ): ( #%d ) F: %3d, D:%3d - %s", + Defender:GetName(), + DefenderTask.Type, + DefenderTask.Fsm:GetState(), + Defender:GetSize(), + Fuel, + Damage, + Defender:HasTask() == true and "Executing" or "Idle" ) ) + end end end end @@ -3050,20 +3060,23 @@ do -- AI_A2A_DISPATCHER TaskCount = TaskCount + 1 local Defender = Defender -- Wrapper.Group#GROUP if not DefenderTask.Target then - local DefenderHasTask = Defender:HasTask() - local Fuel = Defender:GetFuelMin() * 100 - local Damage = Defender:GetLife() / Defender:GetLife0() * 100 - Report:Add( string.format( " - %s ( %s - %s ): ( #%d ) F: %3d, D:%3d - %s", - Defender:GetName(), - DefenderTask.Type, - DefenderTask.Fsm:GetState(), - Defender:GetSize(), - Fuel, - Damage, - Defender:HasTask() == true and "Executing" or "Idle" ) ) + if Defender:IsAlive() then + local DefenderHasTask = Defender:HasTask() + local Fuel = Defender:GetFuelMin() * 100 + local Damage = Defender:GetLife() / Defender:GetLife0() * 100 + DefenderGroupCount = DefenderGroupCount + 1 + Report:Add( string.format( " - %s ( %s - %s ): ( #%d ) F: %3d, D:%3d - %s", + Defender:GetName(), + DefenderTask.Type, + DefenderTask.Fsm:GetState(), + Defender:GetSize(), + Fuel, + Damage, + Defender:HasTask() == true and "Executing" or "Idle" ) ) + end end end - Report:Add( string.format( "\n - %d Tasks", TaskCount ) ) + Report:Add( string.format( "\n - %d Tasks - %d Defender Groups", TaskCount, DefenderGroupCount ) ) self:F( Report:Text( "\n" ) ) trigger.action.outText( Report:Text( "\n" ), 25 ) @@ -3634,6 +3647,7 @@ do self:HandleEvent( EVENTS.Crash, self.OnEventCrashOrDead ) self:HandleEvent( EVENTS.Dead, self.OnEventCrashOrDead ) + --self:HandleEvent( EVENTS.RemoveUnit, self.OnEventCrashOrDead ) self:HandleEvent( EVENTS.Land ) self:HandleEvent( EVENTS.EngineShutdown ) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index a9817109e..7a6160490 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -932,6 +932,7 @@ function SPAWN:InitArray( SpawnAngle, SpawnWidth, SpawnDeltaX, SpawnDeltaY ) self:HandleEvent( EVENTS.Birth, self._OnBirth ) self:HandleEvent( EVENTS.Dead, self._OnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._OnDeadOrCrash ) + self:HandleEvent( EVENTS.RemoveUnit, self._OnDeadOrCrash ) if self.Repeat then self:HandleEvent( EVENTS.Takeoff, self._OnTakeOff ) self:HandleEvent( EVENTS.Land, self._OnLand ) @@ -1151,6 +1152,7 @@ function SPAWN:SpawnWithIndex( SpawnIndex ) self:HandleEvent( EVENTS.Birth, self._OnBirth ) self:HandleEvent( EVENTS.Dead, self._OnDeadOrCrash ) self:HandleEvent( EVENTS.Crash, self._OnDeadOrCrash ) + self:HandleEvent( EVENTS.RemoveUnit, self._OnDeadOrCrash ) if self.Repeat then self:HandleEvent( EVENTS.Takeoff, self._OnTakeOff ) self:HandleEvent( EVENTS.Land, self._OnLand ) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 66ca390e2..1f4eb758f 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -513,9 +513,21 @@ do -- DETECTION_BASE local DetectionTimeStamp = timer.getTime() + -- Reset detection cache for the next detection run. + for DetectionObjectName, DetectedObjectData in pairs( self.DetectedObjects ) do + + self.DetectedObjects[DetectionObjectName].IsDetected = false + self.DetectedObjects[DetectionObjectName].IsVisible = false + self.DetectedObjects[DetectionObjectName].KnowDistance = nil + self.DetectedObjects[DetectionObjectName].LastTime = nil + self.DetectedObjects[DetectionObjectName].LastPos = nil + self.DetectedObjects[DetectionObjectName].LastVelocity = nil + self.DetectedObjects[DetectionObjectName].Distance = 10000000 + + end for DetectionGroupID, DetectionGroupData in pairs( self.DetectionSetGroup:GetSet() ) do --self:F( { DetectionGroupData } ) - self:F( {"FF", DetectionGroupData } ) + self:F( { DetectionGroup = DetectionGroupData:GetName() } ) self:__DetectionGroup( DetectDelay, DetectionGroupData, DetectionTimeStamp ) -- Process each detection asynchronously. self.DetectionCount = self.DetectionCount + 1 DetectDelay = DetectDelay + 1 @@ -530,6 +542,8 @@ do -- DETECTION_BASE -- @param #number DetectionTimeStamp Time stamp of detection event. function DETECTION_BASE:onafterDetectionGroup( From, Event, To, DetectionGroup, DetectionTimeStamp ) + --self:F( { DetectedObjects = self.DetectedObjects } ) + self.DetectionRun = self.DetectionRun + 1 local HasDetectedObjects = false @@ -552,13 +566,27 @@ do -- DETECTION_BASE self.DetectDLINK ) - --self:F( DetectedTargets ) + self:F( { DetectedTargets = DetectedTargets } ) for DetectionObjectID, Detection in pairs( DetectedTargets ) do local DetectedObject = Detection.object -- DCS#Object if DetectedObject and DetectedObject:isExist() and DetectedObject.id_ < 50000000 then -- and ( DetectedObject:getCategory() == Object.Category.UNIT or DetectedObject:getCategory() == Object.Category.STATIC ) then - + local DetectedObjectName = DetectedObject:getName() + if not self.DetectedObjects[DetectedObjectName] then + self.DetectedObjects[DetectedObjectName] = self.DetectedObjects[DetectedObjectName] or {} + self.DetectedObjects[DetectedObjectName].Name = DetectedObjectName + self.DetectedObjects[DetectedObjectName].Object = DetectedObject + end + end + end + + for DetectionObjectName, DetectedObjectData in pairs( self.DetectedObjects ) do + + local DetectedObject = DetectedObjectData.Object + + if DetectedObject:isExist() then + local TargetIsDetected, TargetIsVisible, TargetLastTime, TargetKnowType, TargetKnowDistance, TargetLastPos, TargetLastVelocity = DetectionUnit:IsTargetDetected( DetectedObject, self.DetectVisual, @@ -570,7 +598,7 @@ do -- DETECTION_BASE ) --self:T2( { TargetIsDetected = TargetIsDetected, TargetIsVisible = TargetIsVisible, TargetLastTime = TargetLastTime, TargetKnowType = TargetKnowType, TargetKnowDistance = TargetKnowDistance, TargetLastPos = TargetLastPos, TargetLastVelocity = TargetLastVelocity } ) - + -- Only process if the target is visible. Detection also returns invisible units. --if Detection.visible == true then @@ -633,7 +661,7 @@ do -- DETECTION_BASE -- Calculate additional probabilities - if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.DistanceProbability then + if not self.DetectedObjects[DetectedObjectName] and TargetIsVisible and self.DistanceProbability then local DistanceFactor = Distance / 4 local DistanceProbabilityReversed = ( 1 - self.DistanceProbability ) * DistanceFactor local DistanceProbability = 1 - DistanceProbabilityReversed @@ -645,7 +673,7 @@ do -- DETECTION_BASE end end - if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.AlphaAngleProbability then + if not self.DetectedObjects[DetectedObjectName] and TargetIsVisible and self.AlphaAngleProbability then local NormalVec2 = { x = DetectedObjectVec2.x - DetectionGroupVec2.x, y = DetectedObjectVec2.y - DetectionGroupVec2.y } local AlphaAngle = math.atan2( NormalVec2.y, NormalVec2.x ) local Sinus = math.sin( AlphaAngle ) @@ -662,7 +690,7 @@ do -- DETECTION_BASE end - if not self.DetectedObjects[DetectedObjectName] and Detection.visible and self.ZoneProbability then + if not self.DetectedObjects[DetectedObjectName] and TargetIsVisible and self.ZoneProbability then for ZoneDataID, ZoneData in pairs( self.ZoneProbability ) do self:F({ZoneData}) @@ -685,33 +713,50 @@ do -- DETECTION_BASE HasDetectedObjects = true - self.DetectedObjects[DetectedObjectName] = self.DetectedObjects[DetectedObjectName] or {} + self.DetectedObjects[DetectedObjectName] = self.DetectedObjects[DetectedObjectName] or {} self.DetectedObjects[DetectedObjectName].Name = DetectedObjectName - self.DetectedObjects[DetectedObjectName].IsDetected = TargetIsDetected - self.DetectedObjects[DetectedObjectName].IsVisible = TargetIsVisible - self.DetectedObjects[DetectedObjectName].LastTime = TargetLastTime - self.DetectedObjects[DetectedObjectName].LastPos = TargetLastPos - self.DetectedObjects[DetectedObjectName].LastVelocity = TargetLastVelocity - self.DetectedObjects[DetectedObjectName].KnowType = TargetKnowType - self.DetectedObjects[DetectedObjectName].KnowDistance = Detection.distance -- TargetKnowDistance - self.DetectedObjects[DetectedObjectName].Distance = Distance + + if TargetIsDetected and TargetIsDetected == true then + self.DetectedObjects[DetectedObjectName].IsDetected = TargetIsDetected + end + + if TargetIsDetected and TargetIsVisible and TargetIsVisible == true then + self.DetectedObjects[DetectedObjectName].IsVisible = TargetIsDetected and TargetIsVisible + end + + if TargetIsDetected and not self.DetectedObjects[DetectedObjectName].KnowType then + self.DetectedObjects[DetectedObjectName].KnowType = TargetIsDetected and TargetKnowType + end + self.DetectedObjects[DetectedObjectName].KnowDistance = TargetKnowDistance -- Detection.distance -- TargetKnowDistance + self.DetectedObjects[DetectedObjectName].LastTime = ( TargetIsDetected and TargetIsVisible == false ) and TargetLastTime + self.DetectedObjects[DetectedObjectName].LastPos = ( TargetIsDetected and TargetIsVisible == false ) and TargetLastPos + self.DetectedObjects[DetectedObjectName].LastVelocity = ( TargetIsDetected and TargetIsVisible == false ) and TargetLastVelocity + + if not self.DetectedObjects[DetectedObjectName].Distance or ( Distance and self.DetectedObjects[DetectedObjectName].Distance > Distance ) then + self.DetectedObjects[DetectedObjectName].Distance = Distance + end + self.DetectedObjects[DetectedObjectName].DetectionTimeStamp = DetectionTimeStamp - --self:F( { DetectedObject = self.DetectedObjects[DetectedObjectName] } ) + self:F( { DetectedObject = self.DetectedObjects[DetectedObjectName] } ) local DetectedUnit = UNIT:FindByName( DetectedObjectName ) DetectedUnits[DetectedObjectName] = DetectedUnit else -- if beyond the DetectionRange then nullify... + self:F( { DetectedObject = "No more detection for " .. DetectedObjectName } ) if self.DetectedObjects[DetectedObjectName] then self.DetectedObjects[DetectedObjectName] = nil end end - --end + + --self:T2( self.DetectedObjects ) + else + -- The previously detected object does not exist anymore, delete from the cache. + self:F( "Removing from DetectedObjects: " .. DetectionObjectName ) + self.DetectedObjects[DetectionObjectName] = nil end - - --self:T2( self.DetectedObjects ) end if HasDetectedObjects then @@ -1398,16 +1443,18 @@ do -- DETECTION_BASE -- @param #string ObjectName -- @return #DETECTION_BASE.DetectedObject function DETECTION_BASE:GetDetectedObject( ObjectName ) - --self:F2( ObjectName ) + self:F2( { ObjectName = ObjectName } ) if ObjectName then local DetectedObject = self.DetectedObjects[ObjectName] if DetectedObject then + --self:F( { DetectedObjects = self.DetectedObjects } ) -- Only return detected objects that are alive! local DetectedUnit = UNIT:FindByName( ObjectName ) if DetectedUnit and DetectedUnit:IsAlive() then if self:IsDetectedObjectIdentified( DetectedObject ) == false then + --self:F( { DetectedObject = DetectedObject } ) return DetectedObject end end @@ -1606,7 +1653,7 @@ do -- DETECTION_BASE return nil end - --- Set IsDetected flag for all DetectedItems. + --- Set IsDetected flag for the DetectedItem, which can have more units. -- @param #DETECTION_BASE self -- @return #DETECTION_BASE.DetectedItem DetectedItem -- @return #boolean true if at least one UNIT is detected from the DetectedSet, false if no UNIT was detected from the DetectedSet. @@ -1899,6 +1946,7 @@ do -- DETECTION_UNITS -- Yes, the DetectedUnit is still detected or exists. Flag as identified. self:IdentifyDetectedObject( DetectedObject ) + self:F( { "**DETECTED**", IsVisible = DetectedObject.IsVisible } ) -- Update the detection with the new data provided. DetectedItem.TypeName = DetectedUnit:GetTypeName() DetectedItem.CategoryName = DetectedUnit:GetCategoryName() @@ -2023,6 +2071,9 @@ do -- DETECTION_UNITS Report:Add(DetectedItemID .. ", " .. DetectedItemCoordText) Report:Add( string.format( "Threat: [%s]", string.rep( "â– ", ThreatLevelA2G ), string.rep( "â–¡", 10-ThreatLevelA2G ) ) ) Report:Add( string.format("Type: %s%s", UnitCategoryText, UnitDistanceText ) ) + Report:Add( string.format("Visible: %s", DetectedItem.IsVisible and "yes" or "no" ) ) + Report:Add( string.format("Detected: %s", DetectedItem.IsDetected and "yes" or "no" ) ) + Report:Add( string.format("Distance: %s", DetectedItem.KnowDistance and "yes" or "no" ) ) return Report end return nil @@ -2513,7 +2564,9 @@ do -- DETECTION_AREAS function DETECTION_AREAS:CreateDetectionItems() - self:T2( "Checking Detected Items for new Detected Units ..." ) + self:F( "Checking Detected Items for new Detected Units ..." ) + --self:F( { DetectedObjects = self.DetectedObjects } ) + -- First go through all detected sets, and check if there are new detected units, match all existing detected units and identify undetected units. -- Regroup when needed, split groups when needed. for DetectedItemID, DetectedItemData in pairs( self.DetectedItems ) do From 36aa36398cdeebd7845af0e8a06458bd74008c45 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Thu, 13 Sep 2018 21:51:21 +0200 Subject: [PATCH 353/420] Hopefully this fixes the cargo lost :-) --- Moose Development/Moose/Wrapper/Group.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index e8e4e949d..46dd8392c 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1491,7 +1491,7 @@ function GROUP:RespawnAtCurrentAirbase(SpawnTemplate, Takeoff, Uncontrolled) -- SpawnTemplate.uncontrolled=Uncontrolled -- Destroy and respawn. - self:Destroy() + self:Destroy( false ) _DATABASE:Spawn( SpawnTemplate ) -- Reset events. From 3b630be4f41bf024c7b27b9d9cfd868f431eb2ea Mon Sep 17 00:00:00 2001 From: FlightControl Date: Thu, 13 Sep 2018 22:19:48 +0200 Subject: [PATCH 354/420] Hopefully this fixes the cargo lost :-) --- Moose Development/Moose/Cargo/CargoCrate.lua | 2 +- Moose Development/Moose/Cargo/CargoGroup.lua | 6 ++++-- Moose Development/Moose/Cargo/CargoSlingload.lua | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua index fce280625..b12869c16 100644 --- a/Moose Development/Moose/Cargo/CargoCrate.lua +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -63,7 +63,7 @@ do -- CARGO_CRATE self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead ) self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead ) - self:HandleEvent( EVENTS.RemoveUnit, self.OnEventCargoDead ) + --self:HandleEvent( EVENTS.RemoveUnit, self.OnEventCargoDead ) self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead ) self:SetEventPriority( 4 ) diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 90e656d86..d40c0f815 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -121,7 +121,7 @@ do -- CARGO_GROUP self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead ) self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead ) - self:HandleEvent( EVENTS.RemoveUnit, self.OnEventCargoDead ) + --self:HandleEvent( EVENTS.RemoveUnit, self.OnEventCargoDead ) self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead ) self:SetEventPriority( 4 ) @@ -138,7 +138,7 @@ do -- CARGO_GROUP for CargoID, CargoData in pairs( self.CargoSet:GetSet() ) do local Cargo = CargoData -- Cargo.Cargo#CARGO - Cargo:Destroy( false ) -- Destroy the cargo and generate a remove unit event to update the sets. + Cargo:Destroy() -- Destroy the cargo and generate a remove unit event to update the sets. Cargo:SetStartState( "UnLoaded" ) end @@ -258,6 +258,8 @@ do -- CARGO_GROUP --- @param #CARGO_GROUP self -- @param Core.Event#EVENTDATA EventData function CARGO_GROUP:OnEventCargoDead( EventData ) + + self:E(EventData) local Destroyed = false diff --git a/Moose Development/Moose/Cargo/CargoSlingload.lua b/Moose Development/Moose/Cargo/CargoSlingload.lua index c22fdb38b..a3831dcf6 100644 --- a/Moose Development/Moose/Cargo/CargoSlingload.lua +++ b/Moose Development/Moose/Cargo/CargoSlingload.lua @@ -56,7 +56,7 @@ do -- CARGO_SLINGLOAD self:HandleEvent( EVENTS.Dead, self.OnEventCargoDead ) self:HandleEvent( EVENTS.Crash, self.OnEventCargoDead ) - self:HandleEvent( EVENTS.RemoveUnit, self.OnEventCargoDead ) + --self:HandleEvent( EVENTS.RemoveUnit, self.OnEventCargoDead ) self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead ) self:SetEventPriority( 4 ) From 7fa7f0fb7958b30046dfcfc8477f9bff62207ba8 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Fri, 14 Sep 2018 05:48:48 +0200 Subject: [PATCH 355/420] Added DetectedItem event --- Moose Development/Moose/Functional/Detection.lua | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 1f4eb758f..42f25311a 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -287,6 +287,7 @@ do -- DETECTION_BASE -- @list <#DETECTION_BASE.DetectedItem> --- @type DETECTION_BASE.DetectedItem + -- @field #boolean IsDetected Indicates if the DetectedItem has been detected or not. -- @field Core.Set#SET_UNIT Set -- @field Core.Set#SET_UNIT Set -- The Set of Units in the detected area. -- @field Core.Zone#ZONE_UNIT Zone -- The Zone of the detected area. @@ -445,7 +446,16 @@ do -- DETECTION_BASE -- @param #DETECTION_BASE self -- @param #number Delay The delay in seconds. + self:AddTransition( "Detecting", "DetectedItem", "Detecting" ) + --- OnAfter Transition Handler for Event DetectedItem. + -- @function [parent=#DETECTION_BASE] OnAfterDetectedItem + -- @param #DETECTION_BASE self + -- @param #string From The From State string. + -- @param #string Event The Event string. + -- @param #string To The To State string. + -- @param #table DetectedItem The DetectedItem. + self:AddTransition( "*", "Stop", "Stopped" ) --- OnBefore Transition Handler for Event Stop. @@ -782,6 +792,9 @@ do -- DETECTION_BASE for DetectedItemID, DetectedItem in pairs( self.DetectedItems ) do self:UpdateDetectedItemDetection( DetectedItem ) self:CleanDetectionItem( DetectedItem, DetectedItemID ) -- Any DetectionItem that has a Set with zero elements in it, must be removed from the DetectionItems list. + if DetectedItem then + self:__DetectedItem( 0.1, DetectedItem ) + end end self:__Detect( self.RefreshTimeInterval ) @@ -2400,6 +2413,7 @@ do -- DETECTION_AREAS Report:Add(DetectedItemID .. ", " .. DetectedItemCoordText) Report:Add( string.format( "Threat: [%s]", string.rep( "â– ", ThreatLevelA2G ), string.rep( "â–¡", 10-ThreatLevelA2G ) ) ) Report:Add( string.format("Type: %2d of %s", DetectedItemsCount, DetectedItemsTypes ) ) + Report:Add( string.format("Detected: %s", DetectedItem.IsDetected and "yes" or "no" ) ) return Report end From c7c63b37fe0a10fb8c78303dc1cc90a3feb7311d Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Fri, 14 Sep 2018 15:25:06 +0200 Subject: [PATCH 356/420] Warehouse v0.4.4w --- Moose Development/Moose/DCS.lua | 5 +- .../Moose/Functional/Warehouse.lua | 430 ++++++++++-------- .../Moose/Wrapper/Identifiable.lua | 13 +- 3 files changed, 257 insertions(+), 191 deletions(-) diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua index c8e997f5c..8caec3753 100644 --- a/Moose Development/Moose/DCS.lua +++ b/Moose Development/Moose/DCS.lua @@ -208,9 +208,6 @@ do -- country -- @type country -- @field #country.id id - --- [DCS Enum country](https://wiki.hoggitworld.com/view/DCS_enum_country) - -- @field #country - country = {} --- [DCS enumerator country](https://wiki.hoggitworld.com/view/DCS_enum_country) -- @type country.id @@ -289,6 +286,8 @@ do -- country -- @field OMAN -- @field UNITED_ARAB_EMIRATES + country = {} --#country + end -- country diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 61bff02e4..9b37cea88 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -26,14 +26,10 @@ -- @field #boolean Debug If true, send debug messages to all. -- @field #boolean Report If true, send status messages to coalition. -- @field Wrapper.Static#STATIC warehouse The phyical warehouse structure. --- @field DCS#coalition.side coalition Coalition side the warehouse belongs to. --- @field DCS#country.id country Country ID the warehouse belongs to. -- @field #string alias Alias of the warehouse. Name its called when sending messages. -- @field Core.Zone#ZONE zone Zone around the warehouse. If this zone is captured, the warehouse and all its assets goes to the capturing coaliton. -- @field Wrapper.Airbase#AIRBASE airbase Airbase the warehouse belongs to. -- @field #string airbasename Name of the airbase associated to the warehouse. --- @field DCS#Airbase.Category category Category of the home airbase, i.e. airdrome, helipad/farp or ship. --- @field Core.Point#COORDINATE coordinate Coordinate of the warehouse. -- @field Core.Point#COORDINATE road Closest point to warehouse on road. -- @field Core.Point#COORDINATE rail Closest point to warehouse on rail. -- @field Core.Zone#ZONE spawnzone Zone in which assets are spawned. @@ -89,10 +85,10 @@ -- ## What means of transportation are available? -- Firstly, all mobile assets can be send from warehouse to another on their own. -- --- * Ground vehicles will use the road infrastructure. So a good road connection for both warehouses is important. +-- * Ground vehicles will use the road infrastructure. So a good road connection for both warehouses is important but also off road connections can be added if necessary. -- * Airborne units get a flightplan from the airbase of the sending warehouse to the airbase of the receiving warehouse. This already implies that for airborne -- assets both warehouses need an airbase. If either one of the warehouses does not have an associated airbase, direct transportation of airborne assest is not possible. --- * Naval units can be exchanged between warehouses which posses a port/habour. Also shipping lanes must be specified manually but the user since DCS does not provide these. +-- * Naval units can be exchanged between warehouses which possess a port, which can be defined by the user. Also shipping lanes must be specified manually but the user since DCS does not provide these. -- * Trains (would) use the available railroad infrastructure and both warehouses must have a connection to the railroad. Unfortunately, however, trains are not yet implemented to -- a reasonable degree in DCS at the moment and hence cannot be used yet. -- @@ -103,7 +99,8 @@ -- * @{AI.AI_Cargo_Dispatcher_Helicopter#AI_DISPATCHER_HELICOPTER} and -- * @{AI.AI_Cargo_Dispatcher_Airplane#AI_DISPATCHER_AIRPLANE}. -- --- Depending on which cargo dispatcher is used (ground or airbore), similar considerations like in the self propelled case are necessary. +-- Depending on which cargo dispatcher is used (ground or airbore), similar considerations like in the self propelled case are necessary. Howver, note that +-- the dispatchers as of yet cannot use user defined off road paths for example since they are classes of their own and use a different routing logic. -- -- === -- @@ -298,7 +295,7 @@ -- In order to use airborne assets, a warehouse needs to have an associated airbase. This can be an airdrome, a FARP/HELOPAD or a ship. -- -- If there is an airbase within 3 km range of the warehouse it is automatically set as the associated airbase. A user can set an airbase manually --- with the @{#WAREHOUSE.SetAirbase} function. Keep in mind, that sometimes, ground units need to walk/drive from the spawn zone to the airport +-- with the @{#WAREHOUSE.SetAirbase} function. Keep in mind that sometimes ground units need to walk/drive from the spawn zone to the airport -- to get to their transport carriers. -- -- ## Naval Connections @@ -633,8 +630,8 @@ -- -- -- Enemy has entered the warehouse zone. This triggers the "Attacked" event. -- function warehouse.Senaki:OnAfterAttacked(From,Event,To,Coalition,Country) --- MESSAGE:New(string.format("Warehouse %s: We are under attack!", self.alias), 30):ToCoalition(self.coalition) --- self.coordinate:SmokeRed() +-- MESSAGE:New(string.format("Warehouse %s: We are under attack!", self.alias), 30):ToCoalition(self:GetCoalition()) +-- self:GetCoordinate():SmokeRed() -- end -- -- -- Now the red BMP also captured the warehouse. So the warehouse and the airbase are both red and planes can be spawned again. @@ -695,7 +692,11 @@ -- -- ## Example 10: Aircraft Carrier - Rescue Helo and Escort -- --- This example shows how to spawn assets from a warehouse located on an aircraft carrier. +-- This example shows how to spawn assets from a warehouse located on an aircraft carrier. The warehouse must still be represented by a +-- physical static object. However, on a carrier space is limit so we take a smaller static. In priciple one could also take something +-- like a windsock. +-- +-- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_Carrier.png) -- -- After 10 seconds we make a self request for a rescue helicopter. Note, that the @{#WAREHOUSE.AddRequest} function has a parameter which lets you -- specify an "Assignment". This can be later used to identify the request and take the right actions. @@ -710,7 +711,9 @@ -- a fresh helo. Effectively, there we created an infinite, never ending loop. So a rescue helo will be up at all times. -- -- After 30 and 45 seconds requests for five groups of armed speedboats are made. These will be spawned in the port zone right behind the carrier. --- The first five groups will go port of the carrier an form a left wing formation. The seconds groups will to the analogue on the starboard side. +-- The first five groups will go port of the carrier an form a left wing formation. The seconds groups will to the analogue on the starboard side. +-- **Note** that in order to spawn naval assets a warehouse needs a port (zone). Since the carrier and hence the warehouse is mobile, we define a moving +-- zone as @{Core.Zone#ZONE_UNIT} with the carrier as reference unit. The "port" of the Stennis at its stern so all naval assets are spawned behing the carrier. -- -- -- Start warehouse on USS Stennis. -- warehouse.Stennis:Start() @@ -734,17 +737,20 @@ -- local groupset=groupset --Core.Set#SET_GROUP -- local request=request --Functional.Warehouse#WAREHOUSE.Pendingitem -- +-- -- USS Stennis is the mother ship. -- local Mother=UNIT:FindByName("Stennis") -- --- if request.assignment=="Speedboats Left" then +-- -- Get assignment for this request. +-- local assignment=warehouse.Stennis:GetAssignment(request) +-- +-- if assignment=="Speedboats Left" then -- -- -- Define AI Formation object. -- -- Note that this has to be a global variable or the garbage collector will remove it for some reason! --- CarrierFormationLeft = AI_FORMATION:New(Mother, groupset, "Left Formation with Carrier", "Follow Carrier at given parameters.") +-- CarrierFormationLeft = AI_FORMATION:New(Mother, groupset, "Port Formation with Carrier", "Follow Carrier at given parameters.") -- --- -- Formation parameters. --- CarrierFormationLeft:FormationLeftWing(200 ,50, 0, 0, 500, 50) --- +-- -- Formation parameters and start. +-- CarrierFormationLeft:FormationLeftWing(200 ,50, 0, 0, 500, 50) -- CarrierFormationLeft:__Start(2) -- -- for _,group in pairs(groupset:GetSetObjects()) do @@ -752,41 +758,33 @@ -- group:FlareRed() -- end -- --- elseif request.assignment=="Speedboats Right" then +-- elseif assignment=="Speedboats Right" then -- -- -- Define AI Formation object. -- -- Note that this has to be a global variable or the garbage collector will remove it for some reason! --- CarrierFormationRight = AI_FORMATION:New(Mother, groupset, "Right Formation with Carrier", "Follow Carrier at given parameters.") +-- CarrierFormationRight = AI_FORMATION:New(Mother, groupset, "Starboard Formation with Carrier", "Follow Carrier at given parameters.") -- --- -- Formation parameters. +-- -- Formation parameters and start. -- CarrierFormationRight:FormationRightWing(200 ,50, 0, 0, 500, 50) +-- CarrierFormationRight:__Start(2) -- --- CarrierFormationRight:__Start(2) --- --- for _,group in pairs(groupset:GetSetObjects()) do --- local group=group --Wrapper.Group#GROUP --- group:FlareGreen() --- end --- --- elseif request.assignment=="Rescue Helo" then +-- elseif assignment=="Rescue Helo" then -- -- -- Define AI Formation object. -- CarrierFormationHelo = AI_FORMATION:New(Mother, groupset, "Helo Formation with Carrier", "Follow Carrier at given parameters.") -- --- -- Formation parameters. +-- -- Formation parameters and start. -- CarrierFormationHelo:FormationCenterWing(-150, 50, 20, 50, 100, 50) --- --- -- Start formation FSM. -- CarrierFormationHelo:__Start(2) -- -- end -- --- --- When the helo is out of fuel, it will return to the carrier and should be delivered. +-- --- When the helo is out of fuel, it will return to the carrier. The asset is considered as delivered. -- function warehouse.Stennis:OnAfterDelivered(From,Event,To,request) -- local request=request --Functional.Warehouse#WAREHOUSE.Pendingitem -- -- -- So we start another request. --- if request.assignment=="Rescue Helo" then +-- if warehouse.Stennis:GetAssignment(request)=="Rescue Helo" then -- warehouse.Stennis:__AddRequest(10, warehouse.Stennis, WAREHOUSE.Descriptor.TEMPLATENAME, "CH-53E", 1, nil, nil, nil, "Rescue Helo") -- end -- end @@ -800,14 +798,10 @@ WAREHOUSE = { Debug = false, Report = true, warehouse = nil, - coalition = nil, - country = nil, alias = nil, zone = nil, airbase = nil, airbasename = nil, - category = -1, - coordinate = nil, road = nil, rail = nil, spawnzone = nil, @@ -1052,7 +1046,12 @@ WAREHOUSE.version="0.4.4w" -- @param #string alias (Optional) Alias of the warehouse, i.e. the name it will be called when sending messages etc. Default is the name of the static -- @return #WAREHOUSE self function WAREHOUSE:New(warehouse, alias) - BASE:E({warehouse=warehouse:GetName()}) + BASE:T({warehouse=warehouse}) + + -- Check if just a string was given and convert to static. + if type(warehouse)=="string" then + warehouse=STATIC:FindByName(warehouse, true) + end -- Nil check. if warehouse==nil then @@ -1075,16 +1074,12 @@ function WAREHOUSE:New(warehouse, alias) -- Set some variables. self.warehouse=warehouse self.uid=tonumber(warehouse:GetID()) - self.coalition=warehouse:GetCoalition() - self.country=warehouse:GetCountry() - self.coordinate=warehouse:GetCoordinate() -- Closest of the same coalition but within a certain range. - local _airbase=self.coordinate:GetClosestAirbase(nil, self.coalition) - if _airbase and _airbase:GetCoordinate():Get2DDistance(self.coordinate) < 3000 then + local _airbase=self:GetCoordinate():GetClosestAirbase(nil, self:GetCoalition()) + if _airbase and _airbase:GetCoordinate():Get2DDistance(self:GetCoordinate()) < 3000 then self.airbase=_airbase self.airbasename=self.airbase:GetName() - self.category=self.airbase:GetDesc().category end -- Define warehouse and default spawn zone. @@ -1174,14 +1169,14 @@ function WAREHOUSE:New(warehouse, alias) -- @param #number delay Delay in seconds. - --- Trigger the FSM event "AddAsset". Add an airplane group to the warehouse stock. + --- Trigger the FSM event "AddAsset". Add a group to the warehouse stock. -- @function [parent=#WAREHOUSE] AddAsset -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group Group to be added as new asset. -- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1. -- @param #WAREHOUSE.Attribute forceattribute (Optional) Explicitly force a generalized attribute for the asset. This has to be an @{#WAREHOUSE.Attribute}. - --- Trigger the FSM event "AddAsset" with a delay. Add an airplane group to the warehouse stock. + --- Trigger the FSM event "AddAsset" with a delay. Add a group to the warehouse stock. -- @function [parent=#WAREHOUSE] __AddAsset -- @param #WAREHOUSE self -- @param #number delay Delay in seconds. @@ -1228,7 +1223,7 @@ function WAREHOUSE:New(warehouse, alias) -- @param #WAREHOUSE.Queueitem Request Information table of the request. - --- Triggers the FSM event "Arrived", i.e. when a group has arrived at the destination warehouse. + --- Triggers the FSM event "Arrived" when a group has arrived at the destination warehouse. -- This function should always be called from the sending and not the receiving warehouse. -- If the group is a cargo asset, it is added to the receiving warehouse. If the group is a transporter it -- is added to the sending warehouse since carriers are supposed to return to their home warehouse once @@ -1237,7 +1232,10 @@ function WAREHOUSE:New(warehouse, alias) -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group Group that has arrived. - --- Triggers the FSM event "Arrived" after a delay, i.e. when a group has arrived at the destination. + --- Triggers the FSM event "Arrived" after a delay when a group has arrived at the destination. + -- This function should always be called from the sending and not the receiving warehouse. + -- If the group is a cargo asset, it is added to the receiving warehouse. If the group is a transporter it + -- is added to the sending warehouse since carriers are supposed to return to their home warehouse once -- @function [parent=#WAREHOUSE] __Arrived -- @param #WAREHOUSE self -- @param #number delay Delay in seconds. @@ -1272,7 +1270,7 @@ function WAREHOUSE:New(warehouse, alias) -- @param #WAREHOUSE.Pendingitem request Pending request that was now delivered. - --- Triggers the FSM event "SelfRequest". Request was initiated to the warehouse itself. Groups are just spawned at the warehouse or the associated airbase. + --- Triggers the FSM event "SelfRequest". Request was initiated from the warehouse to itself. Groups are just spawned at the warehouse or the associated airbase. -- If the warehouse is currently under attack when the self request is made, the self request is added to the defending table. One the attack is defeated, -- this request is used to put the groups back into the warehouse stock. -- @function [parent=#WAREHOUSE] SelfRequest @@ -1280,7 +1278,7 @@ function WAREHOUSE:New(warehouse, alias) -- @param Core.Set#SET_GROUP groupset The set of cargo groups that was delivered to the warehouse itself. -- @param #WAREHOUSE.Pendingitem request Pending self request. - --- Triggers the FSM event "SelfRequest" with a delay. Request was initiated to the warehouse itself. Groups are just spawned at the warehouse or the associated airbase. + --- Triggers the FSM event "SelfRequest" with a delay. Request was initiated from the warehouse to itself. Groups are just spawned at the warehouse or the associated airbase. -- If the warehouse is currently under attack when the self request is made, the self request is added to the defending table. One the attack is defeated, -- this request is used to put the groups back into the warehouse stock. -- @function [parent=#WAREHOUSE] __SelfRequest @@ -1289,7 +1287,27 @@ function WAREHOUSE:New(warehouse, alias) -- @param Core.Set#SET_GROUP groupset The set of cargo groups that was delivered to the warehouse itself. -- @param #WAREHOUSE.Pendingitem request Pending self request. - --- On after "SelfRequest" event. Request was initiated to the warehouse itself. Groups are just spawned at the warehouse or the associated airbase. + --- On after "SelfRequest" event. Request was initiated from the warehouse to itself. Groups are simply spawned at the warehouse or the associated airbase. + -- All requested assets are passed as a @{Core.Set#SET_GROUP} and can be used for further tasks or in other MOOSE classes. + -- Note that airborne assets are spawned in uncontrolled state so they do not simply "fly away" after spawning. + -- + -- @usage + -- --- Self request event. Triggered once the assets are spawned in the spawn zone or at the airbase. + -- function mywarehouse:OnAfterSelfRequest(From, Event, To, groupset, request) + -- local groupset=groupset --Core.Set#SET_GROUP + -- + -- -- Loop over all groups spawned from that request. + -- for _,group in pairs(groupset:GetSetObjects()) do + -- local group=group --Wrapper.Group#GROUP + -- + -- -- Gree smoke on spawned group. + -- group:SmokeGreen() + -- + -- -- Activate uncontrolled airborne group if necessary. + -- group:StartUncontrolled() + -- end + -- end + -- -- @function [parent=#WAREHOUSE] OnAfterSelfRequest -- @param #WAREHOUSE self -- @param #string From From state. @@ -1325,15 +1343,11 @@ function WAREHOUSE:New(warehouse, alias) --- Triggers the FSM event "Defeated" when an attack from an enemy was defeated. -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] Defeated - -- @param DCS#coalition.side Coalition which is attacking the warehouse. - -- @param DCS#country.id Country which is attacking the warehouse. --- Triggers the FSM event "Defeated" with a delay when an attack from an enemy was defeated. -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] __Defeated -- @param #number delay Delay in seconds. - -- @param DCS#coalition.side Coalition which is attacking the warehouse. - -- @param DCS#country.id Country which is attacking the warehouse. --- On after "Defeated" event user function. Called when an enemy attack was defeated. -- @param #WAREHOUSE self @@ -1341,8 +1355,6 @@ function WAREHOUSE:New(warehouse, alias) -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. - -- @param DCS#coalition.side Coalition which is attacking the warehouse. - -- @param DCS#country.id Country which is attacking the warehouse. --- Triggers the FSM event "Captured" when a warehouse has been captured by another coalition. @@ -1358,26 +1370,26 @@ function WAREHOUSE:New(warehouse, alias) -- @param DCS#coalition.side Coalition which captured the warehouse. -- @param DCS#country.id Country which has captured the warehouse. - --- On after "Captured" event user function. Called when the warehouse has been captured by an enemy coalition. + --- On after "Captured" event user function. Called when the warehouse has been captured by an enemy coalition. -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] OnAfterCaptured -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. - -- @param DCS#coalition.side Coalition which captured the warehouse. - -- @param DCS#country.id Country which has captured the warehouse. + -- @param DCS#coalition.side Coalition Coalition side which captured the warehouse, i.e. a number of @{DCS#coalition.side} enumerator. + -- @param DCS#country.id Country Country id which has captured the warehouse, i.e. a number @{DCS#country.id} enumerator. -- --- Triggers the FSM event "AirbaseCaptured" when the airbase of the warehouse has been captured by another coalition. -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] AirbaseCaptured - -- @param DCS#coalition.side Coalition which captured the airbase. + -- @param DCS#coalition.side Coalition Coalition side which captured the airbase, i.e. a number of @{DCS#coalition.side} enumerator. --- Triggers the FSM event "AirbaseCaptured" with a delay when the airbase of the warehouse has been captured by another coalition. -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] __AirbaseCaptured -- @param #number delay Delay in seconds. - -- @param DCS#coalition.side Coalition which captured the airbase. + -- @param DCS#coalition.side Coalition Coalition side which captured the airbase, i.e. a number of @{DCS#coalition.side} enumerator. --- On after "AirbaseCaptured" even user function. Called when the airbase of the warehouse has been captured by another coalition. -- @param #WAREHOUSE self @@ -1385,19 +1397,19 @@ function WAREHOUSE:New(warehouse, alias) -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. - -- @param DCS#coalition.side Coalition which captured the airbase. + -- @param DCS#coalition.side Coalition Coalition side which captured the airbase, i.e. a number of @{DCS#coalition.side} enumerator. --- Triggers the FSM event "AirbaseRecaptured" when the airbase of the warehouse has been re-captured from the other coalition. -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] AirbaseRecaptured - -- @param DCS#coalition.side Coalition which re-captured the airbase. + -- @param DCS#coalition.side Coalition Coalition which re-captured the airbase, i.e. the same as the current warehouse owner coalition. --- Triggers the FSM event "AirbaseRecaptured" with a delay when the airbase of the warehouse has been re-captured from the other coalition. -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] __AirbaseRecaptured -- @param #number delay Delay in seconds. - -- @param DCS#coalition.side Coalition which re-captured the airbase. + -- @param DCS#coalition.side Coalition Coalition which re-captured the airbase, i.e. the same as the current warehouse owner coalition. --- On after "AirbaseRecaptured" event user function. Called when the airbase of the warehouse has been re-captured from the other coalition. -- @param #WAREHOUSE self @@ -1405,7 +1417,7 @@ function WAREHOUSE:New(warehouse, alias) -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. - -- @param DCS#coalition.side Coalition which re-captured the airbase. + -- @param DCS#coalition.side Coalition Coalition which re-captured the airbase, i.e. the same as the current warehouse owner coalition. --- Triggers the FSM event "Destroyed" when the warehouse was destroyed. All services are stopped. @@ -1463,7 +1475,7 @@ function WAREHOUSE:SetReportOff() return self end ---- Set interval of status updates. Note that only one request can be processed per time interval. +--- Set interval of status updates. Note that normally only one request can be processed per time interval. -- @param #WAREHOUSE self -- @param #number timeinterval Time interval in seconds. -- @return #WAREHOUSE self @@ -1515,6 +1527,7 @@ end -- @return #WAREHOUSE self function WAREHOUSE:SetAirbase(airbase) self.airbase=airbase + self.airbasename=airbase:GetName() return self end @@ -1588,7 +1601,7 @@ function WAREHOUSE:AddShippingLane(remotewarehouse, group, oneway) for i=1,#lane do local coord=lane[i] --Core.Point#COORDINATE local text=string.format("Shipping lane %s to %s. Point %d.", self.alias, remotewarehouse.alias, i) - coord:MarkToCoalition(text, self.coalition) + coord:MarkToCoalition(text, self:GetCoalition()) end end @@ -1634,7 +1647,7 @@ function WAREHOUSE:AddOffRoadPath(remotewarehouse, group, oneway) for i=1,#path do local coord=path[i] --Core.Point#COORDINATE local text=string.format("Off road path from %s to %s. Point %d.", self.alias, remotewarehouse.alias, i) - coord:MarkToCoalition(text, self.coalition) + coord:MarkToCoalition(text, self:GetCoalition()) end end @@ -1872,6 +1885,70 @@ function WAREHOUSE:GetNumberOfAssets(Descriptor, DescriptorValue) end +--- Get coordinate of warehouse static. +-- @param #WAREHOUSE self +-- @return Core.Point#COORDINATE The coordinate of the warehouse. +function WAREHOUSE:GetCoordinate() + return self.warehouse:GetCoordinate() +end + +--- Get coalition side of warehouse static. +-- @param #WAREHOUSE self +-- @return #number Coalition side, i.e. number of @{DCS#coalition.side}. +function WAREHOUSE:GetCoalition() + return self.warehouse:GetCoalition() +end + +--- Get coalition name of warehouse static. +-- @param #WAREHOUSE self +-- @return #number Coalition side, i.e. number of @{DCS#coalition.side}. +function WAREHOUSE:GetCoalitionName() + return self.warehouse:GetCoalitionName() +end + +--- Get country id of warehouse static. +-- @param #WAREHOUSE self +-- @return #number Country id, i.e. number of @{DCS#country.id}. +function WAREHOUSE:GetCountry() + return self.warehouse:GetCountry() +end + +--- Get country name of warehouse static. +-- @param #WAREHOUSE self +-- @return #number Country id, i.e. number of @{DCS#coalition.side}. +function WAREHOUSE:GetCountryName() + return self.warehouse:GetCountryName() +end + +--- Get airbase associated to the warehouse. +-- @param #WAREHOUSE self +-- @return Wrapper.Airbase#AIRBASE Airbase object or nil if warehouse has no airbase connection. +function WAREHOUSE:GetAirbase() + return self.airbase +end + +--- Get name airbase associated to the warehouse. +-- @param #WAREHOUSE self +-- @return #string name of the airbase assosicated to the warehouse or "none" if the airbase has not airbase connection currently. +function WAREHOUSE:GetAirbaseName() + local name="none" + if self.airbase then + name=self.airbase:GetName() + end + return name +end + +--- Get category of airbase associated to the warehouse. +-- @param #WAREHOUSE self +-- @return #number Category of airbase or -1 if warehouse has (currently) no airbase. +function WAREHOUSE:GetAirbaseCategory() + local category=-1 + if self.airbase then + category=self.airbase:GetDesc().category + end + return category +end + --- Get assignment of a request. -- @param #WAREHOUSE self -- @param #WAREHOUSE.Pendingitem request The request from which the assignment is extracted. @@ -1880,6 +1957,48 @@ function WAREHOUSE:GetAssignment(request) return tostring(request.assignment) end +--- Get warehouse unique ID from static warehouse object. This is the ID under which you find the @{#WAREHOUSE} object in the global data base. +-- @param #WAREHOUSE self +-- @param #string staticname Name of the warehouse static object. +-- @return #number Warehouse unique ID. +function WAREHOUSE:GetWarehouseID(staticname) + local warehouse=STATIC:FindByName(staticname, true) + local uid=tonumber(warehouse:GetID()) + return uid +end + +--- Find a warehouse in the global warehouse data base. +-- @param #WAREHOUSE self +-- @param #number uid The unique ID of the warehouse. +-- @return #WAREHOUSE The warehouse object or nil if no warehouse exists. +function WAREHOUSE:FindWarehouseInDB(uid) + return WAREHOUSE.db.Warehouses[uid] +end + +--- Find an asset in the the global warehouse data base. Parameter is the MOOSE group object. +-- Note that the group name must contain they "AID" keyword. +-- @param #WAREHOUSE self +-- @param Wrapper.Group#GROUP group The group from which it is assumed that it has a registered asset. +-- @return #WAREHOUSE.Assetitem The asset from the data base or nil if it could not be found. +function WAREHOUSE:FindAssetInDB(group) + + -- Get unique ids from group name. + local wid,aid,rid=self:_GetIDsFromGroup(group) + + if aid~=nil then + + local asset=WAREHOUSE.db.Assets[aid] + self:E({asset=asset}) + if asset==nil then + self:_ErrorMessage(string.format("ERROR: Asset for group %s not found in the data base!", group:GetName()), 0) + end + return asset + end + + self:_ErrorMessage(string.format("ERROR: Group %s does not contain an asset ID in its name!", group:GetName()), 0) + return nil +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- FSM states ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -1893,21 +2012,19 @@ function WAREHOUSE:onafterStart(From, Event, To) -- Short info. local text=string.format("Starting warehouse %s alias %s:\n",self.warehouse:GetName(), self.alias) - text=text..string.format("Coaliton = %d\n", self.coalition) - text=text..string.format("Country = %d\n", self.country) - text=text..string.format("Airbase = %s (%s)\n", tostring(self.airbase:GetName()), tostring(self.category)) + text=text..string.format("Coaliton = %s\n", self:GetCoalitionName()) + text=text..string.format("Country = %s\n", self:GetCountryName()) + text=text..string.format("Airbase = %s (category=%d)\n", self:GetAirbaseName(), self:GetAirbaseCategory()) env.info(text) -- Save self in static object. Easier to retrieve later. self.warehouse:SetState(self.warehouse, "WAREHOUSE", self) -- Set airbase name and category. - if self.airbase and self.airbase:GetCoalition()==self.coalition then + if self.airbase and self.airbase:GetCoalition()==self:GetCoalition() then self.airbasename=self.airbase:GetName() - self.category=self.airbase:GetDesc().category else self.airbasename=nil - self.category=-1 -- The -1 indicates that we dont have an airbase at this warehouse. end -- THIS! caused aircraft to be spawned and started but they would never begin their route! @@ -2026,9 +2143,6 @@ end function WAREHOUSE:onafterStatus(From, Event, To) self:I(self.wid..string.format("Checking status of warehouse %s. Current FSM state %s. Global warehouse assets = %d.", self.alias, self:GetState(), #WAREHOUSE.db.Assets)) - -- Update coordinate in case we have a "moving" warehouse (e.g. on a carrier). - self.coordinate=self.warehouse:GetCoordinate() - -- Check if any pending jobs are done and can be deleted from the self:_JobDone() @@ -2282,7 +2396,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu --------------------------- -- Get the original warehouse this group belonged to. - local warehouse=self:_FindWarehouseInDB(wid) + local warehouse=self:FindWarehouseInDB(wid) if warehouse then local request=warehouse:_GetRequestOfGroup(group, warehouse.pending) if request then @@ -2305,7 +2419,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu end -- Get the asset from the global DB. - local asset=self:_FindAssetInDB(group) + local asset=self:FindAssetInDB(group) -- Note the group is only added once, i.e. the ngroups parameter is ignored here. -- This is because usually these request comes from an asset that has been transfered from another warehouse and hence should only be added once. @@ -2348,38 +2462,6 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu self:__Status(-1) end - ---- Find an asset in the the global warehouse db. --- @param #WAREHOUSE self --- @param Wrapper.Group#GROUP group The group from which it is assumed that it has a registered asset. --- @return #WAREHOUSE.Assetitem The asset from the data base or nil if it could not be found. -function WAREHOUSE:_FindAssetInDB(group) - - -- Get unique ids from group name. - local wid,aid,rid=self:_GetIDsFromGroup(group) - - if aid~=nil then - - local asset=WAREHOUSE.db.Assets[aid] - self:E({asset=asset}) - if asset==nil then - self:_ErrorMessage(string.format("ERROR: Asset for group %s not found in the data base!", group:GetName()), 0) - end - return asset - end - - self:_ErrorMessage(string.format("ERROR: Group %s does not contain an asset ID in its name!", group:GetName()), 0) - return nil -end - ---- Find a warehouse in the global warehouse data base. --- @param #WAREHOUSE self --- @param #number uid The unique ID of the warehouse. --- @return #WAREHOUSE The warehouse object or nil if no warehouse exists. -function WAREHOUSE:_FindWarehouseInDB(uid) - return WAREHOUSE.db.Warehouses[uid] -end - --- Register new asset in globase warehouse data base. -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group The group that will be added to the warehouse stock. @@ -2484,7 +2566,7 @@ end --- Asset item characteristics. -- @param #WAREHOUSE self --- @param #WAREHOUSE.Assetitem asset +-- @param #WAREHOUSE.Assetitem asset The asset for which info in printed in trace mode. function WAREHOUSE:_AssetItemInfo(asset) -- Info about asset: local text=string.format("\nNew asset with id=%d for warehouse %s:\n", asset.uid, self.alias) @@ -2622,8 +2704,8 @@ function WAREHOUSE:onafterAddRequest(From, Event, To, warehouse, AssetDescriptor transporttype=TransportType, ntransport=nTransport, assignment=tostring(Assignment), - airbase=warehouse.airbase, - category=warehouse.category, + airbase=warehouse:GetAirbase(), + category=warehouse:GetAirbaseCategory(), ndelivered=0, ntransporthome=0, assets={}, @@ -2654,7 +2736,7 @@ function WAREHOUSE:onbeforeRequest(From, Event, To, Request) self:T3({warehouse=self.alias, request=Request}) -- Distance from warehouse to requesting warehouse. - local distance=self.coordinate:Get2DDistance(Request.warehouse.coordinate) + local distance=self:GetCoordinate():Get2DDistance(Request.warehouse:GetCoordinate()) -- Shortcut to cargoassets. local _assets=Request.cargoassets @@ -3195,7 +3277,7 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) -- Route mobile ground group to the warehouse. Group has 60 seconds to get there or it is despawned and added as asset to the new warehouse regardless. if group:IsGround() and group:GetSpeedMax()>1 then - group:RouteGroundTo(warehouse.coordinate, group:GetSpeedMax()*0.3, "Off Road") + group:RouteGroundTo(warehouse:GetCoordinate(), group:GetSpeedMax()*0.3, "Off Road") end -- Increase number of cargo delivered and transports home. @@ -3248,7 +3330,7 @@ function WAREHOUSE:onafterDelivered(From, Event, To, request) self:_InfoMessage(text, 5) -- Make some noise :) - self:_Fireworks(request.warehouse.coordinate) + self:_Fireworks(request.warehouse:GetCoordinate()) -- Set delivered status for this request uid. self.delivered[request.uid]=true @@ -3318,7 +3400,7 @@ function WAREHOUSE:onafterAttacked(From, Event, To, Coalition, Country) -- Debug smoke. if self.Debug then - self.coordinate:SmokeOrange() + self:GetCoordinate():SmokeOrange() end -- Spawn all ground units in the spawnzone? @@ -3354,7 +3436,7 @@ function WAREHOUSE:onafterDefeated(From, Event, To) -- Debug smoke. if self.Debug then - self.coordinate:SmokeGreen() + self:GetCoordinate():SmokeGreen() end -- Auto defence: put assets back into stock. @@ -3368,7 +3450,7 @@ function WAREHOUSE:onafterDefeated(From, Event, To) -- Get max speed of group and route it back slowly to the warehouse. local speed=group:GetSpeedMax() if group:IsGround() and speed>1 then - group:RouteGroundTo(self.coordinate, speed*0.3) + group:RouteGroundTo(self:GetCoordinate(), speed*0.3) end -- Add asset group back to stock after 60 seconds. @@ -3392,16 +3474,12 @@ end function WAREHOUSE:onafterCaptured(From, Event, To, Coalition, Country) -- Message. - local text=string.format("Warehouse %s: We were captured by enemy coalition (%d)!", self.alias, Coalition) + local text=string.format("Warehouse %s: We were captured by enemy coalition (ID=%d)!", self.alias, Coalition) self:_InfoMessage(text) -- Respawn warehouse with new coalition/country. self.warehouse:ReSpawn(Country) - -- Set new country and coalition - self.coalition=Coalition - self.country=Country - -- Delete all waiting requests because they are not valid any more self.queue=nil self.queue={} @@ -3410,22 +3488,20 @@ function WAREHOUSE:onafterCaptured(From, Event, To, Coalition, Country) local airbase=AIRBASE:FindByName(self.airbasename) local airbasecoaltion=airbase:GetCoalition() - if self.coalition==airbasecoaltion then + if Coalition==airbasecoaltion then -- Airbase already owned by the coalition that captured the warehouse. Airbase can be used by this warehouse. self.airbase=airbase - self.category=airbase:GetDesc().category else -- Airbase is owned by other coalition. So this warehouse does not have an airbase unil it is captured. self.airbase=nil - self.category=-1 end -- Debug smoke. if self.Debug then if Coalition==coalition.side.RED then - self.coordinate:SmokeRed() + self:GetCoordinate():SmokeRed() elseif Coalition==coalition.side.BLUE then - self.coordinate:SmokeBlue() + self:GetCoordinate():SmokeBlue() end end @@ -3454,7 +3530,6 @@ function WAREHOUSE:onafterAirbaseCaptured(From, Event, To, Coalition) -- Set airbase to nil and category to no airbase. self.airbase=nil - self.category=-1 -- -1 indicates no airbase. end --- On after "AirbaseRecaptured" event. Airbase of warehouse has been re-captured from other coalition. @@ -3462,7 +3537,7 @@ end -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- @param DCS#coalition.side Coalition which captured the warehouse. +-- @param DCS#coalition.side Coalition Coalition side which originally captured the warehouse. function WAREHOUSE:onafterAirbaseRecaptured(From, Event, To, Coalition) -- Message. @@ -3471,7 +3546,6 @@ function WAREHOUSE:onafterAirbaseRecaptured(From, Event, To, Coalition) -- Set airbase and category. self.airbase=AIRBASE:FindByName(self.airbasename) - self.category=self.airbase:GetDesc().category -- Debug smoke. if self.Debug then @@ -3713,7 +3787,7 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol -- Get airbase ID and category. local AirbaseID = self.airbase:GetID() - local AirbaseCategory = self.category + local AirbaseCategory = self:GetAirbaseCategory() -- Check enough parking spots. if AirbaseCategory==Airbase.Category.HELIPAD or AirbaseCategory==Airbase.Category.SHIP then @@ -3809,8 +3883,8 @@ function WAREHOUSE:_SpawnAssetPrepareTemplate(asset, alias) template.name=alias -- Set current(!) coalition and country. - template.CoalitionID=self.coalition - template.CountryID=self.country + template.CoalitionID=self:GetCoalition() + template.CountryID=self:GetCountry() -- Nillify the group ID. template.groupId=nil @@ -4423,19 +4497,19 @@ function WAREHOUSE:_OnEventBaseCaptured(EventData) local NewCoalitionAirbase=airbase:GetCoalition() -- Debug info - self:T(self.wid..string.format("Airbase of warehouse %s (coalition = %d) was captured! New owner coalition = %d.",self.alias, self.coalition, NewCoalitionAirbase)) + self:T(self.wid..string.format("Airbase of warehouse %s (coalition ID=%d) was captured! New owner coalition ID=%d.",self.alias, self:GetCoalition(), NewCoalitionAirbase)) -- So what can happen? -- Warehouse is blue, airbase is blue and belongs to warehouse and red captures it ==> self.airbase=nil -- Warehouse is blue, airbase is blue self.airbase is nil and blue (re-)captures it ==> self.airbase=Event.Place if self.airbase==nil then -- New coalition is the same as of the warehouse ==> warehouse previously lost this airbase and now it was re-captured. - if NewCoalitionAirbase == self.coalition then + if NewCoalitionAirbase == self:GetCoalition() then self:AirbaseRecaptured(NewCoalitionAirbase) end else -- Captured airbase belongs to this warehouse but was captured by other coaltion. - if NewCoalitionAirbase ~= self.coalition then + if NewCoalitionAirbase ~= self:GetCoalition() then self:AirbaseCaptured(NewCoalitionAirbase) end end @@ -4506,8 +4580,8 @@ function WAREHOUSE:_CheckConquered() -- Figure out the new coalition if any. -- Condition is that only units of one coalition are within the zone. - local newcoalition=self.coalition - local newcountry=self.country + local newcoalition=self:GetCoalition() + local newcountry=self:GetCountry() if Nblue>0 and Nred==0 and Nneutral==0 then -- Only blue units in zone ==> Zone goes to blue. newcoalition=coalition.side.BLUE @@ -4523,14 +4597,14 @@ function WAREHOUSE:_CheckConquered() end -- Coalition has changed ==> warehouse was captured! This should be before the attack check. - if self:IsAttacked() and newcoalition ~= self.coalition then + if self:IsAttacked() and newcoalition ~= self:GetCoalition() then self:Captured(newcoalition, newcountry) return end -- Before a warehouse can be captured, it has to be attacked. -- That is, even if only enemy units are present it is not immediately captured in order to spawn all ground assets for defence. - if self.coalition==coalition.side.BLUE then + if self:GetCoalition()==coalition.side.BLUE then -- Blue warehouse is running and we have red units in the zone. if self:IsRunning() and Nred>0 then self:Attacked(coalition.side.RED, CountryRed) @@ -4539,7 +4613,7 @@ function WAREHOUSE:_CheckConquered() if self:IsAttacked() and Nred==0 then self:Defeated() end - elseif self.coalition==coalition.side.RED then + elseif self:GetCoalition()==coalition.side.RED then -- Red Warehouse is running and we have blue units in the zone. if self:IsRunning() and Nblue>0 then self:Attacked(coalition.side.BLUE, CountryBlue) @@ -4548,7 +4622,7 @@ function WAREHOUSE:_CheckConquered() if self:IsAttacked() and Nblue==0 then self:Defeated() end - elseif self.coalition==coalition.side.NEUTRAL then + elseif self:GetCoalition()==coalition.side.NEUTRAL then -- Neutrals dont attack! end @@ -4566,17 +4640,15 @@ function WAREHOUSE:_CheckAirbaseOwner() if self.airbase then -- Warehouse has lost its airbase. - if self.coalition~=airbasecurrentcoalition then + if self:GetCoalition()~=airbasecurrentcoalition then self.airbase=nil - self.category=-1 end else -- Warehouse has re-captured the airbase. - if self.coalition==airbasecurrentcoalition then + if self:GetCoalition()==airbasecurrentcoalition then self.airbase=airbase - self.category=airbase:GetDesc().category end end @@ -4611,8 +4683,8 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) end -- Request from enemy coalition? - if self.coalition~=request.warehouse.coalition then - self:E(self.wid..string.format("ERROR: INVALID request. Requesting warehouse is of wrong coaltion! Own coalition %d != %d of requesting warehouse.", self.coalition, request.warehouse.coalition)) + if self:GetCoalition()~=request.warehouse:GetCoalition() then + self:E(self.wid..string.format("ERROR: INVALID request. Requesting warehouse is of wrong coaltion! Own coalition %s != %s of requesting warehouse.", self:GetCoalitionName(), request.warehouse:GetCoalitionName())) valid=false end @@ -4681,6 +4753,9 @@ function WAREHOUSE:_CheckRequestValid(request) -- Assume everything is okay. local valid=true + -- Category of the requesting warehouse airbase. + local requestcategory=request.warehouse:GetAirbaseCategory() + if request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then ------------------------------------------- -- Case where the units go my themselves -- @@ -4691,7 +4766,7 @@ function WAREHOUSE:_CheckRequestValid(request) if asset_plane then -- No airplane to or from FARPS. - if request.category==Airbase.Category.HELIPAD or self.category==Airbase.Category.HELIPAD then + if requestcategory==Airbase.Category.HELIPAD or self:GetAirbaseCategory()==Airbase.Category.HELIPAD then self:E("ERROR: Incorrect request. Asset airplane requested but warehouse or requestor is HELIPAD/FARP!") valid=false end @@ -4703,7 +4778,7 @@ function WAREHOUSE:_CheckRequestValid(request) -- Helos need a FARP or AIRBASE or SHIP for spawning. Also at the the receiving warehouse. So even if they could go there they "cannot" be spawned again. -- Unless I allow spawning of helos in the the spawn zone. But one should place at least a FARP there. - if self.category==-1 or request.category==-1 then + if self:GetAirbaseCategory()==-1 or requestcategory==-1 then self:E("ERROR: Incorrect request. Helos need a AIRBASE/HELIPAD/SHIP as home/destination base!") valid=false end @@ -4751,7 +4826,7 @@ function WAREHOUSE:_CheckRequestValid(request) -- No ground assets directly to or from ships. -- TODO: May needs refinement if warehouse is on land and requestor is ship in harbour?! - if (request.category==Airbase.Category.SHIP or self.category==Airbase.Category.SHIP) then + if (requestcategory==Airbase.Category.SHIP or self:GetAirbaseCategory()==Airbase.Category.SHIP) then self:E("ERROR: Incorrect request. Ground asset requested but warehouse or requestor is SHIP!") valid=false end @@ -4804,7 +4879,7 @@ function WAREHOUSE:_CheckRequestValid(request) if request.transporttype==WAREHOUSE.TransportType.AIRPLANE then -- Airplanes only to AND from airdromes. - if self.category~=Airbase.Category.AIRDROME or request.category~=Airbase.Category.AIRDROME then + if self:GetAirbaseCategory()~=Airbase.Category.AIRDROME or requestcategory~=Airbase.Category.AIRDROME then self:E("ERROR: Incorrect request. Warehouse or requestor does not have an airdrome. No transport by plane possible!") valid=false end @@ -4816,7 +4891,7 @@ function WAREHOUSE:_CheckRequestValid(request) -- Transport by ground units. -- No transport to or from ships - if self.category==Airbase.Category.SHIP or request.category==Airbase.Category.SHIP then + if self:GetAirbaseCategory()==Airbase.Category.SHIP or requestcategory==Airbase.Category.SHIP then self:E("ERROR: Incorrect request. Warehouse or requestor is SHIP. No transport by APC possible!") valid=false end @@ -4831,7 +4906,7 @@ function WAREHOUSE:_CheckRequestValid(request) elseif request.transporttype==WAREHOUSE.TransportType.HELICOPTER then -- Transport by helicopters ==> need airbase for spawning but not for delivering to the spawn zone of the receiver. - if self.category==-1 then + if self:GetAirbaseCategory()==-1 then self:E("ERROR: Incorrect request. Warehouse has no airbase. Transport by helicopter not possible!") valid=false end @@ -5933,13 +6008,7 @@ function WAREHOUSE:_PrintQueue(queue, name) for i,qitem in ipairs(queue) do local qitem=qitem --#WAREHOUSE.Pendingitem - - -- Set airbase: - local airbasename="none" - if qitem.airbase then - airbasename=qitem.airbase:GetName() - end - + local uid=qitem.uid local prio=qitem.prio local clock="N/A" @@ -5948,7 +6017,8 @@ function WAREHOUSE:_PrintQueue(queue, name) end local assignment=tostring(qitem.assignment) local requestor=qitem.warehouse.alias - local requestorAirbaseCat=qitem.category + local airbasename=qitem.warehouse:GetAirbaseName() + local requestorAirbaseCat=qitem.warehouse:GetAirbaseCategory() local assetdesc=qitem.assetdesc local assetdescval=qitem.assetdescval local nasset=tostring(qitem.nasset) @@ -5986,20 +6056,13 @@ end --- Display status of warehouse. -- @param #WAREHOUSE self -function WAREHOUSE:_DisplayStatus() - - -- Set airbase name. - local airbasename="none" - if self.airbase then - airbasename=self.airbase:GetName() - end - +function WAREHOUSE:_DisplayStatus() local text=string.format("\n------------------------------------------------------\n") text=text..string.format("Warehouse %s status: %s\n", self.alias, self:GetState()) text=text..string.format("------------------------------------------------------\n") - text=text..string.format("Coalition side = %d\n", self.coalition) - text=text..string.format("Country name = %d\n", self.country) - text=text..string.format("Airbase name = %s\n", airbasename) + text=text..string.format("Coalition name = %d\n", self:GetCoalitionName()) + text=text..string.format("Country name = %d\n", self:GetCountryName()) + text=text..string.format("Airbase name = %s\n", self:GetAirbaseName()) text=text..string.format("Queued requests = %d\n", #self.queue) text=text..string.format("Pending requests = %d\n", #self.pending) text=text..string.format("------------------------------------------------------\n") @@ -6016,13 +6079,6 @@ function WAREHOUSE:_GetStockAssetsText(messagetoall) -- Get assets in stock. local _data=self:GetStockInfo(self.stock) - --[[ - local function _sort(a,b) - return a[1]0 then - MESSAGE:New(text, duration):ToCoalitionIf(self.coalition, self.Debug or self.Report) + MESSAGE:New(text, duration):ToCoalitionIf(self:GetCoalition(), self.Debug or self.Report) end self:I(self.wid..text) end diff --git a/Moose Development/Moose/Wrapper/Identifiable.lua b/Moose Development/Moose/Wrapper/Identifiable.lua index 0f6e14f06..13a5b3234 100644 --- a/Moose Development/Moose/Wrapper/Identifiable.lua +++ b/Moose Development/Moose/Wrapper/Identifiable.lua @@ -206,8 +206,19 @@ function IDENTIFIABLE:GetCountry() self:F( self.ClassName .. " " .. self.IdentifiableName .. " not found!" ) return nil end - +--- Returns country name of the Identifiable. +-- @param #IDENTIFIABLE self +-- @return #string Name of the country. +function IDENTIFIABLE:GetCountryName() + self:F2( self.IdentifiableName ) + local countryid=self:GetCountry() + for name,id in pairs(country.id) do + if countryid==id then + return name + end + end +end --- Returns Identifiable descriptor. Descriptor type depends on Identifiable category. -- @param #IDENTIFIABLE self From 4ab94ecf26a86f286e8188929768e5e4ceff72c7 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Fri, 14 Sep 2018 21:59:30 +0200 Subject: [PATCH 357/420] Fixes for performance in DESIGNATE. --- .../Moose/Functional/Designate.lua | 168 ++++++++++-------- .../Moose/Functional/Detection.lua | 2 +- 2 files changed, 96 insertions(+), 74 deletions(-) diff --git a/Moose Development/Moose/Functional/Designate.lua b/Moose Development/Moose/Functional/Designate.lua index 25d554466..3d120744b 100644 --- a/Moose Development/Moose/Functional/Designate.lua +++ b/Moose Development/Moose/Functional/Designate.lua @@ -936,90 +936,112 @@ do -- DESIGNATE return self end - --- Sets the Designate Menu. + + --- Sets the Designate Menu for one attack groups. + -- @param #DESIGNATE self + -- @return #DESIGNATE + function DESIGNATE:SetMenu( AttackGroup ) + + self.MenuDesignate = self.MenuDesignate or {} + + local MissionMenu = nil + + if self.Mission then + --MissionMenu = self.Mission:GetRootMenu( AttackGroup ) + MissionMenu = self.Mission:GetMenu( AttackGroup ) + end + + local MenuTime = timer.getTime() + + self.MenuDesignate[AttackGroup] = MENU_GROUP_DELAYED:New( AttackGroup, self.DesignateName, MissionMenu ):SetTime( MenuTime ):SetTag( self.DesignateName ) + local MenuDesignate = self.MenuDesignate[AttackGroup] -- Core.Menu#MENU_GROUP_DELAYED + + -- Set Menu option for auto lase + + if self.AutoLase then + MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Auto Lase Off", MenuDesignate, self.MenuAutoLase, self, false ):SetTime( MenuTime ):SetTag( self.DesignateName ) + else + MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Auto Lase On", MenuDesignate, self.MenuAutoLase, self, true ):SetTime( MenuTime ):SetTag( self.DesignateName ) + end + + local StatusMenu = MENU_GROUP_DELAYED:New( AttackGroup, "Status", MenuDesignate ):SetTime( MenuTime ):SetTag( self.DesignateName ) + MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Report Status", StatusMenu, self.MenuStatus, self, AttackGroup ):SetTime( MenuTime ):SetTag( self.DesignateName ) + + if self.FlashStatusMenu[AttackGroup] then + MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Flash Status Report Off", StatusMenu, self.MenuFlashStatus, self, AttackGroup, false ):SetTime( MenuTime ):SetTag( self.DesignateName ) + else + MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Flash Status Report On", StatusMenu, self.MenuFlashStatus, self, AttackGroup, true ):SetTime( MenuTime ):SetTag( self.DesignateName ) + end + + local DesignateCount = 0 + + for DesignateIndex, Designating in pairs( self.Designating ) do + + local DetectedItem = self.Detection:GetDetectedItemByIndex( DesignateIndex ) + + if DetectedItem then + + local Coord = self.Detection:GetDetectedItemCoordinate( DetectedItem ) + local ID = self.Detection:GetDetectedItemID( DetectedItem ) + local MenuText = ID --.. ", " .. Coord:ToStringA2G( AttackGroup ) + + MenuText = string.format( "(%3s) %s", Designating, MenuText ) + local DetectedMenu = MENU_GROUP_DELAYED:New( AttackGroup, MenuText, MenuDesignate ):SetTime( MenuTime ):SetTag( self.DesignateName ) + + -- Build the Lasing menu. + if string.find( Designating, "L", 1, true ) == nil then + MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Search other target", DetectedMenu, self.MenuForget, self, DesignateIndex ):SetTime( MenuTime ):SetTag( self.DesignateName ) + for LaserCode, MenuText in pairs( self.MenuLaserCodes ) do + MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, string.format( MenuText, LaserCode ), DetectedMenu, self.MenuLaseCode, self, DesignateIndex, 60, LaserCode ):SetTime( MenuTime ):SetTag( self.DesignateName ) + end + MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Lase with random laser code(s)", DetectedMenu, self.MenuLaseOn, self, DesignateIndex, 60 ):SetTime( MenuTime ):SetTag( self.DesignateName ) + else + MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Stop lasing", DetectedMenu, self.MenuLaseOff, self, DesignateIndex ):SetTime( MenuTime ):SetTag( self.DesignateName ) + end + + -- Build the Smoking menu. + if string.find( Designating, "S", 1, true ) == nil then + MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Smoke red", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.Red ):SetTime( MenuTime ):SetTag( self.DesignateName ) + MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Smoke blue", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.Blue ):SetTime( MenuTime ):SetTag( self.DesignateName ) + MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Smoke green", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.Green ):SetTime( MenuTime ):SetTag( self.DesignateName ) + MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Smoke white", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.White ):SetTime( MenuTime ):SetTag( self.DesignateName ) + MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Smoke orange", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.Orange ):SetTime( MenuTime ):SetTag( self.DesignateName ) + end + + -- Build the Illuminate menu. + if string.find( Designating, "I", 1, true ) == nil then + MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Illuminate", DetectedMenu, self.MenuIlluminate, self, DesignateIndex ):SetTime( MenuTime ):SetTag( self.DesignateName ) + end + end + + DesignateCount = DesignateCount + 1 + if DesignateCount > 10 then + break + end + end + MenuDesignate:Remove( MenuTime, self.DesignateName ) + MenuDesignate:Set() + end + + + --- Sets the Designate Menu for all the attack groups. -- @param #DESIGNATE self -- @return #DESIGNATE function DESIGNATE:SetDesignateMenu() self.AttackSet:Flush( self ) + + local Delay = 1 self.AttackSet:ForEachGroupAlive( --- @param Wrapper.Group#GROUP GroupReport function( AttackGroup ) - self.MenuDesignate = self.MenuDesignate or {} - - local MissionMenu = nil - - if self.Mission then - --MissionMenu = self.Mission:GetRootMenu( AttackGroup ) - MissionMenu = self.Mission:GetMenu( AttackGroup ) - end - - local MenuTime = timer.getTime() - - self.MenuDesignate[AttackGroup] = MENU_GROUP_DELAYED:New( AttackGroup, self.DesignateName, MissionMenu ):SetTime( MenuTime ):SetTag( self.DesignateName ) - local MenuDesignate = self.MenuDesignate[AttackGroup] -- Core.Menu#MENU_GROUP_DELAYED - - -- Set Menu option for auto lase - - if self.AutoLase then - MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Auto Lase Off", MenuDesignate, self.MenuAutoLase, self, false ):SetTime( MenuTime ):SetTag( self.DesignateName ) - else - MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Auto Lase On", MenuDesignate, self.MenuAutoLase, self, true ):SetTime( MenuTime ):SetTag( self.DesignateName ) - end - - local StatusMenu = MENU_GROUP_DELAYED:New( AttackGroup, "Status", MenuDesignate ):SetTime( MenuTime ):SetTag( self.DesignateName ) - MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Report Status", StatusMenu, self.MenuStatus, self, AttackGroup ):SetTime( MenuTime ):SetTag( self.DesignateName ) - - if self.FlashStatusMenu[AttackGroup] then - MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Flash Status Report Off", StatusMenu, self.MenuFlashStatus, self, AttackGroup, false ):SetTime( MenuTime ):SetTag( self.DesignateName ) - else - MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Flash Status Report On", StatusMenu, self.MenuFlashStatus, self, AttackGroup, true ):SetTime( MenuTime ):SetTag( self.DesignateName ) - end - for DesignateIndex, Designating in pairs( self.Designating ) do - - local DetectedItem = self.Detection:GetDetectedItemByIndex( DesignateIndex ) - - if DetectedItem then - - local Coord = self.Detection:GetDetectedItemCoordinate( DetectedItem ) - local ID = self.Detection:GetDetectedItemID( DetectedItem ) - local MenuText = ID --.. ", " .. Coord:ToStringA2G( AttackGroup ) - - MenuText = string.format( "(%3s) %s", Designating, MenuText ) - local DetectedMenu = MENU_GROUP_DELAYED:New( AttackGroup, MenuText, MenuDesignate ):SetTime( MenuTime ):SetTag( self.DesignateName ) - - -- Build the Lasing menu. - if string.find( Designating, "L", 1, true ) == nil then - MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Search other target", DetectedMenu, self.MenuForget, self, DesignateIndex ):SetTime( MenuTime ):SetTag( self.DesignateName ) - for LaserCode, MenuText in pairs( self.MenuLaserCodes ) do - MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, string.format( MenuText, LaserCode ), DetectedMenu, self.MenuLaseCode, self, DesignateIndex, 60, LaserCode ):SetTime( MenuTime ):SetTag( self.DesignateName ) - end - MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Lase with random laser code(s)", DetectedMenu, self.MenuLaseOn, self, DesignateIndex, 60 ):SetTime( MenuTime ):SetTag( self.DesignateName ) - else - MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Stop lasing", DetectedMenu, self.MenuLaseOff, self, DesignateIndex ):SetTime( MenuTime ):SetTag( self.DesignateName ) - end - - -- Build the Smoking menu. - if string.find( Designating, "S", 1, true ) == nil then - MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Smoke red", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.Red ):SetTime( MenuTime ):SetTag( self.DesignateName ) - MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Smoke blue", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.Blue ):SetTime( MenuTime ):SetTag( self.DesignateName ) - MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Smoke green", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.Green ):SetTime( MenuTime ):SetTag( self.DesignateName ) - MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Smoke white", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.White ):SetTime( MenuTime ):SetTag( self.DesignateName ) - MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Smoke orange", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.Orange ):SetTime( MenuTime ):SetTag( self.DesignateName ) - end - - -- Build the Illuminate menu. - if string.find( Designating, "I", 1, true ) == nil then - MENU_GROUP_COMMAND_DELAYED:New( AttackGroup, "Illuminate", DetectedMenu, self.MenuIlluminate, self, DesignateIndex ):SetTime( MenuTime ):SetTag( self.DesignateName ) - end - end - end - MenuDesignate:Remove( MenuTime, self.DesignateName ) - MenuDesignate:Set() - end + self:ScheduleOnce( Delay, self.SetMenu, self, AttackGroup ) + Delay = Delay + 1 + end + ) return self diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 42f25311a..bde32e8cf 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -2027,7 +2027,7 @@ do -- DETECTION_UNITS local DetectedFirstUnitCoord = DetectedFirstUnit:GetCoordinate() self:SetDetectedItemCoordinate( DetectedItem, DetectedFirstUnitCoord, DetectedFirstUnit ) - self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table + --self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table self:SetDetectedItemThreatLevel( DetectedItem ) self:NearestRecce( DetectedItem ) From 88734fa9fcc94b4b0ae343bc4f72ec9afb3b0e1f Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sat, 15 Sep 2018 00:31:55 +0200 Subject: [PATCH 358/420] Warehouse v0.4.5 Fixed overlapping aircraft on parking spots. Other fixes. --- .../Moose/Functional/Warehouse.lua | 74 ++++++++++--------- Moose Development/Moose/Wrapper/Client.lua | 4 +- 2 files changed, 43 insertions(+), 35 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 9b37cea88..eaa7e9ff9 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -985,13 +985,12 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.4.4w" +WAREHOUSE.version="0.4.5" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Check overlapping aircraft sometimes. -- TODO: Spawn assets only virtually, i.e. remove requested assets from stock but do NOT spawn them ==> Interface to A2A dispatcher! Maybe do a negative sign on asset number? -- TODO: Test capturing a neutral warehouse. -- TODO: Make more examples: ARTY, CAP, ... @@ -999,6 +998,7 @@ WAREHOUSE.version="0.4.4w" -- TODO: Handle the case when units of a group die during the transfer. -- TODO: Added habours as interface for transport to from warehouses? -- TODO: Add save/load capability of warehouse <==> percistance after mission restart. Difficult in lua! +-- DONE: Check overlapping aircraft sometimes. -- DONE: Case when all transports are killed and there is still cargo to be delivered. Put cargo back into warehouse. Should be done now! -- DONE: Add transport units from dispatchers back to warehouse stock once they completed their mission. -- DONE: Write documentation. @@ -3355,10 +3355,6 @@ function WAREHOUSE:onafterSelfRequest(From, Event, To, groupset, request) -- Debug info. for _,_group in pairs(groupset:GetSetObjects()) do local group=_group --Wrapper.Group#GROUP - - --local text=string.format("Group name = %s, IsAlive=%s.", tostring(group:GetName()), tostring(group:IsAlive())) - --env.info(text) - if self.Debug then group:FlareGreen() end @@ -3378,11 +3374,10 @@ function WAREHOUSE:onafterSelfRequest(From, Event, To, groupset, request) end end + -- Add request to defenders. table.insert(self.defending, request) end - -- Remove pending request. - --self:_DeleteQueueItem(request, self.pending) end --- On after "Attacked" event. Warehouse is under attack by an another coalition. @@ -5412,7 +5407,7 @@ end function WAREHOUSE:_FindParkingForAssets(airbase, assets) -- Init default - local scanradius=50 + local scanradius=100 local scanunits=true local scanstatics=true local scanscenery=false @@ -5420,7 +5415,7 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) -- Function calculating the overlap of two (square) objects. local function _overlap(l1,l2,dist) - local safedist=(l1/2+l2/2)*1.1 + local safedist=(l1/2+l2/2)*1.1 -- 10% safety margine added to safe distance! local safe = (dist > safedist) self:T3(string.format("l1=%.1f l2=%.1f s=%.1f d=%.1f ==> safe=%s", l1,l2,safedist,dist,tostring(safe))) return safe @@ -5432,18 +5427,15 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) -- List of obstacles. local obstacles={} - -- Loop over all parking spots and get the obstacles. + -- Loop over all parking spots and get the currently present obstacles. -- How long does this take on very large airbases, i.e. those with hundereds of parking spots? Seems to be okay! for _,parkingspot in pairs(parkingdata) do -- Coordinate of the parking spot. local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE local _termid=parkingspot.TerminalID - - -- Obstacles at or around this parking spot. - obstacles[_termid]={} - -- Scan a radius of 50 meters around the spot. + -- Scan a radius of 100 meters around the spot. local _,_,_,_units,_statics,_sceneries=_spot:ScanObjects(scanradius, scanunits, scanstatics, scanscenery) -- Check all units. @@ -5452,7 +5444,7 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) local _coord=unit:GetCoordinate() local _size=self:_GetObjectSize(unit:GetDCSObject()) local _name=unit:GetName() - table.insert(obstacles[_termid],{coord=_coord, size=_size, name=_name, type="unit"}) + table.insert(obstacles, {coord=_coord, size=_size, name=_name, type="unit"}) end -- Check all statics. @@ -5461,7 +5453,7 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) local _coord=COORDINATE:NewFromVec3(_vec3) local _name=static:getName() local _size=self:_GetObjectSize(static) - table.insert(obstacles[_termid],{coord=_coord, size=_size, name=_name, type="static"}) + table.insert(obstacles, {coord=_coord, size=_size, name=_name, type="static"}) end -- Check all scenery. @@ -5470,21 +5462,23 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) local _coord=COORDINATE:NewFromVec3(_vec3) local _name=scenery:getTypeName() local _size=self:_GetObjectSize(scenery) - table.insert(obstacles[_termid],{coord=_coord, size=_size, name=_name, type="scenery"}) + table.insert(obstacles,{coord=_coord, size=_size, name=_name, type="scenery"}) end - -- TODO Clients? Unoccupied client aircraft are also important! Are they already included in scanned units maybe? --[[ + -- TODO Clients? Unoccupied client aircraft are also important! Are they already included in scanned units maybe? local clients=_DATABASE.CLIENTS for _,_client in pairs(clients) do local client=_client --Wrapper.Client#CLIENT - local unit=client:GetClientGroupUnit() + env.info(string.format("FF Client name %s", client:GetName())) + local unit=UNIT:FindByName(client:GetName()) + --local unit=client:GetClientGroupUnit() local _coord=unit:GetCoordinate() local _name=unit:GetName() local _size=self:_GetObjectSize(client:GetClientGroupDCSUnit()) - table.insert(obstacles[_termid],{coord=_coord, size=_size, name=_name, type="client"}) - end - ]] + table.insert(obstacles,{coord=_coord, size=_size, name=_name, type="client"}) + end + ]] end -- Parking data for all assets. @@ -5492,9 +5486,9 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) -- Loop over all assets that need a parking psot. for _,asset in pairs(assets) do - local _asset=asset --#WAREHOUSE.Assetitem + -- Get terminal type of this asset local terminaltype=self:_GetTerminal(asset.attribute) -- Asset specific parking. @@ -5505,20 +5499,23 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) -- Loop over all parking spots. local gotit=false - for _,parkingspot in pairs(parkingdata) do + for _,_parkingspot in pairs(parkingdata) do + local parkingspot=_parkingspot --Wrapper.Airbase#AIRBASE.ParkingSpot -- Check correct terminal type for asset. We don't want helos in shelters etc. - if AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) then + if AIRBASE._CheckTerminalType(parkingspot.TerminalType, terminaltype) then -- Coordinate of the parking spot. local _spot=parkingspot.Coordinate -- Core.Point#COORDINATE local _termid=parkingspot.TerminalID local _toac=parkingspot.TOAC + + --env.info(string.format("FF asset=%s (id=%d): needs terminal type=%d, id=%d, #obstacles=%d", _asset.templatename, _asset.uid, terminaltype, _termid, #obstacles)) -- Loop over all obstacles. local free=true local problem=nil - for _,obstacle in pairs(obstacles[_termid]) do + for _,obstacle in pairs(obstacles) do -- Check if aircraft overlaps with any obstacle. local dist=_spot:Get2DDistance(obstacle.coord) @@ -5526,14 +5523,18 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) -- Spot is blocked. if not safe then + --env.info(string.format("FF asset=%s (id=%d): spot id=%d dist=%.1fm is NOT SAFE", _asset.templatename, _asset.uid, _termid, dist)) free=false problem=obstacle problem.dist=dist break + else + --env.info(string.format("FF asset=%s (id=%d): spot id=%d dist=%.1fm is SAFE", _asset.templatename, _asset.uid, _termid, dist)) end end + -- Check if spot is free if free then -- Add parkingspot for this asset unit. @@ -5543,23 +5544,27 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) -- Add the unit as obstacle so that this spot will not be available for the next unit. -- TODO Alternatively, I could remove this parking spot from the table, right? - table.insert(obstacles[_termid], {coord=_spot, size=_asset.size, name=_asset.templatename, type="asset"}) + table.insert(obstacles, {coord=_spot, size=_asset.size, name=_asset.templatename, type="asset"}) gotit=true break + else + + -- Debug output for occupied spots. self:T(self.wid..string.format("Parking spot #%d is occupied or not big enough!", _termid)) if self.Debug then local coord=problem.coord --Core.Point#COORDINATE local text=string.format("Obstacle blocking spot #%d is %s type %s with size=%.1f m and distance=%.1f m.", _termid, problem.name, problem.type, problem.size, problem.dist) coord:MarkToAll(string.format(text)) end + end end -- check terminal type end -- loop over parking spots - + -- No parking spot for at least one asset :( if not gotit then self:T(self.wid..string.format("WARNING: No free parking spot for asset id=%d",_asset.uid)) return nil @@ -5916,7 +5921,10 @@ end --- Size of the bounding box of a DCS object derived from the DCS descriptor table. If boundinb box is nil, a size of zero is returned. -- @param #WAREHOUSE self -- @param DCS#Object DCSobject The DCS object for which the size is needed. --- @return #number Max size of object in meters. +-- @return #number Max size of object in meters (length (x) or width (z) components not including height (y)). +-- @return #number Length (x component) of size. +-- @return #number Height (y component) of size. +-- @return #number Width (z component) of size. function WAREHOUSE:_GetObjectSize(DCSobject) local DCSdesc=DCSobject:getDesc() if DCSdesc.box then @@ -6060,9 +6068,9 @@ function WAREHOUSE:_DisplayStatus() local text=string.format("\n------------------------------------------------------\n") text=text..string.format("Warehouse %s status: %s\n", self.alias, self:GetState()) text=text..string.format("------------------------------------------------------\n") - text=text..string.format("Coalition name = %d\n", self:GetCoalitionName()) - text=text..string.format("Country name = %d\n", self:GetCountryName()) - text=text..string.format("Airbase name = %s\n", self:GetAirbaseName()) + text=text..string.format("Coalition name = %s\n", self:GetCoalitionName()) + text=text..string.format("Country name = %s\n", self:GetCountryName()) + text=text..string.format("Airbase name = %s (category=%d)\n", self:GetAirbaseName(), self:GetAirbaseCategory()) text=text..string.format("Queued requests = %d\n", #self.queue) text=text..string.format("Pending requests = %d\n", #self.pending) text=text..string.format("------------------------------------------------------\n") diff --git a/Moose Development/Moose/Wrapper/Client.lua b/Moose Development/Moose/Wrapper/Client.lua index 7df3c0586..a4178538e 100644 --- a/Moose Development/Moose/Wrapper/Client.lua +++ b/Moose Development/Moose/Wrapper/Client.lua @@ -376,8 +376,8 @@ end -- @param #CLIENT self -- @return Wrapper.Unit#UNIT function CLIENT:GetClientGroupUnit() - self:F2() - + self:F2() + local ClientDCSUnit = Unit.getByName( self.ClientName ) self:T( self.ClientDCSUnit ) From 47aaf6f6b22ab60657dcb1fd6c6c963dc396c28f Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sat, 15 Sep 2018 07:06:07 +0200 Subject: [PATCH 359/420] Redo including set of friendlies --- Moose Development/Moose/Functional/Detection.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index bde32e8cf..42f25311a 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -2027,7 +2027,7 @@ do -- DETECTION_UNITS local DetectedFirstUnitCoord = DetectedFirstUnit:GetCoordinate() self:SetDetectedItemCoordinate( DetectedItem, DetectedFirstUnitCoord, DetectedFirstUnit ) - --self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table + self:ReportFriendliesNearBy( { DetectedItem = DetectedItem, ReportSetGroup = self.DetectionSetGroup } ) -- Fill the Friendlies table self:SetDetectedItemThreatLevel( DetectedItem ) self:NearestRecce( DetectedItem ) From f8f000eae5f39f445ad217fbdefdcb99a0dccff7 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sat, 15 Sep 2018 07:45:20 +0200 Subject: [PATCH 360/420] Fixing problems with STATICS used for task transportation. I think it should work now. --- Moose Development/Moose/Core/Database.lua | 3 +++ Moose Development/Moose/Core/SpawnStatic.lua | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 913ec4fe9..901723088 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -188,7 +188,10 @@ function DATABASE:AddStatic( DCSStaticName ) if not self.STATICS[DCSStaticName] then self.STATICS[DCSStaticName] = STATIC:Register( DCSStaticName ) + return self.STATICS[DCSStaticName] end + + return nil end diff --git a/Moose Development/Moose/Core/SpawnStatic.lua b/Moose Development/Moose/Core/SpawnStatic.lua index 334201e4c..a4d0036a4 100644 --- a/Moose Development/Moose/Core/SpawnStatic.lua +++ b/Moose Development/Moose/Core/SpawnStatic.lua @@ -183,8 +183,8 @@ function SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1 local Static = coalition.addStaticObject( CountryID, StaticTemplate ) self.SpawnIndex = self.SpawnIndex + 1 - - return Static + + return _DATABASE:AddStatic(Static:getName()) end return nil @@ -216,7 +216,7 @@ function SPAWNSTATIC:ReSpawn() local Static = coalition.addStaticObject( CountryID, StaticTemplate ) - return Static + return _DATABASE:AddStatic(Static:getName()) end return nil @@ -247,7 +247,7 @@ function SPAWNSTATIC:ReSpawnAt( Coordinate, Heading ) local Static = coalition.addStaticObject( CountryID, StaticTemplate ) - return Static + return _DATABASE:AddStatic(Static:getName()) end return nil @@ -265,6 +265,6 @@ function SPAWNSTATIC:SpawnFromZone( Zone, Heading, NewName ) --R2.1 local Static = self:SpawnFromPointVec2( Zone:GetPointVec2(), Heading, NewName ) - return Static + return _DATABASE:AddStatic(Static:getName()) end From e16d6c0ca44ce74a07a0e2d62a5ba5612bdf3bc3 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sat, 15 Sep 2018 20:26:08 +0200 Subject: [PATCH 361/420] Warehouse v0.4.6 Little adjustments. Controllable: Fixed bombing task. --- Moose Development/Moose/Core/Point.lua | 10 ++-- .../Moose/Functional/Warehouse.lua | 5 +- .../Moose/Wrapper/Controllable.lua | 52 ++++++++++++++----- 3 files changed, 48 insertions(+), 19 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index c899a48c6..b5d173644 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -964,7 +964,7 @@ do -- COORDINATE -- @param DCS#Speed Speed Airspeed in km/h. Default is 500 km/h. -- @param #boolean SpeedLocked true means the speed is locked. -- @param Wrapper.Airbase#AIRBASE airbase The airbase for takeoff and landing points. - -- @param #table DCSTasks A table of DCS#Task items which are executed at the waypoint. + -- @param #table DCSTasks A table of @{DCS#Task} items which are executed at the waypoint. -- @param #string description A text description of the waypoint, which will be shown on the F10 map. -- @return #table The route point. function COORDINATE:WaypointAir( AltType, Type, Action, Speed, SpeedLocked, airbase, DCSTasks, description ) @@ -1028,7 +1028,7 @@ do -- COORDINATE RoutePoint.task.params = {} RoutePoint.task.params.tasks = DCSTasks or {} - self:E({RoutePoint=RoutePoint}) + self:T({RoutePoint=RoutePoint}) return RoutePoint end @@ -1037,9 +1037,11 @@ do -- COORDINATE -- @param #COORDINATE self -- @param #COORDINATE.WaypointAltType AltType The altitude type. -- @param DCS#Speed Speed Airspeed in km/h. + -- @param #table DCSTasks (Optional) A table of @{DCS#Task} items which are executed at the waypoint. + -- @param #string description (Optional) A text description of the waypoint, which will be shown on the F10 map. -- @return #table The route point. - function COORDINATE:WaypointAirTurningPoint( AltType, Speed ) - return self:WaypointAir( AltType, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, Speed ) + function COORDINATE:WaypointAirTurningPoint( AltType, Speed, DCSTasks, description ) + return self:WaypointAir( AltType, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, Speed, true, nil, DCSTasks, description ) end diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index eaa7e9ff9..73ba2f6c6 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -985,7 +985,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.4.5" +WAREHOUSE.version="0.4.6" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -5415,7 +5415,7 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) -- Function calculating the overlap of two (square) objects. local function _overlap(l1,l2,dist) - local safedist=(l1/2+l2/2)*1.1 -- 10% safety margine added to safe distance! + local safedist=(l1/2+l2/2)*1.05 -- 5% safety margine added to safe distance! local safe = (dist > safedist) self:T3(string.format("l1=%.1f l2=%.1f s=%.1f d=%.1f ==> safe=%s", l1,l2,safedist,dist,tostring(safe))) return safe @@ -5543,7 +5543,6 @@ function WAREHOUSE:_FindParkingForAssets(airbase, assets) self:T(self.wid..string.format("Parking spot #%d is free for asset id=%d!", _termid, _asset.uid)) -- Add the unit as obstacle so that this spot will not be available for the next unit. - -- TODO Alternatively, I could remove this parking spot from the table, right? table.insert(obstacles, {coord=_spot, size=_asset.size, name=_asset.templatename, type="asset"}) gotit=true diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 92aed3843..256294fac 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -726,28 +726,56 @@ end -- @param DCS#Azimuth Direction (optional) Desired ingress direction from the target to the attacking aircraft. Controllable/aircraft will make its attacks from the direction. Of course if there is no way to attack from the direction due the terrain controllable/aircraft will choose another direction. -- @param #number Altitude (optional) The altitude from where to attack. -- @param #number WeaponType (optional) The WeaponType. +-- @param #boolean Divebomb (optional) Perform dive bombing. Default false. -- @return DCS#Task The DCS task structure. -function CONTROLLABLE:TaskBombing( Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType ) - self:F2( { self.ControllableName, Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType } ) +function CONTROLLABLE:TaskBombing( Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType, Divebomb ) + self:E( { self.ControllableName, Vec2, GroupAttack, WeaponExpend, AttackQty, Direction, Altitude, WeaponType, Divebomb } ) + + local _groupattack=false + if GroupAttack then + _groupattack=GroupAttack + end + + local _direction=0 + local _directionenabled=false + if Direction then + _direction=math.rad(Direction) + _directionenabled=true + end + + local _altitude=5000 + local _altitudeenabled=false + if Altitude then + _altitude=Altitude + _altitudeenabled=true + end + + local _attacktype=nil + if Divebomb then + _attacktype="Dive" + end + local DCSTask DCSTask = { id = 'Bombing', params = { - point = Vec2, - groupAttack = GroupAttack or false, + x = Vec2.x, + y = Vec2.y, + groupAttack = _groupattack, expend = WeaponExpend or "Auto", - attackQtyLimit = AttackQty and true or false, - attackQty = AttackQty, - directionEnabled = Direction and true or false, - direction = Direction, - altitudeEnabled = Altitude and true or false, - altitude = Altitude or 30, + attackQtyLimit = false, --AttackQty and true or false, + attackQty = AttackQty or 1, + directionEnabled = _directionenabled, + direction = _direction, + altitudeEnabled = _altitudeenabled, + altitude = _altitude, weaponType = WeaponType, + --attackType=_attacktype, }, - }, + } - self:T3( { DCSTask } ) + self:E( { TaskBombing=DCSTask } ) return DCSTask end From 8f08fd87befef281be54079441b81ed0135ff74b Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sun, 16 Sep 2018 01:36:41 +0200 Subject: [PATCH 362/420] Warehouse v0.4.7 Added EWR as attribute. Fixed bug in shipping lanes. Added airbase check in arrived event. Removed starting point from shipping lane since it is in the port zone anyway. --- .../Moose/Functional/Warehouse.lua | 72 ++++++------------- 1 file changed, 23 insertions(+), 49 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 73ba2f6c6..39abc47c4 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -425,7 +425,7 @@ -- -- This section shows some examples how the WAREHOUSE class is used in practice. This is one of the best ways to explain things, in my opinion. -- --- But first, let me introduce a convenient way to define several warehouses in a table. This is absolutely *not* necessary but quite handy if you have +-- But first, let me introduce a convenient way to define several warehouses in a table. This is absolutely *not necessary* but quite handy if you have -- multiple WAREHOUSE objects in your mission. -- -- ## Example 0: Setting up a Warehouse Array @@ -899,11 +899,12 @@ WAREHOUSE.Descriptor = { -- @field #string AIR_UAV Unpiloted Aerial Vehicle, e.g. drones. -- @field #string AIR_OTHER Any airborne unit that does not fall into any other airborne category. -- @field #string GROUND_APC Infantry carriers, in particular Amoured Personell Carrier. This can be used to transport other assets. --- @field #string GROUND_TRUCK Unarmed ground vehicles. +-- @field #string GROUND_TRUCK Unarmed ground vehicles, which has the DCS "Truck" attribute. -- @field #string GROUND_INFANTRY Ground infantry assets. -- @field #string GROUND_ARTILLERY Artillery assets. -- @field #string GROUND_TANK Tanks (modern or old). -- @field #string GROUND_TRAIN Trains. Not that trains are **not** yet properly implemented in DCS and cannot be used currently. +-- @field #string GROUND_EWR Early Warning Radar. -- @field #string GROUND_AAA Anti-Aircraft Artillery. -- @field #string GROUND_SAM Surface-to-Air Missile system or components. -- @field #string GROUND_OTHER Any ground unit that does not fall into any other ground category. @@ -929,6 +930,7 @@ WAREHOUSE.Attribute = { GROUND_ARTILLERY="Ground_Artillery", GROUND_TANK="Ground_Tank", GROUND_TRAIN="Ground_Train", + GROUND_EWR="Ground_EWR", GROUND_AAA="Ground_AAA", GROUND_SAM="Ground_SAM", GROUND_OTHER="Ground_OtherGround", @@ -985,7 +987,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.4.6" +WAREHOUSE.version="0.4.7" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -1710,7 +1712,7 @@ function WAREHOUSE:_NewLane(group, startcoord, finalcoord) end -- Add beginning and end. - table.insert(lane, 1, startcoord) + --table.insert(lane, 1, startcoord) table.insert(lane, #lane, finalcoord) return lane @@ -4000,12 +4002,9 @@ function WAREHOUSE:_RouteNaval(group, request) -- Set speed to 80% of max possible. local _speed=group:GetSpeedMax()*0.8 - -- Get shipping lane to remote warehouse. - --local lane=self.shippinglanes[request.warehouse.warehouse:GetName()] - - -- Get off road path to remote warehouse. If more have been defined, pick one randomly. + -- Get shipping lane to remote warehouse. If more have been defined, pick one randomly. local remotename=request.warehouse.warehouse:GetName() - local lane=self.shippinglanes[remotename][math.random(#self.ship[remotename])] + local lane=self.shippinglanes[remotename][math.random(#self.shippinglanes[remotename])] if lane then @@ -4189,29 +4188,6 @@ function WAREHOUSE:_OnEventLanding(EventData) -- Debug info. self:T(self.wid..string.format("Warehouse %s captured event landing of its asset unit %s.", self.alias, EventData.IniUnitName)) - --[[ - -- Check if all cargo was delivered. - if self.delivered[rid]==true then - - -- Check if helicopter landed in spawn zone. If so, we call it a day and add it back to stock. - if group:GetCategory()==Group.Category.HELICOPTER then - if self.spawnzone:IsCoordinateInZone(EventData.IniUnit:GetCoordinate()) then - - -- Debug message. - self:_DebugMessage("Helicopter landed in spawn zone. No pending request. Putting back into stock.") - if self.Debug then - group:SmokeWhite() - end - - -- Group arrived. - self:Arrived(group) - - end - end - - end - ]] - end end end @@ -4262,9 +4238,12 @@ function WAREHOUSE:_OnEventArrived(EventData) local request=self:_GetRequestOfGroup(group, self.pending) local istransport=self:_GroupIsTransport(group,request) + + -- Check if engine shutdown happend at right airbase because the event is also triggered in other situations. + local rightairbase=group:GetCoordinate():GetClosestAirbase():GetName()==request.warehouse:GetAirbase():GetName() -- Check that group is cargo and not transport. - if istransport==false then + if istransport==false and rightairbase then -- Debug info. local text=string.format("Air asset group %s from warehouse %s arrived at its destination.", group:GetName(), self.alias) @@ -4456,14 +4435,6 @@ function WAREHOUSE:_UnitDead(deadunit, request) end end -end - ---- Remove group. --- @param #WAREHOUSE self --- @param Wrapper.Group#GROUP group The group to be removed. -function WAREHOUSE:_RemoveGroup(group) - - end ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -5828,10 +5799,10 @@ function WAREHOUSE:_GetAttribute(group) -- Planes local transportplane=group:HasAttribute("Transports") and group:HasAttribute("Planes") local awacs=group:HasAttribute("AWACS") - local fighter=group:HasAttribute("Fighters") or group:HasAttribute("Interceptors") or group:HasAttribute("Multirole fighters") - local bomber=group:HasAttribute("Bombers") + local fighter=group:HasAttribute("Fighters") or group:HasAttribute("Interceptors") or group:HasAttribute("Multirole fighters") or (group:HasAttribute("Bombers") and not group:HasAttribute("Strategic bombers")) + local bomber=group:HasAttribute("Strategic bombers") local tanker=group:HasAttribute("Tankers") - local uav=group:HasAttribute("UAV") + local uav=group:HasAttribute("UAVs") -- Helicopters local transporthelo=group:HasAttribute("Transport helicopters") local attackhelicopter=group:HasAttribute("Attack helicopters") @@ -5841,12 +5812,13 @@ function WAREHOUSE:_GetAttribute(group) -------------- -- Ground local apc=group:HasAttribute("Infantry carriers") - local truck=group:HasAttribute("Trucks") and not group:GetCategory()==Group.Category.TRAIN + local truck=group:HasAttribute("Trucks") and group:GetCategory()==Group.Category.GROUND local infantry=group:HasAttribute("Infantry") local artillery=group:HasAttribute("Artillery") local tank=group:HasAttribute("Old Tanks") or group:HasAttribute("Modern Tanks") local aaa=group:HasAttribute("AAA") - local sam=group:HasAttribute("SAM") + local ewr=group:HasAttribute("EWR") + local sam=group:HasAttribute("SAM elements") and (not group:HasAttribute("AAA")) -- Train local train=group:GetCategory()==Group.Category.TRAIN @@ -5879,8 +5851,6 @@ function WAREHOUSE:_GetAttribute(group) attribute=WAREHOUSE.Attribute.AIR_UAV elseif apc then attribute=WAREHOUSE.Attribute.GROUND_APC - elseif truck then - attribute=WAREHOUSE.Attribute.GROUND_TRUCK elseif infantry then attribute=WAREHOUSE.Attribute.GROUND_INFANTRY elseif artillery then @@ -5889,8 +5859,12 @@ function WAREHOUSE:_GetAttribute(group) attribute=WAREHOUSE.Attribute.GROUND_TANK elseif aaa then attribute=WAREHOUSE.Attribute.GROUND_AAA + elseif ewr then + attribute=WAREHOUSE.Attribute.GROUND_EWR elseif sam then - attribute=WAREHOUSE.Attribute.GROUND_SAM + attribute=WAREHOUSE.Attribute.GROUND_SAM + elseif truck then + attribute=WAREHOUSE.Attribute.GROUND_TRUCK elseif train then attribute=WAREHOUSE.Attribute.GROUND_TRAIN elseif aircraftcarrier then From 2d029c640508230faaa62de1378ae53c58e6cad3 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Sun, 16 Sep 2018 12:52:13 +0200 Subject: [PATCH 363/420] Warehouse v0.4.8 Set loadradius no nil for APC dispatcher. Adjustet general image. Use SetAirbase() function in New() Remove Stop() from Destroyed. --- .../Moose/Functional/Warehouse.lua | 83 ++++++++++--------- 1 file changed, 44 insertions(+), 39 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 39abc47c4..c58bde6fe 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -18,7 +18,7 @@ -- ### Authors: **funkyfranky**, FlightControl (cargo dispatcher classes) -- -- @module Functional.Warehouse --- @image Warehouse.JPG +-- @image MOOSE.JPG --- WAREHOUSE class. -- @type WAREHOUSE @@ -281,7 +281,6 @@ -- added two times while Path_2(A->B) was added only once. Hence, the group will choose Path_1 with a probability of 66.6 % while Path_2 is only chosen with -- a probability of 33.3 %. -- --- -- ## Rail Connections -- -- A rail connection is automatically defined as the closest point on a railway measured from the center of the spawn zone. But only, if the distance is less than 3 km. @@ -384,15 +383,15 @@ -- Technically, the capturing of the airbase is triggered by the DCS [S\_EVENT\_BASE\_CAPTURED](https://wiki.hoggitworld.com/view/DCS_event_base_captured) event. -- So the capturing takes place when only enemy ground units are in the airbase zone whilst no ground units of the present airbase owner are in that zone. -- --- The warehouse will also create an event named "AirbaseCaptured", which can be captured by the @{#WAREHOUSE.OnAfterAirbaseCaptured} function. So the warehouse can react on --- this attack and for example spawn ground groups to re-capture its airbase. +-- The warehouse will also create an event **AirbaseCaptured**, which can be captured by the @{#WAREHOUSE.OnAfterAirbaseCaptured} function. So the warehouse chief can react on +-- this attack and for example deploy ground groups to re-capture its airbase. -- --- When an airbase is re-captured the event "AirbaseRecaptured" is triggered and can be captured by the @{#WAREHOUSE.OnAfterAirbaseRecaptured} function. +-- When an airbase is re-captured the event **AirbaseRecaptured** is triggered and can be captured by the @{#WAREHOUSE.OnAfterAirbaseRecaptured} function. -- This can be used to put the defending assets back into the warehouse stock. -- -- ## Capturing the Warehouse -- --- A warehouse can also be captured by the enemy coalition. If enemy ground troops enter the warehouse zone the event **Attacked** is triggered which can be captured by the +-- A warehouse can be captured by the enemy coalition. If enemy ground troops enter the warehouse zone the event **Attacked** is triggered which can be captured by the -- @{#WAREHOUSE.OnAfterAttacked} event. By default the warehouse zone circular zone with a radius of 500 meters located at the center of the physical warehouse. -- The warehouse zone can be set via the @{#WAREHOUSE.SetWarehouseZone}(*zone*) function. The parameter *zone* must also be a cirular zone. -- @@ -723,7 +722,7 @@ -- warehouse.Stennis:AddAsset("CH-53E", 3) -- -- -- Define a "port" at the Stennis to be able to spawn Naval assets. This zone will move behind the Stennis. --- local stenniszone=ZONE_UNIT:New("Spawnzone Stennis", UNIT:FindByName("Stennis"), 100, {rho=250, theta=180, relative_to_unit=true}) +-- local stenniszone=ZONE_UNIT:New("Spawnzone Stennis", UNIT:FindByName("USS Stennis"), 100, {rho=250, theta=180, relative_to_unit=true}) -- warehouse.Stennis:SetPortZone(stenniszone) -- -- -- Self request of rescue helo and speed boats. @@ -738,7 +737,7 @@ -- local request=request --Functional.Warehouse#WAREHOUSE.Pendingitem -- -- -- USS Stennis is the mother ship. --- local Mother=UNIT:FindByName("Stennis") +-- local Mother=UNIT:FindByName("USS Stennis") -- -- -- Get assignment for this request. -- local assignment=warehouse.Stennis:GetAssignment(request) @@ -987,12 +986,13 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.4.7" +WAREHOUSE.version="0.4.8" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- - +-- TODO: Get cargo bay and weight from CARGO_GROUP and GROUP. +-- TODO: Add possibility to set weight and cargo bay manually in AddAsset function as optional parameters. -- TODO: Spawn assets only virtually, i.e. remove requested assets from stock but do NOT spawn them ==> Interface to A2A dispatcher! Maybe do a negative sign on asset number? -- TODO: Test capturing a neutral warehouse. -- TODO: Make more examples: ARTY, CAP, ... @@ -1080,13 +1080,12 @@ function WAREHOUSE:New(warehouse, alias) -- Closest of the same coalition but within a certain range. local _airbase=self:GetCoordinate():GetClosestAirbase(nil, self:GetCoalition()) if _airbase and _airbase:GetCoordinate():Get2DDistance(self:GetCoordinate()) < 3000 then - self.airbase=_airbase - self.airbasename=self.airbase:GetName() + self:SetAirbase(_airbase) end -- Define warehouse and default spawn zone. self.zone=ZONE_RADIUS:New(string.format("Warehouse zone %s", self.warehouse:GetName()), warehouse:GetVec2(), 500) - self.spawnzone=ZONE_RADIUS:New(string.format("Warehouse %s spawn zone", self.warehouse:GetName()), warehouse:GetVec2(), 200) + self.spawnzone=ZONE_RADIUS:New(string.format("Warehouse %s spawn zone", self.warehouse:GetName()), warehouse:GetVec2(), 250) -- Add warehouse to database. WAREHOUSE.db.Warehouses[self.uid]=self @@ -1422,16 +1421,16 @@ function WAREHOUSE:New(warehouse, alias) -- @param DCS#coalition.side Coalition Coalition which re-captured the airbase, i.e. the same as the current warehouse owner coalition. - --- Triggers the FSM event "Destroyed" when the warehouse was destroyed. All services are stopped. + --- Triggers the FSM event "Destroyed" when the warehouse was destroyed. Services are stopped. -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] Destroyed - --- Triggers the FSM event "Destroyed" with a delay when the warehouse was destroyed. All services are stopped. + --- Triggers the FSM event "Destroyed" with a delay when the warehouse was destroyed. Services are stopped. -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] Destroyed -- @param #number delay Delay in seconds. - --- On after "Destroyed" event user function. Called when the warehouse was destroyed. All services are stopped. + --- On after "Destroyed" event user function. Called when the warehouse was destroyed. Services are stopped. -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] OnAfterDestroyed -- @param #string From From state. @@ -1529,7 +1528,11 @@ end -- @return #WAREHOUSE self function WAREHOUSE:SetAirbase(airbase) self.airbase=airbase - self.airbasename=airbase:GetName() + if airbase~=nil then + self.airbasename=airbase:GetName() + else + self.airbasename=nil + end return self end @@ -1695,7 +1698,7 @@ function WAREHOUSE:_NewLane(group, startcoord, finalcoord) local distF=startcoord:Get2DDistance(coordF) local distL=startcoord:Get2DDistance(coordL) - -- Add the shipping lane. Need to take care of the wrong "direction". + -- Add the lane. Need to take care of the wrong "direction". local lane={} if distF ishome=%s", group:GetName(), speed, tostring(onground), airbase, tostring(inspawnzone), tostring(ishome)) - self:E(self.wid..text) + self:T(self.wid..text) if ishome then -- Info message. - self:_InfoMessage(string.format("Warehouse %s: No cargo assets left for request id=%s. Remaining %s transport assets go back into stock!", self.alias, request.uid, ntransport)) + local text=string.format("Warehouse %s: Transport group arrived back home and no cargo left for request id=%d.\nSending transport group %s back to stock.", self.alias, request.uid, group:GetName()) + self:_InfoMessage(text) + -- Debug smoke. if self.Debug then group:SmokeRed() end @@ -2453,7 +2451,8 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu -- Destroy group if it is alive. if group:IsAlive()==true then self:_DebugMessage(string.format("Destroying group %s.", group:GetName()), 5) - group:Destroy() + -- Setting parameter to false, i.e. creating NO dead or remove unit event, seems to not confuse the dispatcher logic. + group:Destroy(false) end else @@ -2894,7 +2893,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Add groups to cargo if they don't go by themselfs. local CargoGroups --Core.Set#SET_CARGO - --TODO: make nearradius depended on transport type and asset type. + -- Load radius and near radius. local _loadradius=5000 local _nearradius=nil @@ -2904,7 +2903,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) --_loadradius=1000 _loadradius=nil elseif Request.transporttype==WAREHOUSE.TransportType.APC then - _loadradius=1000 + _loadradius=nil end --_loadradius=nil @@ -3568,7 +3567,8 @@ function WAREHOUSE:onafterDestroyed(From, Event, To) self:_InfoMessage(text) -- Stop warehouse FSM in one minute. - self:__Stop(60) + -- Maybe dont stop it or pending requests are not updated any more. + --self:__Stop(60) end --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -3580,7 +3580,7 @@ end -- @param #WAREHOUSE.Queueitem Request Information table of the request. -- @return Core.Set#SET_GROUP Set of groups that were spawned. function WAREHOUSE:_SpawnAssetRequest(Request) - self:E({requestUID=Request.uid}) + self:F2({requestUID=Request.uid}) -- Shortcut to cargo assets. local _assetstock=Request.cargoassets @@ -5103,8 +5103,13 @@ function WAREHOUSE:_GetTransportsForAssets(request) -- Get all transports of the requested type in stock. local transports=self:_FilterStock(self.stock, WAREHOUSE.Descriptor.ATTRIBUTE, request.transporttype) - -- Copy asset. - local cargoassets=UTILS.DeepCopy(request.cargoassets) + -- Copy asset. + local cargoassets=UTILS.DeepCopy(request.cargoassets) + local cargoset=request.transportcargoset + + -- TODO: Get weight and cargo bay from CARGO_GROUP + --local cargogroup=CARGO_GROUP:New(CargoGroup,Type,Name,LoadRadius,NearRadius) + --cargogroup:GetWeight() -- Sort transport carriers w.r.t. cargo bay size. local function sort_transports(a,b) @@ -5955,7 +5960,7 @@ function WAREHOUSE:_DeleteQueueItem(qitem, queue) for i=1,#queue do local _item=queue[i] --#WAREHOUSE.Queueitem if _item.uid==qitem.uid then - self:I(self.wid..string.format("Deleting queue item %d.", qitem.uid)) + self:T(self.wid..string.format("Deleting queue item id=%d.", qitem.uid)) table.remove(queue,i) break end From a0502a12fa19d21194fdd2668f17a42d48875f72 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sun, 16 Sep 2018 15:37:22 +0200 Subject: [PATCH 364/420] Fixing SPAWNING of STATICS for CARGO and respawning of STATICS for CARGO. Please test. --- Moose Development/Moose/Cargo/CargoCrate.lua | 2 +- Moose Development/Moose/Core/Database.lua | 14 +++- Moose Development/Moose/Core/Spawn.lua | 2 +- Moose Development/Moose/Core/SpawnStatic.lua | 84 ++++++++------------ Moose Development/Moose/Wrapper/Static.lua | 53 ++++++++++++ 5 files changed, 101 insertions(+), 54 deletions(-) diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua index b12869c16..92fccec02 100644 --- a/Moose Development/Moose/Cargo/CargoCrate.lua +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -154,7 +154,7 @@ do -- CARGO_CRATE if self.CargoObject then self:T("Destroying") self.NoDestroy = true - self.CargoObject:Destroy() + self.CargoObject:Destroy( false ) -- Do not generate a remove unit event, because we want to keep the template for later respawn in the database. --local Coordinate = self.CargoObject:GetCoordinate():GetRandomCoordinateInRadius( 50, 20 ) --self.CargoObject:ReSpawnAt( Coordinate, 0 ) end diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 901723088..5e8c32d1c 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -672,10 +672,12 @@ end --- Private method that registers new Static Templates within the DATABASE Object. -- @param #DATABASE self --- @param #table GroupTemplate +-- @param #table StaticTemplate -- @return #DATABASE self function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID ) + local StaticTemplate = UTILS.DeepCopy( StaticTemplate ) + local StaticTemplateName = env.getValueDictByKey(StaticTemplate.name) self.Templates.Statics[StaticTemplateName] = self.Templates.Statics[StaticTemplateName] or {} @@ -704,11 +706,17 @@ end --- @param #DATABASE self -function DATABASE:GetStaticUnitTemplate( StaticName ) - local StaticTemplate = self.Templates.Statics[StaticName].UnitTemplate +function DATABASE:GetStaticGroupTemplate( StaticName ) + local StaticTemplate = self.Templates.Statics[StaticName].GroupTemplate return StaticTemplate, self.Templates.Statics[StaticName].CoalitionID, self.Templates.Statics[StaticName].CategoryID, self.Templates.Statics[StaticName].CountryID end +--- @param #DATABASE self +function DATABASE:GetStaticUnitTemplate( StaticName ) + local UnitTemplate = self.Templates.Statics[StaticName].UnitTemplate + return UnitTemplate, self.Templates.Statics[StaticName].CoalitionID, self.Templates.Statics[StaticName].CategoryID, self.Templates.Statics[StaticName].CountryID +end + function DATABASE:GetGroupNameFromUnitName( UnitName ) return self.Templates.Units[UnitName].GroupName diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 7a6160490..fd62653f1 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1,4 +1,4 @@ ---- **Core** -- SPAWN class dynamically spawns new groups of units in your missions. +--- **Core** --Spawn dynamically new GROUPs of UNITs in your missions. -- -- === -- diff --git a/Moose Development/Moose/Core/SpawnStatic.lua b/Moose Development/Moose/Core/SpawnStatic.lua index a4d0036a4..2efce77f7 100644 --- a/Moose Development/Moose/Core/SpawnStatic.lua +++ b/Moose Development/Moose/Core/SpawnStatic.lua @@ -82,7 +82,7 @@ function SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, SpawnCountryID ) --R2.1 local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC self:F( { SpawnTemplatePrefix } ) - local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticUnitTemplate( SpawnTemplatePrefix ) + local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( SpawnTemplatePrefix ) if TemplateStatic then self.SpawnTemplatePrefix = SpawnTemplatePrefix self.CountryID = SpawnCountryID or CountryID @@ -90,7 +90,7 @@ function SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, SpawnCountryID ) --R2.1 self.CoalitionID = CoalitionID self.SpawnIndex = 0 else - error( "SPAWNSTATIC:New: There is no group declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" ) + error( "SPAWNSTATIC:New: There is no static declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" ) end self:SetEventPriority( 5 ) @@ -124,25 +124,22 @@ end function SPAWNSTATIC:Spawn( Heading, NewName ) --R2.3 self:F( { Heading, NewName } ) - local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix ) + local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix ) if StaticTemplate then - local CountryID = self.CountryID - local CountryName = _DATABASE.COUNTRY_NAME[CountryID] + local StaticUnitTemplate = StaticTemplate.units[1] StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex ) StaticTemplate.heading = ( Heading / 180 ) * math.pi - StaticTemplate.CountryID = nil - StaticTemplate.CoalitionID = nil - StaticTemplate.CategoryID = nil - - local Static = coalition.addStaticObject( CountryID, StaticTemplate ) + _DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID ) + + local Static = coalition.addStaticObject( CountryID, StaticTemplate.units[1] ) self.SpawnIndex = self.SpawnIndex + 1 - return Static + return _DATABASE:FindStatic(Static:getName()) end return nil @@ -159,32 +156,31 @@ end function SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1 self:F( { PointVec2, Heading, NewName } ) - local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix ) + local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix ) if StaticTemplate then - - local CountryID = self.CountryID - local CountryName = _DATABASE.COUNTRY_NAME[CountryID] - - StaticTemplate.x = PointVec2.x - StaticTemplate.y = PointVec2.z - StaticTemplate.units = nil + local StaticUnitTemplate = StaticTemplate.units[1] + + StaticUnitTemplate.x = PointVec2.x + StaticUnitTemplate.y = PointVec2.z + StaticTemplate.route = nil StaticTemplate.groupId = nil StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex ) - StaticTemplate.heading = ( Heading / 180 ) * math.pi + StaticUnitTemplate.name = StaticTemplate.name + StaticUnitTemplate.heading = ( Heading / 180 ) * math.pi - StaticTemplate.CountryID = nil - StaticTemplate.CoalitionID = nil - StaticTemplate.CategoryID = nil + _DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID) - local Static = coalition.addStaticObject( CountryID, StaticTemplate ) + self:F({StaticTemplate = StaticTemplate}) + + local Static = coalition.addStaticObject( CountryID, StaticTemplate.units[1] ) self.SpawnIndex = self.SpawnIndex + 1 - return _DATABASE:AddStatic(Static:getName()) + return _DATABASE:FindStatic(Static:getName()) end return nil @@ -199,24 +195,18 @@ end -- @return #SPAWNSTATIC function SPAWNSTATIC:ReSpawn() - local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix ) + local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix ) if StaticTemplate then - local CountryID = self.CountryID - local CountryName = _DATABASE.COUNTRY_NAME[CountryID] - - StaticTemplate.units = nil + local StaticUnitTemplate = StaticTemplate.units[1] + StaticTemplate.route = nil StaticTemplate.groupId = nil - StaticTemplate.CountryID = nil - StaticTemplate.CoalitionID = nil - StaticTemplate.CategoryID = nil + local Static = coalition.addStaticObject( CountryID, StaticTemplate.units[1] ) - local Static = coalition.addStaticObject( CountryID, StaticTemplate ) - - return _DATABASE:AddStatic(Static:getName()) + return _DATABASE:FindStatic(Static:getName()) end return nil @@ -230,24 +220,20 @@ end -- @return #SPAWNSTATIC function SPAWNSTATIC:ReSpawnAt( Coordinate, Heading ) - local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix ) + local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix ) if StaticTemplate then - local CountryID = self.CountryID - - StaticTemplate.x = Coordinate.x - StaticTemplate.y = Coordinate.z + local StaticUnitTemplate = StaticTemplate.units[1] + + StaticUnitTemplate.x = Coordinate.x + StaticUnitTemplate.y = Coordinate.z - StaticTemplate.heading = Heading and ( ( Heading / 180 ) * math.pi ) or StaticTemplate.heading - - StaticTemplate.CountryID = nil - StaticTemplate.CoalitionID = nil - StaticTemplate.CategoryID = nil + StaticUnitTemplate.heading = Heading and ( ( Heading / 180 ) * math.pi ) or StaticTemplate.heading - local Static = coalition.addStaticObject( CountryID, StaticTemplate ) + local Static = coalition.addStaticObject( CountryID, StaticTemplate.units[1] ) - return _DATABASE:AddStatic(Static:getName()) + return _DATABASE:FindStatic(Static:getName()) end return nil @@ -265,6 +251,6 @@ function SPAWNSTATIC:SpawnFromZone( Zone, Heading, NewName ) --R2.1 local Static = self:SpawnFromPointVec2( Zone:GetPointVec2(), Heading, NewName ) - return _DATABASE:AddStatic(Static:getName()) + return Static end diff --git a/Moose Development/Moose/Wrapper/Static.lua b/Moose Development/Moose/Wrapper/Static.lua index a591522af..88d0d18dd 100644 --- a/Moose Development/Moose/Wrapper/Static.lua +++ b/Moose Development/Moose/Wrapper/Static.lua @@ -89,6 +89,59 @@ function STATIC:FindByName( StaticName, RaiseError ) return nil end +--- Destroys the STATIC. +-- @param #STATIC self +-- @param #boolean GenerateEvent (Optional) true if you want to generate a crash or dead event for the static. +-- @return #nil The DCS StaticObject is not existing or alive. +-- @usage +-- -- Air static example: destroy the static Helicopter and generate a S_EVENT_CRASH. +-- Helicopter = STATIC:FindByName( "Helicopter" ) +-- Helicopter:Destroy( true ) +-- +-- @usage +-- -- Ground static example: destroy the static Tank and generate a S_EVENT_DEAD. +-- Tanks = UNIT:FindByName( "Tank" ) +-- Tanks:Destroy( true ) +-- +-- @usage +-- -- Ship static example: destroy the Ship silently. +-- Ship = STATIC:FindByName( "Ship" ) +-- Ship:Destroy() +-- +-- @usage +-- -- Destroy without event generation example. +-- Ship = STATIC:FindByName( "Boat" ) +-- Ship:Destroy( false ) -- Don't generate an event upon destruction. +-- +function STATIC:Destroy( GenerateEvent ) + self:F2( self.ObjectName ) + + local DCSObject = self:GetDCSObject() + + if DCSObject then + + local StaticName = DCSObject:getName() + self:F( { StaticName = StaticName } ) + + if GenerateEvent and GenerateEvent == true then + if self:IsAir() then + self:CreateEventCrash( timer.getTime(), DCSObject ) + else + self:CreateEventDead( timer.getTime(), DCSObject ) + end + elseif GenerateEvent == false then + -- Do nothing! + else + self:CreateEventRemoveUnit( timer.getTime(), DCSObject ) + end + + DCSObject:destroy() + end + + return nil +end + + function STATIC:GetDCSObject() local DCSStatic = StaticObject.getByName( self.StaticName ) From f5a68db7efad8d754a35e403165547145f425695 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sun, 16 Sep 2018 16:22:03 +0200 Subject: [PATCH 365/420] Documented Alarm States --- .../Moose/Wrapper/Controllable.lua | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index ef91a1cff..0689ad712 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -26,13 +26,13 @@ -- * Handle local Controllable Controller. -- * Manage the "state" of the DCS Controllable. -- --- ## CONTROLLABLE constructor +-- # 1) CONTROLLABLE constructor -- -- The CONTROLLABLE class provides the following functions to construct a CONTROLLABLE instance: -- -- * @{#CONTROLLABLE.New}(): Create a CONTROLLABLE instance. -- --- ## CONTROLLABLE Task methods +-- # 2) CONTROLLABLE Task methods -- -- Several controllable task methods are available that help you to prepare tasks. -- These methods return a string consisting of the task description, which can then be given to either a @{Wrapper.Controllable#CONTROLLABLE.PushTask} or @{Wrapper.Controllable#SetTask} method to assign the task to the CONTROLLABLE. @@ -40,7 +40,7 @@ -- Each task description where applicable indicates for which controllable category the task is valid. -- There are 2 main subdivisions of tasks: Assigned tasks and EnRoute tasks. -- --- ### Task assignment +-- ## 2.1) Task assignment -- -- Assigned task methods make the controllable execute the task where the location of the (possible) targets of the task are known before being detected. -- This is different from the EnRoute tasks, where the targets of the task need to be detected before the task can be executed. @@ -71,7 +71,7 @@ -- * @{#CONTROLLABLE.TaskRouteToZone}: (AIR + GROUND) Route the controllable to a given zone. -- * @{#CONTROLLABLE.TaskReturnToBase}: (AIR) Route the controllable to an airbase. -- --- ### EnRoute assignment +-- ## 2.2) EnRoute assignment -- -- EnRoute tasks require the targets of the task need to be detected by the controllable (using its sensors) before the task can be executed: -- @@ -84,7 +84,7 @@ -- * @{#CONTROLLABLE.EnRouteTaskFAC_EngageControllable}: (AIR + GROUND) The task makes the controllable/unit a FAC and lets the FAC to choose the target (enemy ground controllable) as well as other assigned targets. -- * @{#CONTROLLABLE.EnRouteTaskTanker}: (AIR) Aircraft will act as a tanker for friendly units. No parameters. -- --- ### Task preparation +-- ## 2.3) Task preparation -- -- There are certain task methods that allow to tailor the task behaviour: -- @@ -93,7 +93,7 @@ -- * @{#CONTROLLABLE.TaskCondition}: Return a condition section for a controlled task. -- * @{#CONTROLLABLE.TaskControlled}: Return a Controlled Task taking a Task and a TaskCondition. -- --- ### Call a function as a Task +-- ## 2.4) Call a function as a Task -- -- A function can be called which is part of a Task. The method @{#CONTROLLABLE.TaskFunction}() prepares -- a Task that can call a GLOBAL function from within the Controller execution. @@ -102,27 +102,27 @@ -- -- Demonstration Mission: [GRP-502 - Route at waypoint to random point](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/GRP - Group Commands/GRP-502 - Route at waypoint to random point) -- --- ### Tasks at Waypoints +-- ## 2.5) Tasks at Waypoints -- -- Special Task methods are available to set tasks at certain waypoints. -- The method @{#CONTROLLABLE.SetTaskWaypoint}() helps preparing a Route, embedding a Task at the Waypoint of the Route. -- -- This creates a Task element, with an action to call a function as part of a Wrapped Task. -- --- ### Obtain the mission from controllable templates +-- ## 2.6) Obtain the mission from controllable templates -- -- Controllable templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a controllable and assign it to another: -- -- * @{#CONTROLLABLE.TaskMission}: (AIR + GROUND) Return a mission task from a mission template. -- --- ## CONTROLLABLE Command methods +-- # 3) Command methods -- -- Controllable **command methods** prepare the execution of commands using the @{#CONTROLLABLE.SetCommand} method: -- -- * @{#CONTROLLABLE.CommandDoScript}: Do Script command. -- * @{#CONTROLLABLE.CommandSwitchWayPoint}: Perform a switch waypoint command. -- --- ## Routing of Controllables +-- # 4) Routing of Controllables -- -- Different routing methods exist to route GROUPs and UNITs to different locations: -- @@ -130,11 +130,11 @@ -- * @{#CONTROLLABLE.RouteGroundTo}(): Make the GROUND Controllable to drive towards a specific coordinate. -- * @{#CONTROLLABLE.RouteAirTo}(): Make the AIR Controllable to fly towards a specific coordinate. -- --- ## Option methods +-- # 5) Option methods -- -- Controllable **Option methods** change the behaviour of the Controllable while being alive. -- --- ### Rule of Engagement: +-- ## 5.1) Rule of Engagement: -- -- * @{#CONTROLLABLE.OptionROEWeaponFree} -- * @{#CONTROLLABLE.OptionROEOpenFire} @@ -148,7 +148,7 @@ -- * @{#CONTROLLABLE.OptionROEReturnFirePossible} -- * @{#CONTROLLABLE.OptionROEEvadeFirePossible} -- --- ### Rule on thread: +-- ## 5.2) Rule on thread: -- -- * @{#CONTROLLABLE.OptionROTNoReaction} -- * @{#CONTROLLABLE.OptionROTPassiveDefense} @@ -162,6 +162,12 @@ -- * @{#CONTROLLABLE.OptionROTEvadeFirePossible} -- * @{#CONTROLLABLE.OptionROTVerticalPossible} -- +-- ## 5.3) Alarm state: +-- +-- * @{#CONTROLLABLE.OptionAlarmStateAuto} +-- * @{#CONTROLLABLE.OptionAlarmStateGreen} +-- * @{#CONTROLLABLE.OptionAlarmStateRed} +-- -- @field #CONTROLLABLE CONTROLLABLE = { ClassName = "CONTROLLABLE", From 98b427a26fb1028dcf18852f0def9b30a44e1394 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 17 Sep 2018 00:23:04 +0200 Subject: [PATCH 366/420] Warehouse v0.4.9 Added option to force cargo bay limit and cargo asset weight. --- Moose Development/Moose/AI/AI_Cargo.lua | 8 +- .../Moose/Functional/Warehouse.lua | 120 ++++++++++++++---- .../Moose/Wrapper/Positionable.lua | 2 +- 3 files changed, 100 insertions(+), 30 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua index 1d358b735..18155df55 100644 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -142,9 +142,11 @@ function AI_CARGO:New( Carrier, CargoSet ) -- @param #string Event -- @param #string To - for _, CarrierUnit in pairs( Carrier:GetUnits() ) do - CarrierUnit:SetCargoBayWeightLimit() - end + -- FF "Workaround" for not being able to set the cargo bay limit manually for the carrier group. + -- FF Moreover, the carrier group is an input parameter and should not be overwritten here. + --for _, CarrierUnit in pairs( Carrier:GetUnits() ) do + --CarrierUnit:SetCargoBayWeightLimit() + --end self.Transporting = false self.Relocating = false diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index c58bde6fe..07c20d394 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -986,7 +986,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.4.8" +WAREHOUSE.version="0.4.9" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -1090,6 +1090,10 @@ function WAREHOUSE:New(warehouse, alias) -- Add warehouse to database. WAREHOUSE.db.Warehouses[self.uid]=self + ----------------------- + --- FSM Transitions --- + ----------------------- + -- Start State. self:SetStartState("NotReadyYet") @@ -1176,6 +1180,8 @@ function WAREHOUSE:New(warehouse, alias) -- @param Wrapper.Group#GROUP group Group to be added as new asset. -- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1. -- @param #WAREHOUSE.Attribute forceattribute (Optional) Explicitly force a generalized attribute for the asset. This has to be an @{#WAREHOUSE.Attribute}. + -- @param #number forcecargobay (Optional) Explicitly force cargobay weight limit in kg for cargo carriers. This is for each *unit* of the group. + -- @param #number forceweight (Optional) Explicitly force weight in kg of each unit in the group. --- Trigger the FSM event "AddAsset" with a delay. Add a group to the warehouse stock. -- @function [parent=#WAREHOUSE] __AddAsset @@ -1184,6 +1190,8 @@ function WAREHOUSE:New(warehouse, alias) -- @param Wrapper.Group#GROUP group Group to be added as new asset. -- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1. -- @param #WAREHOUSE.Attribute forceattribute (Optional) Explicitly force a generalized attribute for the asset. This has to be an @{#WAREHOUSE.Attribute}. + -- @param #number forcecargobay (Optional) Explicitly force cargobay weight limit in kg for cargo carriers. This is for each *unit* of the group. + -- @param #number forceweight (Optional) Explicitly force weight in kg of each unit in the group. --- Triggers the FSM event "AddRequest". Add a request to the warehouse queue, which is processed when possible. @@ -2178,7 +2186,7 @@ function WAREHOUSE:onafterStatus(From, Event, To) -- Display complete list of stock itmes. if self.Debug then - --self:_DisplayStockItems(self.stock) + self:_DisplayStockItems(self.stock) end -- Call status again in ~30 sec (user choice). @@ -2374,7 +2382,9 @@ end -- @param Wrapper.Group#GROUP group Group or template group to be added to the warehouse stock. -- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1. -- @param #WAREHOUSE.Attribute forceattribute (Optional) Explicitly force a generalized attribute for the asset. This has to be an @{#WAREHOUSE.Attribute}. -function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribute) +-- @param #number forcecargobay (Optional) Explicitly force cargobay weight limit in kg for cargo carriers. This is for each *unit* of the group. +-- @param #number forceweight (Optional) Explicitly force weight in kg of each unit in the group. +function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribute, forcecargobay, forceweight) -- Set default. local n=ngroups or 1 @@ -2439,7 +2449,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu self:_DebugMessage(self.wid..string.format("Warehouse %s: Adding %d NEW assets of group %s to stock.", self.alias, n, tostring(group:GetName())), 5) -- This is a group that is not in the db yet. Add it n times. - local assets=self:_RegisterAsset(group, n, forceattribute) + local assets=self:_RegisterAsset(group, n, forceattribute, forcecargobay, forceweight) -- Add created assets to stock of this warehouse. for _,asset in pairs(assets) do @@ -2468,8 +2478,10 @@ end -- @param Wrapper.Group#GROUP group The group that will be added to the warehouse stock. -- @param #number ngroups Number of groups to be added. -- @param #string forceattribute Forced generalized attribute. +-- @param #number forcecargobay Cargo bay weight limit in kg. +-- @param #number forceweight Weight of units in kg. -- @return #table A table containing all registered assets. -function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute) +function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay, forceweight) self:F({groupname=group:GetName(), ngroups=ngroups, forceattribute=forceattribute}) -- Set default. @@ -2506,15 +2518,30 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute) local Desc=unit:GetDesc() -- Weight. We sum up all units in the group. - local unitweight=Desc.massEmpty + local unitweight=forceweight or Desc.massEmpty if unitweight then weight=weight+unitweight end + local cargomax=0 + local massfuel=Desc.fuelMassMax or 0 + local massempty=Desc.massEmpty or 0 + local massmax=Desc.massMax or 0 + + -- Calcuate cargo bay limit value. + cargomax=massmax-massfuel-massempty + self:T3(self.wid..string.format("Unit name=%s: mass empty=%.1f kg, fuel=%.1f kg, max=%.1f kg ==> cargo=%.1f kg", unit:GetName(), unitweight, massfuel, massmax, cargomax)) + -- Cargo bay size. - local bay=unit:GetCargoBayFreeWeight() + local bay=forcecargobay or unit:GetCargoBayFreeWeight() + + -- Add bay size to table. table.insert(cargobay, bay) + + -- Sum up total bay size. cargobaytot=cargobaytot+bay + + -- Get max bay size. if bay>cargobaymax then cargobaymax=bay end @@ -2912,9 +2939,26 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Empty cargo group set. CargoGroups = SET_CARGO:New() + local function getasset(group) + local _,aid,_=self:_GetIDsFromGroup(group) + self:FindAssetInDB(group) + end + -- Add cargo groups to set. for _i,_group in pairs(_spawngroups:GetSetObjects()) do - CargoGroups:AddCargo(CARGO_GROUP:New(_group, _cargotype,_group:GetName(),_loadradius,_nearradius)) + + -- New cargo group object. + local cargogroup=CARGO_GROUP:New(_group, _cargotype,_group:GetName(),_loadradius,_nearradius) + + -- Find asset belonging to this group. + local asset=self:FindAssetInDB(_group) + if asset then + -- Set weight for this group. + cargogroup:SetWeight(asset.weight) + end + + -- Add group to group set. + CargoGroups:AddCargo(cargogroup) end ------------------------------------------------------------------------------------------------------------------------------------ @@ -3017,20 +3061,31 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Add cargo group set. Pending.transportcargoset=CargoGroups - - -- Create empty tables which will be filled with the cargo groups of each carrier unit. Needed in case a carrier unit dies. - Pending.carriercargo={} - for _,carriergroup in pairs(TransportSet:GetSetObjects()) do - for _,carrierunit in pairs(carriergroup:GetUnits()) do - Pending.carriercargo[carrierunit:GetName()]={} - end - end -- Add request to pending queue. table.insert(self.pending, Pending) -- Delete request from queue. - self:_DeleteQueueItem(Request, self.queue) + self:_DeleteQueueItem(Request, self.queue) + + -- Adjust carrier units. This has to come AFTER the dispatchers have been defined because they set the cargobay free weight! + Pending.carriercargo={} + for _,carriergroup in pairs(TransportSet:GetSetObjects()) do + local asset=self:FindAssetInDB(carriergroup) + for _i,_carrierunit in pairs(carriergroup:GetUnits()) do + local carrierunit=_carrierunit --Wrapper.Unit#UNIT + + -- Create empty tables which will be filled with the cargo groups of each carrier unit. Needed in case a carrier unit dies. + Pending.carriercargo[carrierunit:GetName()]={} + + -- Adjust cargo bay of carrier unit. + local cargobay=asset.cargobay[_i] + carrierunit:SetCargoBayWeightLimit(cargobay) + + -- Debug info. + self:T2(self.wid..string.format("Cargo bay weight limit ofcarrier unit %s: %.1f kg.", carrierunit:GetName(), carrierunit:GetCargoBayFreeWeight())) + end + end ------------------------ -- Create Dispatchers -- @@ -3656,7 +3711,7 @@ function WAREHOUSE:_SpawnAssetRequest(Request) -- Add group to group set and asset list. if _group then _groupset:AddGroup(_group) - table.insert(_assets, _assetitem) + table.insert(_assets, _assetitem) else self:E(self.wid.."ERROR: Cargo asset could not be spawned!") end @@ -4989,7 +5044,8 @@ function WAREHOUSE:_CheckRequestNow(request) if not _enough then local text=string.format("Warehouse %s: Request denied! Not enough (cargo) assets currently available.", self.alias) self:_InfoMessage(text, 5) - + text=string.format("Enough=%s, #_assets=%d, _nassets=%d, request.nasset=%s", tostring(_enough), #_assets,_nassets, tostring(request.nasset)) + env.info(text) return false end @@ -5178,7 +5234,7 @@ function WAREHOUSE:_GetTransportsForAssets(request) --self:E(self.wid..string.format("%s unit %d loads cargo uid=%d: bayempty=%02d, bayloaded = %02d - weight=%02d", transport.templatename, k, asset.uid, transport.cargobay[k], cargobay, asset.weight)) -- Cargo fits into carrier - if delta>0 then + if delta>=0 then -- Reduce remaining cargobay. cargobay=cargobay-asset.weight self:T3(self.wid..string.format("%s unit %d loads cargo uid=%d: bayempty=%02d, bayloaded = %02d - weight=%02d", transport.templatename, k, asset.uid, transport.cargobay[k], cargobay, asset.weight)) @@ -5189,7 +5245,7 @@ function WAREHOUSE:_GetTransportsForAssets(request) -- This transport group is used. used=true else - self:T2(self.wid..string.format("Carrier unit %s too small for cargo asset %s ==> cannot be not used! Cargo bay - asset weight = %d kg", transport.templatename, asset.templatename, delta)) + self:T2(self.wid..string.format("Carrier unit %s too small for cargo asset %s ==> cannot be used! Cargo bay - asset weight = %d kg", transport.templatename, asset.templatename, delta)) end end -- loop over assets @@ -6118,14 +6174,26 @@ end -- @param #table stock Table holding all assets in stock of the warehouse. Each entry is of type @{#WAREHOUSE.Assetitem}. function WAREHOUSE:_DisplayStockItems(stock) - local text=self.wid..string.format("Warehouse %s stock assets:\n", self.airbase:GetName()) - for _,_stock in pairs(stock) do + local text=self.wid..string.format("Warehouse %s stock assets:", self.airbase:GetName()) + for _i,_stock in pairs(stock) do local mystock=_stock --#WAREHOUSE.Assetitem - text=text..string.format("template = %s, category = %d, unittype = %s, attribute = %s\n", mystock.templatename, mystock.category, mystock.unittype, mystock.attribute) + local name=mystock.templatename + local category=mystock.category + local cargobaymax=mystock.cargobaymax + local cargobaytot=mystock.cargobaytot + local nunits=mystock.nunits + local range=mystock.range + local size=mystock.size + local speed=mystock.speedmax + local uid=mystock.uid + local unittype=mystock.unittype + local weight=mystock.weight + local attribute=mystock.attribute + text=text..string.format("\n%02d) uid=%d, name=%s, unittype=%s, category=%d, attribute=%s, nunits=%d, speed=%.1f km/h, range=%.1f km, size=%.1f m, weight=%.1f kg, cargobax max=%.1f kg tot=%.1f kg", + _i, uid, name, unittype, category, attribute, nunits, speed, range/1000, size, weight, cargobaymax, cargobaytot) end - env.info(text) - MESSAGE:New(text, 10):ToAll() + self:T3(text) end --- Fireworks! diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 86c3c4240..7a37c7426 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -1019,7 +1019,7 @@ do -- Cargo -- self.__.CargoBayVolumeLimit = VolumeLimit -- end - --- Get Cargo Bay Weight Limit in kg. + --- Set Cargo Bay Weight Limit in kg. -- @param #POSITIONABLE self -- @param #number WeightLimit function POSITIONABLE:SetCargoBayWeightLimit( WeightLimit ) From f8e1c21a139f54d99e22da9a3164c1986522cb41 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Mon, 17 Sep 2018 11:17:59 +0200 Subject: [PATCH 367/420] Improvement of the C-130 and C-17A --- Moose Development/Moose/AI/AI_Cargo.lua | 1 + Moose Development/Moose/Wrapper/Positionable.lua | 11 +++++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua index 1d358b735..9b0a8e6a1 100644 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -143,6 +143,7 @@ function AI_CARGO:New( Carrier, CargoSet ) -- @param #string To for _, CarrierUnit in pairs( Carrier:GetUnits() ) do + local CarrierUnit = CarrierUnit -- Wrapper.Unit#UNIT CarrierUnit:SetCargoBayWeightLimit() end diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index fc84e5f54..4a2dfc739 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -1022,8 +1022,9 @@ do -- Cargo -- @param #POSITIONABLE self -- @param #number WeightLimit function POSITIONABLE:SetCargoBayWeightLimit( WeightLimit ) + if WeightLimit then - self.__.CargoBayWeightLimit = WeightLimit + self.__.CargoBayWeightLimit = self.__.CargoBayWeightLimit or WeightLimit else -- If weightlimit is not provided, we will calculate it depending on the type of unit. @@ -1031,7 +1032,13 @@ do -- Cargo if self:IsAir() then local Desc = self:GetDesc() self:F({Desc=Desc}) - self.__.CargoBayWeightLimit = Desc.massMax - ( Desc.massEmpty + Desc.fuelMassMax ) + + local Weights = { + ["C-17A"] = 35000, --77519 cannot be used, because it loads way too much apcs and infantry., + ["C-130"] = 22000 --The real value cannot be used, because it loads way too much apcs and infantry., + } + + self.__.CargoBayWeightLimit = Weights[Desc.typeName] or ( Desc.massMax - ( Desc.massEmpty + Desc.fuelMassMax ) ) else local Desc = self:GetDesc() From 6de86ac7080c9cd7d4ba3d19dca6595e8091386a Mon Sep 17 00:00:00 2001 From: FlightControl Date: Mon, 17 Sep 2018 12:35:05 +0200 Subject: [PATCH 368/420] Frank can you retry? --- Moose Development/Moose/Core/SpawnStatic.lua | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Core/SpawnStatic.lua b/Moose Development/Moose/Core/SpawnStatic.lua index 2efce77f7..ac3b776ef 100644 --- a/Moose Development/Moose/Core/SpawnStatic.lua +++ b/Moose Development/Moose/Core/SpawnStatic.lua @@ -77,8 +77,10 @@ SPAWNSTATIC = { --- Creates the main object to spawn a @{Static} defined in the ME. -- @param #SPAWNSTATIC self -- @param #string SpawnTemplatePrefix is the name of the Group in the ME that defines the Template. Each new group will have the name starting with SpawnTemplatePrefix. +-- @param DCS#country.id SpawnCountryID The ID of the country. +-- @param DCS#coalition.side SpawnCoalitionID The ID of the coalition. -- @return #SPAWNSTATIC -function SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, SpawnCountryID ) --R2.1 +function SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, SpawnCountryID, SpawnCoalitionID ) local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC self:F( { SpawnTemplatePrefix } ) @@ -87,7 +89,7 @@ function SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, SpawnCountryID ) --R2.1 self.SpawnTemplatePrefix = SpawnTemplatePrefix self.CountryID = SpawnCountryID or CountryID self.CategoryID = CategoryID - self.CoalitionID = CoalitionID + self.CoalitionID = SpawnCoalitionID or CoalitionID self.SpawnIndex = 0 else error( "SPAWNSTATIC:New: There is no static declared in the mission editor with SpawnTemplatePrefix = '" .. SpawnTemplatePrefix .. "'" ) @@ -102,12 +104,13 @@ end -- @param #SPAWNSTATIC self -- @param #string SpawnTypeName is the name of the type. -- @return #SPAWNSTATIC -function SPAWNSTATIC:NewFromType( SpawnTypeName, SpawnShapeName, SpawnCategory, CountryID ) --R2.1 +function SPAWNSTATIC:NewFromType( SpawnTypeName, SpawnShapeName, SpawnCategory, SpawnCountryID, SpawnCoalitionID ) local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC self:F( { SpawnTypeName } ) self.SpawnTypeName = SpawnTypeName - self.CountryID = CountryID + self.CountryID = SpawnCountryID + self.CoalitionID = SpawnCoalitionID self.SpawnIndex = 0 self:SetEventPriority( 5 ) @@ -135,7 +138,7 @@ function SPAWNSTATIC:Spawn( Heading, NewName ) --R2.3 _DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, CategoryID, CountryID ) - local Static = coalition.addStaticObject( CountryID, StaticTemplate.units[1] ) + local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] ) self.SpawnIndex = self.SpawnIndex + 1 @@ -176,7 +179,7 @@ function SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1 self:F({StaticTemplate = StaticTemplate}) - local Static = coalition.addStaticObject( CountryID, StaticTemplate.units[1] ) + local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] ) self.SpawnIndex = self.SpawnIndex + 1 @@ -204,7 +207,7 @@ function SPAWNSTATIC:ReSpawn() StaticTemplate.route = nil StaticTemplate.groupId = nil - local Static = coalition.addStaticObject( CountryID, StaticTemplate.units[1] ) + local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] ) return _DATABASE:FindStatic(Static:getName()) end @@ -231,7 +234,7 @@ function SPAWNSTATIC:ReSpawnAt( Coordinate, Heading ) StaticUnitTemplate.heading = Heading and ( ( Heading / 180 ) * math.pi ) or StaticTemplate.heading - local Static = coalition.addStaticObject( CountryID, StaticTemplate.units[1] ) + local Static = coalition.addStaticObject( self.CountryID or CountryID, StaticTemplate.units[1] ) return _DATABASE:FindStatic(Static:getName()) end From f4f942d8d4c00cb3977ea30aa314f8d02d6e34c2 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 17 Sep 2018 14:04:30 +0200 Subject: [PATCH 369/420] Warehouse little updates static countryid --- Moose Development/Moose/Core/SpawnStatic.lua | 4 +--- Moose Development/Moose/Wrapper/Static.lua | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Core/SpawnStatic.lua b/Moose Development/Moose/Core/SpawnStatic.lua index 5fa87ddb6..d1b9cf371 100644 --- a/Moose Development/Moose/Core/SpawnStatic.lua +++ b/Moose Development/Moose/Core/SpawnStatic.lua @@ -200,10 +200,8 @@ function SPAWNSTATIC:ReSpawn(countryid) if StaticTemplate then - local CountryID = countryid or self.CountryID - local CountryName = _DATABASE.COUNTRY_NAME[CountryID] + --local CountryID = countryid or (self.CountryID or CountryID) - StaticTemplate.units = nil StaticTemplate.route = nil StaticTemplate.groupId = nil diff --git a/Moose Development/Moose/Wrapper/Static.lua b/Moose Development/Moose/Wrapper/Static.lua index fd20f3328..e624dc021 100644 --- a/Moose Development/Moose/Wrapper/Static.lua +++ b/Moose Development/Moose/Wrapper/Static.lua @@ -197,9 +197,9 @@ end -- @param DCS#country.id countryid The country ID used for spawning the new static. function STATIC:ReSpawn(countryid) - local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName ) + local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, countryid ) - SpawnStatic:ReSpawn(countryid) + SpawnStatic:ReSpawn() end From 0275ba8b6b62179159928874dce26e63efa74f98 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Mon, 17 Sep 2018 14:04:55 +0200 Subject: [PATCH 370/420] warehouse little changes --- .../Moose/Functional/Warehouse.lua | 87 +++++++++++-------- 1 file changed, 51 insertions(+), 36 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 07c20d394..000347120 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -164,9 +164,11 @@ -- * *Prio*: (Optional) A number between 1 (high) and 100 (low) describing the priority of the request. Request with high priority are processed first. Default is 50, i.e. medium priority. -- * *Assignment*: (Optional) A free to choose string describing the assignment. For self requests, this can be used to assign the spawned groups to specific tasks. -- +-- ## Requesting by Generalized Attribute +-- -- For example: -- --- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 5, WAREHOUSE.TransportType.APC, 2, 20) +-- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 5, WAREHOUSE.TransportType.APC, 2) -- -- Here, warehouse Kobuleti requests 5 infantry groups from warehouse Batumi. These "cargo" assets should be transported from Batumi to Kobuleti by 2 APCS. -- Note that the warehouse at Batumi needs to have at least five infantry groups and two APC groups in their stock if the request can be processed. @@ -184,7 +186,7 @@ -- Here, Kobuleti requests a specific unit type, in particular two groups of A-10Cs. Note that the spelling is important as it must exacly be the same as -- what one get's when using the DCS unit type. -- --- ## Requesting a Specifc Group +-- ## Requesting a Specific Group -- -- An even more specific request would be: -- @@ -192,7 +194,7 @@ -- -- In this case three groups named "Group Name as in ME" are requested. So this explicitly request the groups named like that in the Mission Editor. -- --- ## Requesting a general category +-- ## Requesting a General Category -- -- On the other hand, very general unspecifc requests can be made as -- @@ -200,6 +202,8 @@ -- -- Here, Kubuleti requests 10 ground groups and does not care which ones. This could be a mix of infantry, APCs, trucks etc. -- +-- **Note** that these general requests should be made with *great care* due to the fact, that depending on what a warehouse has in stock a lot of different unit types can be spawned. +-- -- # Employing Assets -- -- Assets in the warehouse' stock can used for user defined tasks realtively easily. They can be spawned into the game by a "self request", i.e. the warehouse @@ -1655,8 +1659,13 @@ function WAREHOUSE:AddOffRoadPath(remotewarehouse, group, oneway) -- Create new path from template group waypoints. local path=self:_NewLane(group, startcoord, finalcoord) + if path==nil then + self:E(self.wid.."ERROR: Offroad path could not be added. Group present in ME?") + return + end + -- Debug info. Marks along path. - if self.Debug then + if path and self.Debug then for i=1,#path do local coord=path[i] --Core.Point#COORDINATE local text=string.format("Off road path from %s to %s. Point %d.", self.alias, remotewarehouse.alias, i) @@ -1691,40 +1700,46 @@ end -- @return #table Table with route points. function WAREHOUSE:_NewLane(group, startcoord, finalcoord) - -- Get route from template. - local lanepoints=group:GetTemplateRoutePoints() - - -- First and last waypoints - local laneF=lanepoints[1] - local laneL=lanepoints[#lanepoints] - - -- Get corresponding coordinates. - local coordF=COORDINATE:New(laneF.x, 0, laneF.y) - local coordL=COORDINATE:New(laneL.x, 0, laneL.y) - - -- Figure out which point is closer to the port of this warehouse. - local distF=startcoord:Get2DDistance(coordF) - local distL=startcoord:Get2DDistance(coordL) - - -- Add the lane. Need to take care of the wrong "direction". - local lane={} - if distF Date: Tue, 18 Sep 2018 00:00:19 +0200 Subject: [PATCH 371/420] Warehouse v0.5.0 Improved documentation including examples. Fixed destroy state. Added pictures. --- .../Moose/Functional/Warehouse.lua | 636 ++++++++++++++---- 1 file changed, 488 insertions(+), 148 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 000347120..2cd00d6a4 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -9,9 +9,11 @@ -- * Holds (virtual) assests in stock. -- * Manages requests of assets from other warehouses. -- * Realistic transportation of assets between warehouses. --- * Different means of automatic transportation (planes, helicopters, APCs, selfpropelled). +-- * Different means of automatic transportation (planes, helicopters, APCs, self propelled). -- * Strategic components such as capturing, defending and destroying warehouses and their associated infrastructure. --- * Can be coupled to other MOOSE classes. +-- * Can be easily interfaced to other MOOSE classes. +-- +-- Please not that his class is work in progress and in an **alpha** stage. -- -- === -- @@ -54,7 +56,7 @@ -- -- === -- --- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_Main.jpg) +-- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_Main.png) -- -- # The Warehouse Concept -- @@ -95,9 +97,9 @@ -- Furthermore, ground assets can be transferred between warehouses by transport units. These are APCs, helicopters and airplanes. The transportation process is modelled -- in a realistic way by using the corresponding cargo dispatcher classes, i.e. -- --- * @{AI.AI_Cargo_Dispatcher_APC#AI_DISPATCHER_APC}, --- * @{AI.AI_Cargo_Dispatcher_Helicopter#AI_DISPATCHER_HELICOPTER} and --- * @{AI.AI_Cargo_Dispatcher_Airplane#AI_DISPATCHER_AIRPLANE}. +-- * @{AI.AI_Cargo_Dispatcher_APC#AI_DISPATCHER_APC} +-- * @{AI.AI_Cargo_Dispatcher_Helicopter#AI_DISPATCHER_HELICOPTER} +-- * @{AI.AI_Cargo_Dispatcher_Airplane#AI_DISPATCHER_AIRPLANE} -- -- Depending on which cargo dispatcher is used (ground or airbore), similar considerations like in the self propelled case are necessary. Howver, note that -- the dispatchers as of yet cannot use user defined off road paths for example since they are classes of their own and use a different routing logic. @@ -130,7 +132,7 @@ -- -- # Adding Assets -- --- Assets can be added to the warehouse stock by using the @{#WAREHOUSE.AddAsset}(*group*, *ngroups*, *forceattribute*) function. The parameter *group* has to be a MOOSE @{Wrapper.Group#GROUP}. +-- Assets can be added to the warehouse stock by using the @{#WAREHOUSE.AddAsset}(*group*, *ngroups*, *forceattribute*, *forcecargobay*, *forceweight*) function. The parameter *group* has to be a MOOSE @{Wrapper.Group#GROUP}. -- The parameter *ngroups* specifies how many clones of this group are added to the stock. -- -- @@ -140,11 +142,36 @@ -- This will add five infantry groups to the warehouse stock. Note that the group will normally be a late activated template group, -- which was defined in the mission editor. But you can also add other groups which are already spawned and present in the mission. -- --- You can add assets with a delay by using the @{#WAREHOUSE.__AddAsset}(*delay*, *group*, *ngroups*, *foceattribute*), where *delay* is the delay in seconds before the asset is added. +-- You can add assets with a delay by using the @{#WAREHOUSE.__AddAsset}(*delay*, *group*, *ngroups*, *foceattribute*, *forcecargobay*, *forceweight*), where *delay* +-- is the delay in seconds before the asset is added. +-- +-- In game, the warehouse will get a mark which is regularly updated and showing the currently available assets in stock. +-- +-- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_Stock-Marker.png) +-- +-- ## Options for Fine Tuning -- -- By default, the generalized attribute of the asset is determined automatically from the DCS descriptor attributes. However, this might not always result in the desired outcome. -- Therefore, it is possible, to force a generalized attribute for the asset with the third optional parameter *forceattribute*, which is of type @{#WAREHOUSE.Attribute}. -- +-- ### Setting the Generalized Attibute +-- For example, a UH-1H Huey has in DCS the attibute of an attack helicopter. But of course, it can also transport cargo. If you want to use it for transportation, you can specify this +-- manually when the asset is added +-- +-- warehouse.Batumi:AddAsset("Huey", 5, WAREHOUSE.Attribute.AIR_TRANSPORTHELO) +-- +-- ### Setting the Cargo Bay Weight Limit +-- You can also ajust the cargo bay weight limit, in case it is not calculated correctly automatically. For example, the cargo bay of a C-17A is much smaller in DCS than that of a C-130, which is +-- unrealistic. This can be corrected by the *forcecargobay* parmeter which is here set to 77,000 kg +-- +-- warehouse.Batumi:AddAsset("C-17A", nil, 77000) +-- +-- ### Setting the Weight +-- In the current version of DCS a mortar unit has a weight of 5 tons. This confuses the transporter logic, because it appears to be too have for, e.g. all APCs. You can manually adjust the weight +-- by the *forceweight* parameter and set it to 210 kg for each unit in the group +-- +-- warehouse.Batumi:AddAsset("Mortar Alpha", nil, nil, nil, 210) +-- -- === -- -- # Requesting Assets @@ -168,7 +195,7 @@ -- -- For example: -- --- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 5, WAREHOUSE.TransportType.APC, 2) +-- warehouse.Batumi:AddRequest(warehouse.Kobuleti, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 5, WAREHOUSE.TransportType.APC, 2) -- -- Here, warehouse Kobuleti requests 5 infantry groups from warehouse Batumi. These "cargo" assets should be transported from Batumi to Kobuleti by 2 APCS. -- Note that the warehouse at Batumi needs to have at least five infantry groups and two APC groups in their stock if the request can be processed. @@ -176,12 +203,13 @@ -- transport assets are available. -- -- Also note that the above request is for five infantry groups. So any group in stock that has the generalized attribute "INFANTRY" can be selected. +-- -- -- ## Requesting a Specific Unit Type -- -- A more specific request could look like: -- --- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.UNITTYPE, "A-10C", 2) +-- warehouse.Batumi:AddRequest(warehouse.Kobuleti, WAREHOUSE.Descriptor.UNITTYPE, "A-10C", 2) -- -- Here, Kobuleti requests a specific unit type, in particular two groups of A-10Cs. Note that the spelling is important as it must exacly be the same as -- what one get's when using the DCS unit type. @@ -190,15 +218,15 @@ -- -- An even more specific request would be: -- --- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.TEMPLATENAME, "Group Name as in ME", 3) +-- warehouse.Batumi:AddRequest(warehouse.Kobuleti, WAREHOUSE.Descriptor.TEMPLATENAME, "Group Name as in ME", 3) -- --- In this case three groups named "Group Name as in ME" are requested. So this explicitly request the groups named like that in the Mission Editor. +-- In this case three groups named "Group Name as in ME" are requested. This explicitly request the groups named like that in the Mission Editor. -- -- ## Requesting a General Category -- -- On the other hand, very general unspecifc requests can be made as -- --- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.CATEGORY, Group.Category.Ground, 10) +-- warehouse.Batumi:AddRequest(warehouse.Kobuleti, WAREHOUSE.Descriptor.CATEGORY, Group.Category.Ground, 10) -- -- Here, Kubuleti requests 10 ground groups and does not care which ones. This could be a mix of infantry, APCs, trucks etc. -- @@ -206,10 +234,10 @@ -- -- # Employing Assets -- --- Assets in the warehouse' stock can used for user defined tasks realtively easily. They can be spawned into the game by a "self request", i.e. the warehouse +-- Assets in the warehouse' stock can used for user defined tasks realtively easily. They can be spawned into the game by a "*self request*", i.e. the warehouse -- requests the assets from itself: -- --- warehouseBatumi:AddRequest(warehouseBatumi, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 5) +-- warehouse.Batumi:AddRequest(warehouse.Batumi, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 5) -- -- This would simply spawn five infantry groups in the spawn zone of the Batumi warehouse if/when they are available. -- @@ -266,7 +294,8 @@ -- By default, the closest point on road to the center of the spawn zone is choses as road connection automatically. But only, if distance between the spawn zone -- and the road connection is less than 3 km. -- --- The user can set the road connection manually with the @{#WAREHOUSE.SetRoadConnection} function. +-- The user can set the road connection manually with the @{#WAREHOUSE.SetRoadConnection} function. This is only functional for self propelled assets at the moment +-- and not if using the AI dispatcher classes since these have a different logic to find the route. -- -- ## Off Road Connections -- @@ -277,6 +306,8 @@ -- The parameter *group* is a late activated template group. The waypoints of this group are used to define the path between the two warehouses. -- By default, the reverse paths is automatically added to get *from* the remote warehouse to this warehouse unless the parameter *oneway* is set to *true*. -- +-- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_Off-RoadPaths.png) +-- -- **Note** that if an off road connection is defined between two warehouses this becomes the default path, i.e. even if there is a path *on road* possible -- this will not be used. -- @@ -437,6 +468,7 @@ -- -- -- Define Warehouses. -- local warehouse={} +-- -- Blue warehouses -- warehouse.Senaki = WAREHOUSE:New(STATIC:FindByName("Warehouse Senaki"), "Senaki") --Functional.Warehouse#WAREHOUSE -- warehouse.Batumi = WAREHOUSE:New(STATIC:FindByName("Warehouse Batumi"), "Batumi") --Functional.Warehouse#WAREHOUSE -- warehouse.Kobuleti = WAREHOUSE:New(STATIC:FindByName("Warehouse Kobuleti"), "Kobuleti") --Functional.Warehouse#WAREHOUSE @@ -444,6 +476,11 @@ -- warehouse.Berlin = WAREHOUSE:New(STATIC:FindByName("Warehouse Berlin"), "Berlin") --Functional.Warehouse#WAREHOUSE -- warehouse.London = WAREHOUSE:New(STATIC:FindByName("Warehouse London"), "London") --Functional.Warehouse#WAREHOUSE -- warehouse.Stennis = WAREHOUSE:New(STATIC:FindByName("Warehouse Stennis"), "Stennis") --Functional.Warehouse#WAREHOUSE +-- warehouse.Pampa = WAREHOUSE:New(STATIC:FindByName("Warehouse Pampa"), "Pampa") --Functional.Warehouse#WAREHOUSE +-- -- Red warehouses +-- warehouse.Sukhumi = WAREHOUSE:New(STATIC:FindByName("Warehouse Sukhumi"), "Sukhumi") --Functional.Warehouse#WAREHOUSE +-- warehouse.Gudauta = WAREHOUSE:New(STATIC:FindByName("Warehouse Gudauta"), "Gudauta") --Functional.Warehouse#WAREHOUSE +-- warehouse.Sochi = WAREHOUSE:New(STATIC:FindByName("Warehouse Sochi"), "Sochi") --Functional.Warehouse#WAREHOUSE -- -- Remarks: -- @@ -452,6 +489,10 @@ -- -- **NOTE** that all examples below need this bit or code at the beginning - or at least the warehouses which are used. -- +-- The example mission is based on the same template mission, which has defined a lot of airborne, ground and naval assets as templates. Only few of those are used here. +-- +-- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_Assets.png) +-- -- ## Example 1: Self Request -- -- Ground troops are taken from the Batumi warehouse stock and spawned in its spawn zone. After a short delay, they are added back to the warehouse stock. @@ -477,7 +518,6 @@ -- -- -- Gree smoke on spawned group. -- group:SmokeGreen() --- group:FlareRed() -- -- -- Put asset back to stock after 10 seconds. -- warehouse.Batumi:__AddAsset(10, group) @@ -499,9 +539,9 @@ -- -- Start Warehouse at Batumi. -- warehouse.Batumi:Start() -- --- -- Add 20 infantry groups as assets at Batumi. +-- -- Add 20 infantry groups and ten APCs as assets at Batumi. -- warehouse.Batumi:AddAsset("Infantry Platoon Alpha", 20) --- warehouse.Batumi:AddAsset("TPz Fuchs", 5) +-- warehouse.Batumi:AddAsset("TPz Fuchs", 10) -- -- -- Start Warehouse Berlin. -- warehouse.Berlin:Start() @@ -515,33 +555,35 @@ -- -- ## Example 3: Self Propelled Airborne Assets -- --- Warehouse Senaki receives requests from Kutaisi for one Yak-52s and from FARP London for three Hueys. --- Assets are spawned in Senaki and make their way to the requesting warehouses. +-- Warehouse Senaki receives a high priority request from Kutaisi for one Yak-52s. At the same time, Kobuleti requests half of +-- all available Yak-52s. Request from Kutaisi is first executed and then Kobuleti gets half of the remaining assets. +-- Additionally, London requests one third of all available UH-1H Hueys from Senaki. -- Once the units have arrived they are added to the stock of the receiving warehouses and can be used for further assignments. -- --- -- Start sending warehouse. +-- -- Start warehouses -- warehouse.Senaki:Start() --- --- -- Add assets. --- warehouse.Senaki:AddAsset("Yak-52", 10) --- warehouse.Senaki:AddAsset("Huey", 10) --- --- -- Start receiving warehouses -- warehouse.Kutaisi:Start() +-- warehouse.Kobuleti:Start() -- warehouse.London:Start() -- --- -- Kusaisi requests one Yak-52 form Senaki. FARP London requests three UH-1H Huys from Senaki. --- warehouse.Senaki:AddRequest(warehouse.Kutaisi, WAREHOUSE.Descriptor.TEMPLATENAME, "Yak-52", 1) --- warehouse.Senaki:AddRequest(warehouse.London, WAREHOUSE.Descriptor.TEMPLATENAME, "Huey", 3) +-- -- Add assets to Senaki warehouse. +-- warehouse.Senaki:AddAsset("Yak-52", 10) +-- warehouse.Senaki:AddAsset("Huey", 6) +-- +-- -- Kusaisi requests 3 Yak-52 form Senaki while Kobuleti wants all the rest. +-- warehouse.Senaki:AddRequest(warehouse.Kutaisi, WAREHOUSE.Descriptor.TEMPLATENAME, "Yak-52", 1, nil, nil, 10) +-- warehouse.Senaki:AddRequest(warehouse.Kobuleti, WAREHOUSE.Descriptor.TEMPLATENAME, "Yak-52", WAREHOUSE.Quantity.HALF, nil, nil, 70) +-- +-- -- FARP London wants 1/3 of the six available Hueys. +-- warehouse.Senaki:AddRequest(warehouse.London, WAREHOUSE.Descriptor.TEMPLATENAME, "Huey", WAREHOUSE.Quantity.THIRD) -- -- ## Example 4: Transport of Assets by APCs -- --- Warehouse at FARP Berlin requests three infantry groups from Batumi. These assets shall be transported using one APC. --- Infantry and APC are spawned in the spawn zone at Batumi. The APC picks up two of the three infantry groups and --- drives them to Berlin. There, they unboard and walk to the warehouse where they will be added to the stock. --- Meanwhile the APC drives back and picks up the last infantry group and also brings it to Batumi. --- The APC will then return to Batumi and be added back to the stock of the Batumi warehouse. --- The reason that the APC has to drive twice, it that can only up to ten soldiers. +-- Warehouse at FARP Berlin requests five infantry groups from Batumi. These assets shall be transported using two APC groups. +-- Infantry and APC are spawned in the spawn zone at Batumi. The APCs have a cargo bay large enough to pick up four of the +-- five infantry groups in the first run and will bring them to Berlin. There, they unboard and walk to the warehouse where they will be added to the stock. +-- Meanwhile the APCs go back to Batumi and one will pick up the last remaining soldiers. +-- Once the APCs have completed their mission, they return to Batumi and are added back to stock. -- -- -- Start Warehouse at Batumi. -- warehouse.Batumi:Start() @@ -553,15 +595,17 @@ -- warehouse.Batumi:AddAsset("Infantry Platoon Alpha", 20) -- warehouse.Batumi:AddAsset("TPz Fuchs", 5) -- --- -- Warehouse Berlin requests 3 infantry groups from warehouse Batumi using 1 APC for transport. --- warehouse.Batumi:AddRequest(warehouse.Berlin, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 3, WAREHOUSE.TransportType.APC, 1) +-- -- Warehouse Berlin requests 5 infantry groups from warehouse Batumi using 2 APCs for transport. +-- warehouse.Batumi:AddRequest(warehouse.Berlin, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 5, WAREHOUSE.TransportType.APC, 2) -- --## Example 5: Transport of Assets by Helicopters -- --- Warehouse at FARP Berlin requests 10 infantry groups from Batumi. They shall be transported by one helicopter. +-- Warehouse at FARP Berlin requests five infantry groups from Batumi. They shall be transported by all available transport helicopters. -- Note that the UH-1H Huey in DCS is an attack and not a transport helo. So the warehouse logic would be default also -- register it as an @{#WAREHOUSE.Attribute.AIR_ATTACKHELICOPTER}. In order to use it as a transport we need to force -- it to be added as transport helo. +-- Also note that even though all (here five) helos are requested, only two of them are employed because this number is sufficient to +-- transport all requested assets in one go. -- -- -- Start Warehouses. -- warehouse.Batumi:Start() @@ -570,26 +614,27 @@ -- -- Add 20 infantry groups as assets at Batumi. -- warehouse.Batumi:AddAsset("Infantry Platoon Alpha", 20) -- --- -- Add five Hueys for transport. Note that the Huey in DCS is an attack and not a transport helo. So we force the attribute! +-- -- Add five Hueys for transport. Note that a Huey in DCS is an attack and not a transport helo. So we force this attribute! -- warehouse.Batumi:AddAsset("Huey", 5, WAREHOUSE.Attribute.AIR_TRANSPORTHELO) -- --- -- Warehouse Berlin requests 10 infantry groups from warehouse Batumi using one huey for transport. --- warehouse.Batumi:AddRequest(warehouse.Berlin, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 10, WAREHOUSE.TransportType.HELICOPTER, 1) +-- -- Warehouse Berlin requests 5 infantry groups from warehouse Batumi using all available helos for transport. +-- warehouse.Batumi:AddRequest(warehouse.Berlin, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 5, WAREHOUSE.TransportType.HELICOPTER, WAREHOUSE.Quantity.ALL) -- --## Example 6: Transport of Assets by Airplanes -- --- Kutaisi requests 20 infantry groups from Senaki. These assets will be loaded into one C-130 cargo plane. +-- Warehoues Kobuleti requests all (three) APCs from Batumi using one airplane for transport. +-- The available C-130 is able to carry one APC at a time. So it has to commute three times between Batumi and Kobuleti to deliver all requested cargo assets. +-- Once the cargo is delivered, the C-130 transport returns to Batumi and is added back to stock. -- --- -- Start Warehouses. --- warehouse.Senaki:Start() --- warehouse.Kutaisi:Start() +-- -- Start warehouses. +-- warehouse.Batumi:Start() +-- warehouse.Kobuleti:Start() -- --- -- Add 20 infantry groups and 5 C-130 transport planes as assets to Senaki warehouse. --- warehouse.Senaki:AddAsset("Infantry Platoon Alpha", 20) --- warehouse.Senaki:AddAsset("C-130", 5) --- --- -- Warehouse Berlin requests 10 infantry groups from warehouse Batumi using 3 APCs for transport. --- warehouse.Senaki:AddRequest(warehouse.Kutaisi, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 20, WAREHOUSE.TransportType.AIRPLANE, 1) +-- -- Add assets to Batumi warehouse. +-- warehouse.Batumi:AddAsset("C-130", 1) +-- warehouse.Batumi:AddAsset("TPz Fuchs", 3) +-- +-- warehouse.Batumi:AddRequest(warehouse.Kobuleti, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_APC, WAREHOUSE.Quantity.ALL, WAREHOUSE.TransportType.AIRPLANE) -- -- ## Example 7: Capturing Airbase and Warehouse -- @@ -610,45 +655,45 @@ -- -- Here, we simply activate a blue external unit which drives to the warehouse, destroyes the red intruder and re-captures our warehouse. -- --- -- Start warehouse. +-- -- Start warehouses. -- warehouse.Senaki:Start() +-- warehouse.Sukhumi:Start() -- -- -- Add some assets. -- warehouse.Senaki:AddAsset("TPz Fuchs", 5) -- warehouse.Senaki:AddAsset("Infantry Platoon Alpha", 10) -- warehouse.Senaki:AddAsset("F/A-18C 2ship", 10) -- --- -- Auto defence! When enabled, all ground troops of the warehouse are spawned automatically to defend the warehouse. --- -- warehouse.Senaki:SetAutoDefenceOn() --- --- -- Red BMP trying to capture the airfield and later the warehouse. --- local red1=GROUP:FindByName("Red BMP-80 Senaki") --- red1:Activate() --- --- -- The red BMP first drives to the airbase which gets captured and changes from blue to red. So the warehouse loses its airbase. --- function warehouse.Senaki:OnAfterAirbaseCaptured(From,Event,To,Coalition) --- -- This request should not be processed since the warehouse has lost its airbase. In fact it is deleted from the queue. +-- -- Enable auto defence, i.e. spawn all group troups into the spawn zone. +-- --warehouse.Senaki:SetAutoDefenceOn() +-- +-- -- Activate Red BMP trying to capture the airfield and the warehouse. +-- local red1=GROUP:FindByName("Red BMP-80 Senaki"):Activate() +-- +-- -- The red BMP first drives to the airbase which gets captured and changes from blue to red. +-- -- This triggers the "AirbaseCaptured" event where you can hook in and do things. +-- function warehouse.Senaki:OnAfterAirbaseCaptured(From, Event, To, Coalition) +-- -- This request cannot be processed since the warehouse has lost its airbase. In fact it is deleted from the queue. -- warehouse.Senaki:AddRequest(warehouse.Senaki,WAREHOUSE.Descriptor.CATEGORY, Group.Category.AIRPLANE, 1) -- end -- --- -- Enemy has entered the warehouse zone. This triggers the "Attacked" event. --- function warehouse.Senaki:OnAfterAttacked(From,Event,To,Coalition,Country) --- MESSAGE:New(string.format("Warehouse %s: We are under attack!", self.alias), 30):ToCoalition(self:GetCoalition()) --- self:GetCoordinate():SmokeRed() --- end --- --- -- Now the red BMP also captured the warehouse. So the warehouse and the airbase are both red and planes can be spawned again. --- function warehouse.Senaki:OnAfterCaptured(From,Event,To,Coalition,Country) +-- -- Now the red BMP also captures the warehouse. This triggers the "Captured" event where you can hook in. +-- -- So now the warehouse and the airbase are both red and aircraft can be spawned again. +-- function warehouse.Senaki:OnAfterCaptured(From, Event, To, Coalition, Country) -- -- These units will be spawned as red units because the warehouse has just been captured. --- warehouse.Senaki:AddRequest(warehouse.Senaki,WAREHOUSE.Descriptor.CATEGORY, Group.Category.AIRPLANE, 1) +-- if Coalition==coalition.side.RED then +-- -- Sukhumi tries to "steals" three F/A-18 from Senaki and brings them to Sukhumi. +-- -- Well, actually the aircraft wont make it because blue1 will kill it on the taxi way leaving a blood bath. But that's life! +-- warehouse.Senaki:AddRequest(warehouse.Sukhumi, WAREHOUSE.Descriptor.CATEGORY, Group.Category.AIRPLANE, 3) +-- end -- --- -- Activate Blue Humvee to recapture the warehouse. --- local blue1=GROUP:FindByName("blue1") --- blue1:Activate() +-- -- Activate a blue vehicle to re-capture the warehouse. It will drive to the warehouse zone and kill the red intruder. +-- local blue1=GROUP:FindByName("blue1"):Activate() -- end -- -- ## Example 8: Destroying a Warehouse -- +-- FARP Berlin requests a Huey from Batumi warehouse. This helo is deployed and will be delivered. -- After 30 seconds into the mission we create and (artificial) big explosion - or a terrorist attack if you like - which completely destroys the -- the warehouse at Batumi. All assets are gone and requests cannot be processed anymore. -- @@ -660,47 +705,103 @@ -- warehouse.Batumi:AddAsset("Huey", 5, WAREHOUSE.Attribute.AIR_TRANSPORTHELO) -- warehouse.Berlin:AddAsset("Huey", 5, WAREHOUSE.Attribute.AIR_TRANSPORTHELO) -- --- -- Big explosion at the warehouse. It has a very nice damage model by the way :) +-- -- Big explosion at the warehose. It has a very nice damage model by the way :) -- local function DestroyWarehouse() --- warehouse.Batumi.warehouse:GetCoordinate():Explosion(9999) +-- warehouse.Batumi.warehouse:GetCoordinate():Explosion(999) -- end --- --- -- Create an explosion at the warehouse after 30 sec. -- SCHEDULER:New(nil, DestroyWarehouse, {}, 30) -- --- -- These requests should not be processed any more since the warehouse is destroyed. +-- -- First request is okay since warehouse is still alive. +-- warehouse.Batumi:AddRequest(warehouse.Berlin, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.AIR_TRANSPORTHELO, 1) +-- +-- -- These requests should both not be processed any more since the warehouse at Batumi is destroyed. -- warehouse.Batumi:__AddRequest(35, warehouse.Berlin, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.AIR_TRANSPORTHELO, 1) -- warehouse.Berlin:__AddRequest(40, warehouse.Batumi, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.AIR_TRANSPORTHELO, 1) -- -- ## Example 9: Self Propelled Naval Assets -- --- Kobuleti requests a war ship from Batumi. Both warehouses need to have a port, which we define by two polygon zones at a place --- in the sea closest to the warehouses. Also a shipping lane between the two warehouses needs to be defined manually. --- With this infrastructure it is possible to exachange naval assets between warehouses. +-- Kobuleti requests all naval assets from Batumi. +-- However, before naval assets can be exchanged, both warehouses need a port and at least one shipping lane defined by the user. +-- See the @{#WAREHOUSE.SetPortZone}() and @{#WAREHOUSE.AddShippingLane}() functions. +-- We do not want to spawn them all at once, because this will probably be a disaster +-- in the port zone. Therefore, each ship is spawned with a delay of five minutes. +-- +-- Batumi has quite a selection of different ships (for testing). +-- +-- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_Naval_Assets.png) -- -- -- Start warehouses. -- warehouse.Batumi:Start() -- warehouse.Kobuleti:Start() -- --- -- Define ports and shipping lanes. --- warehouse.Batumi:SetPortZone(ZONE_POLYGON:NewFromGroupName("Warehouse Batumi Port", "Warehouse Batumi Port")) --- warehouse.Kobuleti:SetPortZone(ZONE_POLYGON:NewFromGroupName("Warehouse Kobuleti Port", "Warehouse Kobuleti Port")) --- warehouse.Batumi:AddShippingLane(warehouse.Kobuleti, GROUP:FindByName("Warehouse Batumi-Kobuleti Shipping Lane")) +-- -- Define ports. These are polygon zones created by the waypoints of late activated units. +-- warehouse.Batumi:SetPortZone(ZONE_POLYGON:NewFromGroupName("Warehouse Batumi Port Zone", "Warehouse Batumi Port Zone")) +-- warehouse.Kobuleti:SetPortZone(ZONE_POLYGON:NewFromGroupName("Warehouse Kobuleti Port Zone", "Warehouse Kobuleti Port Zone")) -- --- -- Add five USS Normandy naval assets. --- warehouse.Batumi:AddAsset("Normandy", 5) +-- -- Shipping lane. Again, the waypoints of late activated units are taken as points defining the shipping lane. +-- -- Some units will take lane 1 while others will take lane two. But both lead from Batumi to Kobuleti port. +-- warehouse.Batumi:AddShippingLane(warehouse.Kobuleti, GROUP:FindByName("Warehouse Batumi-Kobuleti Shipping Lane 1")) +-- warehouse.Batumi:AddShippingLane(warehouse.Kobuleti, GROUP:FindByName("Warehouse Batumi-Kobuleti Shipping Lane 2")) -- --- -- Kobuleti requests a war ship from Batumi. --- warehouse.Batumi:AddRequest(warehouse.Kobuleti, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.NAVAL_WARSHIP) +-- -- Large selection of available naval units in DCS. +-- warehouse.Batumi:AddAsset("Speedboat") +-- warehouse.Batumi:AddAsset("Perry") +-- warehouse.Batumi:AddAsset("Normandy") +-- warehouse.Batumi:AddAsset("Stennis") +-- warehouse.Batumi:AddAsset("Carl Vinson") +-- warehouse.Batumi:AddAsset("Tarawa") +-- warehouse.Batumi:AddAsset("SSK 877") +-- warehouse.Batumi:AddAsset("SSK 641B") +-- warehouse.Batumi:AddAsset("Grisha") +-- warehouse.Batumi:AddAsset("Molniya") +-- warehouse.Batumi:AddAsset("Neustrashimy") +-- warehouse.Batumi:AddAsset("Rezky") +-- warehouse.Batumi:AddAsset("Moskva") +-- warehouse.Batumi:AddAsset("Pyotr Velikiy") +-- warehouse.Batumi:AddAsset("Kuznetsov") +-- warehouse.Batumi:AddAsset("Zvezdny") +-- warehouse.Batumi:AddAsset("Yakushev") +-- warehouse.Batumi:AddAsset("Elnya") +-- warehouse.Batumi:AddAsset("Ivanov") +-- warehouse.Batumi:AddAsset("Yantai") +-- warehouse.Batumi:AddAsset("Type 052C") +-- warehouse.Batumi:AddAsset("Guangzhou") +-- +-- -- Get Number of ships at Batumi. +-- local nships=warehouse.Batumi:GetNumberOfAssets(WAREHOUSE.Descriptor.CATEGORY, Group.Category.SHIP) +-- +-- -- Send one ship every 5 minutes. +-- for i=1, nships do +-- warehouse.Batumi:__AddRequest(300*(i-1)+10, warehouse.Kobuleti, WAREHOUSE.Descriptor.CATEGORY, Group.Category.SHIP, 1) +-- end +-- +-- ## Example 10: Warehouse on Aircraft Carrier -- --- ## Example 10: Aircraft Carrier - Rescue Helo and Escort --- -- This example shows how to spawn assets from a warehouse located on an aircraft carrier. The warehouse must still be represented by a -- physical static object. However, on a carrier space is limit so we take a smaller static. In priciple one could also take something -- like a windsock. -- -- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_Carrier.png) -- +-- USS Stennis requests F/A-18s from Batumi. At the same time Kobuleti requests F/A-18s from the Stennis which currently does not have any. +-- So first, Batumi delivers the fighters to the Stennis. After they arrived they are deployed again and send to Kobuleti. +-- +-- -- Start warehouses. +-- warehouse.Batumi:Start() +-- warehouse.Stennis:Start() +-- warehouse.Kobuleti:Start() +-- +-- -- Add F/A-18 2-ship flight to Batmi. +-- warehouse.Batumi:AddAsset("F/A-18C 2ship", 1) +-- +-- -- USS Stennis requests F/A-18 from Batumi. +-- warehouse.Batumi:AddRequest(warehouse.Stennis, WAREHOUSE.Descriptor.TEMPLATENAME, "F/A-18C 2ship") +-- +-- -- Kobuleti requests F/A-18 from USS Stennis. +-- warehouse.Stennis:AddRequest(warehouse.Kobuleti, WAREHOUSE.Descriptor.TEMPLATENAME, "F/A-18C 2ship") +-- +-- ## Example 11: Aircraft Carrier - Rescue Helo and Escort +-- -- After 10 seconds we make a self request for a rescue helicopter. Note, that the @{#WAREHOUSE.AddRequest} function has a parameter which lets you -- specify an "Assignment". This can be later used to identify the request and take the right actions. -- @@ -721,79 +822,291 @@ -- -- Start warehouse on USS Stennis. -- warehouse.Stennis:Start() -- --- -- Add speedboat and helo assets. +-- -- Aircraft carrier gets a moving zone right behind it as port. +-- warehouse.Stennis:SetPortZone(ZONE_UNIT:New("Warehouse Stennis Port Zone", UNIT:FindByName("USS Stennis"), 100, {rho=250, theta=180, relative_to_unit=true})) +-- +-- -- Add speedboat assets. -- warehouse.Stennis:AddAsset("Speedboat", 10) --- warehouse.Stennis:AddAsset("CH-53E", 3) +-- warehouse.Stennis:AddAsset("CH-53E", 1) -- --- -- Define a "port" at the Stennis to be able to spawn Naval assets. This zone will move behind the Stennis. --- local stenniszone=ZONE_UNIT:New("Spawnzone Stennis", UNIT:FindByName("USS Stennis"), 100, {rho=250, theta=180, relative_to_unit=true}) --- warehouse.Stennis:SetPortZone(stenniszone) --- --- -- Self request of rescue helo and speed boats. +-- -- Self request of speed boats. -- warehouse.Stennis:__AddRequest(10, warehouse.Stennis, WAREHOUSE.Descriptor.TEMPLATENAME, "CH-53E", 1, nil, nil, nil, "Rescue Helo") -- warehouse.Stennis:__AddRequest(30, warehouse.Stennis, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.NAVAL_ARMEDSHIP, 5, nil, nil, nil, "Speedboats Left") -- warehouse.Stennis:__AddRequest(45, warehouse.Stennis, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.NAVAL_ARMEDSHIP, 5, nil, nil, nil, "Speedboats Right") -- -- --- Function called after self request --- function warehouse.Stennis:OnAfterSelfRequest(From,Event,To,groupset,request) +-- function warehouse.Stennis:OnAfterSelfRequest(From, Event, To,_groupset, request) -- --- local groupset=groupset --Core.Set#SET_GROUP +-- local groupset=_groupset --Core.Set#SET_GROUP -- local request=request --Functional.Warehouse#WAREHOUSE.Pendingitem -- -- -- USS Stennis is the mother ship. -- local Mother=UNIT:FindByName("USS Stennis") -- --- -- Get assignment for this request. +-- -- Get assignment of the request. -- local assignment=warehouse.Stennis:GetAssignment(request) -- -- if assignment=="Speedboats Left" then -- -- -- Define AI Formation object. -- -- Note that this has to be a global variable or the garbage collector will remove it for some reason! --- CarrierFormationLeft = AI_FORMATION:New(Mother, groupset, "Port Formation with Carrier", "Follow Carrier at given parameters.") --- --- -- Formation parameters and start. --- CarrierFormationLeft:FormationLeftWing(200 ,50, 0, 0, 500, 50) +-- CarrierFormationLeft = AI_FORMATION:New(Mother, groupset, "Left Formation with Carrier", "Escort Carrier.") +-- +-- -- Formation parameters. +-- CarrierFormationLeft:FormationLeftWing(200 ,50, 0, 0, 500, 50) -- CarrierFormationLeft:__Start(2) --- +-- -- for _,group in pairs(groupset:GetSetObjects()) do -- local group=group --Wrapper.Group#GROUP -- group:FlareRed() --- end +-- end -- -- elseif assignment=="Speedboats Right" then -- -- -- Define AI Formation object. -- -- Note that this has to be a global variable or the garbage collector will remove it for some reason! --- CarrierFormationRight = AI_FORMATION:New(Mother, groupset, "Starboard Formation with Carrier", "Follow Carrier at given parameters.") --- --- -- Formation parameters and start. --- CarrierFormationRight:FormationRightWing(200 ,50, 0, 0, 500, 50) --- CarrierFormationRight:__Start(2) +-- CarrierFormationRight = AI_FORMATION:New(Mother, groupset, "Right Formation with Carrier", "Escort Carrier.") +-- +-- -- Formation parameters. +-- CarrierFormationRight:FormationRightWing(200 ,50, 0, 0, 500, 50) +-- CarrierFormationRight:__Start(2) +-- +-- for _,group in pairs(groupset:GetSetObjects()) do +-- local group=group --Wrapper.Group#GROUP +-- group:FlareGreen() +-- end -- -- elseif assignment=="Rescue Helo" then --- +-- +-- -- Start uncontrolled helo. +-- local group=groupset:GetFirst() --Wrapper.Group#GROUP +-- group:StartUncontrolled() +-- -- -- Define AI Formation object. --- CarrierFormationHelo = AI_FORMATION:New(Mother, groupset, "Helo Formation with Carrier", "Follow Carrier at given parameters.") --- --- -- Formation parameters and start. +-- CarrierFormationHelo = AI_FORMATION:New(Mother, groupset, "Helo Formation with Carrier", "Fly Formation.") +-- +-- -- Formation parameters. -- CarrierFormationHelo:FormationCenterWing(-150, 50, 20, 50, 100, 50) -- CarrierFormationHelo:__Start(2) -- -- end -- --- --- When the helo is out of fuel, it will return to the carrier. The asset is considered as delivered. +-- --- When the helo is out of fuel, it will return to the carrier and should be delivered. -- function warehouse.Stennis:OnAfterDelivered(From,Event,To,request) -- local request=request --Functional.Warehouse#WAREHOUSE.Pendingitem -- -- -- So we start another request. --- if warehouse.Stennis:GetAssignment(request)=="Rescue Helo" then +-- if request.assignment=="Rescue Helo" then -- warehouse.Stennis:__AddRequest(10, warehouse.Stennis, WAREHOUSE.Descriptor.TEMPLATENAME, "CH-53E", 1, nil, nil, nil, "Rescue Helo") -- end -- end -- -- end -- +-- ## Example 12: Pause and Unpause a Warehouse +-- +-- This example shows how to pause a warehouse. In paused state, no requests will be processed but assets can be added or be requests made. +-- +-- * Warehouse Batumi is paused after 10 seconds. +-- * Request from Berlin after 15 which will not be processed. +-- * New tank assets for Batumi after 20 seconds. This is possible also in paused state. +-- * Batumi unpaused after 30 seconds. Queued request from Berlin can be processed. +-- * Berlin is paused after 60 seconds. +-- * Berlin requests tanks from Batumi after 90 seconds. Request is not processed because Berlin is paused and not running. +-- * Berlin is unpaused after 120 seconds. Queued request for tanks from Batumi can not be processed. +-- +-- Here is the code: +-- +-- -- Start Warehouse at Batumi. +-- warehouse.Batumi:Start() +-- +-- -- Start Warehouse Berlin. +-- warehouse.Berlin:Start() +-- +-- -- Add 20 infantry groups and 5 tank platoons as assets at Batumi. +-- warehouse.Batumi:AddAsset("Infantry Platoon Alpha", 20) +-- +-- -- Pause the warehouse after 10 seconds +-- warehouse.Batumi:__Pause(10) +-- +-- -- Add a request from Berlin after 15 seconds. A request can be added but not be processed while warehouse is paused. +-- warehouse.Batumi:__AddRequest(15, warehouse.Berlin, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 1) +-- +-- -- New asset added after 20 seconds. This is possible even if the warehouse is paused. +-- warehouse.Batumi:__AddAsset(20, "Abrams", 5) +-- +-- -- Unpause warehouse after 30 seconds. Now the request from Berlin can be processed. +-- warehouse.Batumi:__Unpause(30) +-- +-- -- Pause warehouse Berlin +-- warehouse.Berlin:__Pause(60) +-- +-- -- After 90 seconds request from Berlin for tanks. +-- warehouse.Batumi:__AddRequest(90, warehouse.Berlin, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_TANK, 1) +-- +-- -- After 120 seconds unpause Berlin. +-- warehouse.Berlin:__Unpause(120) +-- +-- ## Example 13: Battlefield Air Interdiction +-- +-- This example show how to couple the WAREHOUSE class with the @{AI.AI_BAI} class. +-- Four enemy targets have been located at the famous Kobuleti X. Three Viggen 2-ship flights are assigned to kill at least one of the BMPs to complete their mission. +-- +-- -- Start Warehouse at Kobuleti. +-- warehouse.Kobuleti:Start() +-- +-- -- Add three 2-ship groups of Viggens. +-- warehouse.Kobuleti:AddAsset("Viggen 2ship", 3) +-- +-- -- Self request for all Viggen assets. +-- warehouse.Kobuleti:AddRequest(warehouse.Kobuleti, WAREHOUSE.Descriptor.TEMPLATENAME, "Viggen 2ship", WAREHOUSE.Quantity.ALL, nil, nil, nil, "BAI") +-- +-- -- Red targets at Kobuleti X (late activated). +-- local RedTargets=GROUP:FindByName("Red IVF Alpha") +-- +-- -- Activate the targets. +-- RedTargets:Activate() +-- +-- -- Do something with the spawned aircraft. +-- function warehouse.Kobuleti:OnAfterSelfRequest(From,Event,To,groupset,request) +-- local groupset=groupset --Core.Set#SET_GROUP +-- local request=request --Functional.Warehouse#WAREHOUSE.Pendingitem +-- +-- if request.assignment=="BAI" then +-- +-- for _,group in pairs(groupset:GetSetObjects()) do +-- local group=group --Wrapper.Group#GROUP +-- +-- -- Start uncontrolled aircraft. +-- group:StartUncontrolled() +-- +-- local BAI=AI_BAI_ZONE:New(ZONE:New("Patrol Zone Kobuleti"), 500, 1000, 500, 600, ZONE:New("Patrol Zone Kobuleti")) +-- +-- -- Tell the program to use the object (in this case called BAIPlane) as the group to use in the BAI function +-- BAI:SetControllable(group) +-- +-- -- Function checking if targets are still alive +-- local function CheckTargets() +-- local nTargets=RedTargets:GetSize() +-- local nInitial=RedTargets:GetInitialSize() +-- local nDead=nInitial-nTargets +-- local nRequired=1 -- Let's make this easy. +-- if RedTargets:IsAlive() and nDead < nRequired then +-- MESSAGE:New(string.format("BAI Mission: %d of %d red targets still alive. At least %d targets need to be eliminated.", nTargets, nInitial, nRequired), 5):ToAll() +-- else +-- MESSAGE:New("BAI Mission: The required red targets are destroyed.", 30):ToAll() +-- BAI:__Accomplish(1) -- Now they should fly back to the patrolzone and patrol. +-- end +-- end +-- +-- -- Start scheduler to monitor number of targets. +-- local Check, CheckScheduleID = SCHEDULER:New(nil, CheckTargets, {}, 60, 60) +-- +-- -- When the targets in the zone are destroyed, (see scheduled function), the planes will return home ... +-- function BAI:OnAfterAccomplish( Controllable, From, Event, To ) +-- MESSAGE:New( "BAI Mission: Sending the Viggens back to base.", 30):ToAll() +-- Check:Stop(CheckScheduleID) +-- BAI:__RTB(1) +-- end +-- +-- -- Start BAI +-- BAI:Start() +-- +-- -- Engage after 5 minutes. +-- BAI:__Engage(300) +-- +-- -- RTB after 30 min max. +-- BAI:__RTB(-30*60) +-- +-- end +-- end +-- +-- end +-- +-- ## Example 14: Strategic Bombing +-- +-- This example shows how to employ stategic bombers in a mission. Three B-52s are lauched at Kobuleti with the assignment to wipe out the enemy warehouse at Sukhumi. +-- The bombers will get a flight path and make their approach from the South at an altitude of 5000 m ASL. After their bombing run, they will return to Kobuleti and +-- added back to stock. +-- +-- -- Start warehouses +-- warehouse.Kobuleti:Start() +-- warehouse.Sukhumi:Start() +-- +-- -- Add a strategic bomber assets +-- warehouse.Kobuleti:AddAsset("B-52H", 3) +-- +-- -- Request bombers for specific task of bombing Sukhumi warehouse. +-- warehouse.Kobuleti:AddRequest(warehouse.Kobuleti, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.AIR_BOMBER, WAREHOUSE.Quantity.ALL, nil, nil, nil, "Bomb Sukhumi") +-- +-- -- Specify assignment after bombers have been spawned. +-- function warehouse.Kobuleti:OnAfterSelfRequest(From, Event, To, groupset, request) +-- local groupset=groupset --Core.Set#SET_GROUP +-- +-- -- Get assignment of this request. +-- local assignment=warehouse.Kobuleti:GetAssignment(request) +-- +-- if assignment=="Bomb Sukhumi" then +-- +-- for _,_group in pairs(groupset:GetSet()) do +-- local group=_group --Wrapper.Group#GROUP +-- +-- group:StartUncontrolled() +-- group:SmokeBlue() +-- +-- -- Target coordinate! +-- local ToCoord=warehouse.Sukhumi:GetCoordinate() +-- ToCoord.y=5000 -- Adjust altitude +-- +-- local FoCoord=warehouse.Kobuleti:GetCoordinate() +-- FoCoord.y=3000 -- Ajust altitude. +-- +-- -- Task bomb Sukhumi warehouse using all bombs (2032) from direction 180 at altitude 5000 m. +-- local task=group:TaskBombing(warehouse.Sukhumi:GetCoordinate():GetVec2(), false, "All", nil , 180, 5000, 2032) +-- +-- -- Define waypoints. +-- local WayPoints={} +-- +-- -- Take off position. +-- WayPoints[1]=warehouse.Kobuleti:GetCoordinate():WaypointAirTakeOffParking() +-- -- Begin bombing run 20 km south of target. +-- WayPoints[2]=ToCoord:Translate(20*1000, 180):WaypointAirTurningPoint(nil, 600, {task}, "Bombing Run") +-- -- Return to base. +-- WayPoints[3]=FoCoord:WaypointAirTurningPoint() +-- -- Land at homebase. Bombers are added back to stock and can be employed in later assignments. +-- WayPoints[4]=warehouse.Kobuleti:GetCoordinate():WaypointAirLanding() +-- +-- -- Route bombers. +-- group:Route(WayPoints) +-- end +-- +-- end +-- end +-- +-- ## Example 15: Defining Off-Road Paths +-- +-- For self propelled assets it is possible to define custom off-road paths from one warehouse to another via the @{#WAREHOUSE.AddOffRoadPath} function. +-- The waypoints of a path are taken from late activated units. In this example, two paths have been defined between the warehouses Kobuleti and FARP London. +-- Trucks are spawned at each warehouse and are guided along the paths to the other warehouse. +-- Note that if more than one path was defined, each asset group will randomly select its route. +-- +-- -- Start warehouses +-- warehouse.Kobuleti:Start() +-- warehouse.London:Start() +-- +-- warehouse.Kobuleti:AddAsset("M978", 20) +-- warehouse.London:AddAsset("M818", 20) +-- +-- -- Off two road paths from Kobuleti to London. The reverse path from London to Kobuleti is added automatically. +-- warehouse.Kobuleti:AddOffRoadPath(warehouse.London, GROUP:FindByName("Warehouse Kobuleti-London OffRoad Path 1")) +-- warehouse.Kobuleti:AddOffRoadPath(warehouse.London, GROUP:FindByName("Warehouse Kobuleti-London OffRoad Path 2")) +-- +-- -- London requests all available trucks from Kobuleti. +-- warehouse.Kobuleti:AddRequest(warehouse.London, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_TRUCK, WAREHOUSE.Quantity.ALL) +-- +-- -- Kobuleti requests all available trucks from London. +-- warehouse.London:AddRequest(warehouse.Kobuleti, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_TRUCK, WAREHOUSE.Quantity.HALF) +-- -- -- @field #WAREHOUSE WAREHOUSE = { @@ -990,13 +1303,12 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.4.9" +WAREHOUSE.version="0.5.0" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- TODO: Get cargo bay and weight from CARGO_GROUP and GROUP. --- TODO: Add possibility to set weight and cargo bay manually in AddAsset function as optional parameters. + -- TODO: Spawn assets only virtually, i.e. remove requested assets from stock but do NOT spawn them ==> Interface to A2A dispatcher! Maybe do a negative sign on asset number? -- TODO: Test capturing a neutral warehouse. -- TODO: Make more examples: ARTY, CAP, ... @@ -1004,6 +1316,8 @@ WAREHOUSE.version="0.4.9" -- TODO: Handle the case when units of a group die during the transfer. -- TODO: Added habours as interface for transport to from warehouses? -- TODO: Add save/load capability of warehouse <==> percistance after mission restart. Difficult in lua! +-- DONE: Get cargo bay and weight from CARGO_GROUP and GROUP. No necessary any more! +-- DONE: Add possibility to set weight and cargo bay manually in AddAsset function as optional parameters. -- DONE: Check overlapping aircraft sometimes. -- DONE: Case when all transports are killed and there is still cargo to be delivered. Put cargo back into warehouse. Should be done now! -- DONE: Add transport units from dispatchers back to warehouse stock once they completed their mission. @@ -1027,7 +1341,7 @@ WAREHOUSE.version="0.4.9" -- DONE: Add general message function for sending to coaliton or debug. -- DONE: Fine tune event handlers. -- DONE: Improve generalized attributes. --- DONE: If warehouse is destoyed, all asssets are gone. +-- DONE: If warehouse is destroyed, all asssets are gone. -- DONE: Add event handlers. -- DONE: Add AI_CARGO_AIRPLANE -- DONE: Add AI_CARGO_APC @@ -1125,7 +1439,7 @@ function WAREHOUSE:New(warehouse, alias) self:AddTransition("Attacked", "Captured", "Running") -- Warehouse was captured by another coalition. It must have been attacked first. self:AddTransition("*", "AirbaseCaptured", "*") -- Airbase was captured by other coalition. self:AddTransition("*", "AirbaseRecaptured", "*") -- Airbase was re-captured from other coalition. - self:AddTransition("*", "Destroyed", "Destoyed") -- Warehouse was destroyed. All assets in stock are gone and warehouse is stopped. + self:AddTransition("*", "Destroyed", "Destroyed") -- Warehouse was destroyed. All assets in stock are gone and warehouse is stopped. ------------------------ --- Pseudo Functions --- @@ -2171,10 +2485,6 @@ function WAREHOUSE:onafterStatus(From, Event, To) -- Check if warehouse is being attacked or has even been captured. self:_CheckConquered() - - -- Print queue. - --self:_PrintQueue(self.queue, "Queue waiting - before request") - --self:_PrintQueue(self.pending, "Queue pending - before request") -- Check if requests are valid and remove invalid one. self:_CheckRequestConsistancy(self.queue) @@ -2189,13 +2499,13 @@ function WAREHOUSE:onafterStatus(From, Event, To) if request then self:Request(request) end - - -- Print queue after processing requests. - self:_PrintQueue(self.queue, "Queue waiting") - self:_PrintQueue(self.pending, "Queue pending") - + end + -- Print queue after processing requests. + self:_PrintQueue(self.queue, "Queue waiting") + self:_PrintQueue(self.pending, "Queue pending") + -- Update warhouse marker on F10 map. self:_UpdateWarehouseMarkText() @@ -2461,7 +2771,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu ------------------------- -- Debug info. - self:_DebugMessage(self.wid..string.format("Warehouse %s: Adding %d NEW assets of group %s to stock.", self.alias, n, tostring(group:GetName())), 5) + self:_DebugMessage(string.format("Warehouse %s: Adding %d NEW assets of group %s to stock.", self.alias, n, tostring(group:GetName())), 5) -- This is a group that is not in the db yet. Add it n times. local assets=self:_RegisterAsset(group, n, forceattribute, forcecargobay, forceweight) @@ -2696,7 +3006,19 @@ function WAREHOUSE:onbeforeAddRequest(From, Event, To, warehouse, AssetDescripto self:_ErrorMessage("ERROR: Invalid request. Asset descriptor is not ATTRIBUTE, CATEGORY, TEMPLATENAME or UNITTYPE!", 5) okay=false end - + + -- Warehouse is stopped? + if self:IsStopped() then + self:_ErrorMessage("ERROR: Invalid request. Warehouse is stopped!", 0) + okay=false + end + + -- Warehouse is destroyed? + if self:IsDestroyed() then + self:_ErrorMessage("ERROR: Invalid request. Warehouse is destroyed!", 0) + okay=false + end + return okay end @@ -2758,7 +3080,7 @@ function WAREHOUSE:onafterAddRequest(From, Event, To, warehouse, AssetDescriptor -- Add request to queue. table.insert(self.queue, request) - local text=string.format("Warehouse %s: New request from %s. Descriptor %s=%s, #assets=%s; Transport=%s, #transports =%s.", + local text=string.format("Warehouse %s: New request from warehouse %s.\nDescriptor %s=%s, #assets=%s; Transport=%s, #transports =%s.", self.alias, warehouse.alias, request.assetdesc, tostring(request.assetdescval), tostring(request.nasset), request.transporttype, tostring(request.ntransport)) self:_DebugMessage(text, 5) @@ -3633,12 +3955,23 @@ end function WAREHOUSE:onafterDestroyed(From, Event, To) -- Message. - local text=string.format("Warehouse %s was destroyed!", self.alias) + local text=string.format("Warehouse %s was destroyed! Assets lost %d.", self.alias, #self.stock) self:_InfoMessage(text) + + -- Remove all table entries from waiting queue and stock. + for k,_ in pairs(self.queue) do + self.queue[k]=nil + end + for k,_ in pairs(self.stock) do + self.stock[k]=nil + end + + --self.queue=nil + --self.queue={} + + --self.stock=nil + --self.stock={} - -- Stop warehouse FSM in one minute. - -- Maybe dont stop it or pending requests are not updated any more. - --self:__Stop(60) end --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- @@ -3709,7 +4042,6 @@ function WAREHOUSE:_SpawnAssetRequest(Request) -- Spawn train. if self.rail then --TODO: Rail should only get one asset because they would spawn on top! - --_group=_spawn:SpawnFromCoordinate(self.rail) end self:E(self.wid.."ERROR: Spawning of TRAIN assets not possible yet!") @@ -3858,6 +4190,7 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol -- Check enough parking spots. if AirbaseCategory==Airbase.Category.HELIPAD or AirbaseCategory==Airbase.Category.SHIP then + --TODO Figure out what's necessary in this case. else @@ -3913,9 +4246,10 @@ function WAREHOUSE:_SpawnAssetAircraft(alias, asset, request, parking, uncontrol -- DCS bug workaround. Spawning helos in uncontrolled state on carriers causes a big spash! -- See https://forums.eagle.ru/showthread.php?t=219550 - if AirbaseCategory == Airbase.Category.SHIP and asset.category==Group.Category.HELICOPTER then - uncontrolled=false - end + -- Should be solved in latest OB update 2.5.3.21708 + --if AirbaseCategory == Airbase.Category.SHIP and asset.category==Group.Category.HELICOPTER then + -- uncontrolled=false + --end -- Uncontrolled spawning. template.uncontrolled=uncontrolled @@ -4729,6 +5063,12 @@ function WAREHOUSE:_CheckRequestConsistancy(queue) self:E(self.wid..string.format("ERROR: INVALID request. Requesting warehouse is stopped!")) valid=false end + + -- Is receiving warehouse destroyed? + if request.warehouse:IsDestroyed() then + self:E(self.wid..string.format("ERROR: INVALID request. Requesting warehouse is destroyed!")) + valid=false + end -- Add request as unvalid and delete it later. if valid==false then @@ -6171,7 +6511,7 @@ function WAREHOUSE:_UpdateWarehouseMarkText() local _data=self:GetStockInfo(self.stock) -- Text. - local text=string.format("Warehouse Stock - total assets %d:\n", #self.stock) + local text=string.format("Warehouse state: %s\nStock - total assets %d:\n", self:GetState(), #self.stock) for _attribute,_count in pairs(_data) do if _count>0 then @@ -6531,7 +6871,7 @@ function WAREHOUSE:_GetFlightplan(asset, departure, destination) text=text..string.format("FL max = %.3f km\n", FLmax/1000) text=text..string.format("Ceiling = %.3f km\n", ceiling/1000) text=text..string.format("Max range = %.3f km\n", Range/1000) - env.info(text) + self:T(self.wid..text) -- Ensure that cruise distance is positve. Can be slightly negative in special cases. And we don't want to turn back. if d_cruise<0 then From ea851af6eacaaf2a3746c5a1271fa8c924dc41ff Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 18 Sep 2018 07:45:11 +0200 Subject: [PATCH 372/420] @shadowze I've done a fix to remove the event handlers from SET_CARGO using :FilterStop(). Can you check this please? --- Moose Development/Moose/Core/Event.lua | 1 - Moose Development/Moose/Core/Set.lua | 6509 ++++++++++++------------ 2 files changed, 3282 insertions(+), 3228 deletions(-) diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index c493a453f..09287fca6 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -503,7 +503,6 @@ function EVENT:RemoveEvent( EventClass, EventID ) self.Events = self.Events or {} self.Events[EventID] = self.Events[EventID] or {} self.Events[EventID][EventPriority] = self.Events[EventID][EventPriority] or {} - self.Events[EventID][EventPriority][EventClass] = self.Events[EventID][EventPriority][EventClass] self.Events[EventID][EventPriority][EventClass] = nil diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 45a0f4091..c5d6c1efe 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -32,1460 +32,1468 @@ -- @image Core_Sets.JPG ---- @type SET_BASE --- @field #table Filter --- @field #table Set --- @field #table List --- @field Core.Scheduler#SCHEDULER CallScheduler --- @extends Core.Base#BASE +do -- SET_BASE - ---- The @{Core.Set#SET_BASE} class defines the core functions that define a collection of objects. --- A SET provides iterators to iterate the SET, but will **temporarily** yield the ForEach interator loop at defined **"intervals"** to the mail simulator loop. --- In this way, large loops can be done while not blocking the simulator main processing loop. --- The default **"yield interval"** is after 10 objects processed. --- The default **"time interval"** is after 0.001 seconds. --- --- ## Add or remove objects from the SET --- --- Some key core functions are @{Core.Set#SET_BASE.Add} and @{Core.Set#SET_BASE.Remove} to add or remove objects from the SET in your logic. --- --- ## Define the SET iterator **"yield interval"** and the **"time interval"** --- --- Modify the iterator intervals with the @{Core.Set#SET_BASE.SetInteratorIntervals} method. --- You can set the **"yield interval"**, and the **"time interval"**. (See above). --- --- @field #SET_BASE SET_BASE -SET_BASE = { - ClassName = "SET_BASE", - Filter = {}, - Set = {}, - List = {}, - Index = {}, -} - - ---- Creates a new SET_BASE object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names. --- @param #SET_BASE self --- @return #SET_BASE --- @usage --- -- Define a new SET_BASE Object. This DBObject will contain a reference to all Group and Unit Templates defined within the ME and the DCSRTE. --- DBObject = SET_BASE:New() -function SET_BASE:New( Database ) - - -- Inherits from BASE - local self = BASE:Inherit( self, FSM:New() ) -- Core.Set#SET_BASE + --- @type SET_BASE + -- @field #table Filter + -- @field #table Set + -- @field #table List + -- @field Core.Scheduler#SCHEDULER CallScheduler + -- @extends Core.Base#BASE - self.Database = Database - - self:SetStartState( "Started" ) - --- Added Handler OnAfter for SET_BASE - -- @function [parent=#SET_BASE] OnAfterAdded + --- The @{Core.Set#SET_BASE} class defines the core functions that define a collection of objects. + -- A SET provides iterators to iterate the SET, but will **temporarily** yield the ForEach interator loop at defined **"intervals"** to the mail simulator loop. + -- In this way, large loops can be done while not blocking the simulator main processing loop. + -- The default **"yield interval"** is after 10 objects processed. + -- The default **"time interval"** is after 0.001 seconds. + -- + -- ## Add or remove objects from the SET + -- + -- Some key core functions are @{Core.Set#SET_BASE.Add} and @{Core.Set#SET_BASE.Remove} to add or remove objects from the SET in your logic. + -- + -- ## Define the SET iterator **"yield interval"** and the **"time interval"** + -- + -- Modify the iterator intervals with the @{Core.Set#SET_BASE.SetInteratorIntervals} method. + -- You can set the **"yield interval"**, and the **"time interval"**. (See above). + -- + -- @field #SET_BASE SET_BASE + SET_BASE = { + ClassName = "SET_BASE", + Filter = {}, + Set = {}, + List = {}, + Index = {}, + } + + + --- Creates a new SET_BASE object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names. -- @param #SET_BASE self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param #string ObjectName The name of the object. - -- @param Object The object. + -- @return #SET_BASE + -- @usage + -- -- Define a new SET_BASE Object. This DBObject will contain a reference to all Group and Unit Templates defined within the ME and the DCSRTE. + -- DBObject = SET_BASE:New() + function SET_BASE:New( Database ) + -- Inherits from BASE + local self = BASE:Inherit( self, FSM:New() ) -- Core.Set#SET_BASE + + self.Database = Database - self:AddTransition( "*", "Added", "*" ) + self:SetStartState( "Started" ) + + --- Added Handler OnAfter for SET_BASE + -- @function [parent=#SET_BASE] OnAfterAdded + -- @param #SET_BASE self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #string ObjectName The name of the object. + -- @param Object The object. + + + self:AddTransition( "*", "Added", "*" ) + + --- Removed Handler OnAfter for SET_BASE + -- @function [parent=#SET_BASE] OnAfterRemoved + -- @param #SET_BASE self + -- @param #string From + -- @param #string Event + -- @param #string To + -- @param #string ObjectName The name of the object. + -- @param Object The object. + + self:AddTransition( "*", "Removed", "*" ) - --- Removed Handler OnAfter for SET_BASE - -- @function [parent=#SET_BASE] OnAfterRemoved + self.YieldInterval = 10 + self.TimeInterval = 0.001 + + self.Set = {} + self.Index = {} + + self.CallScheduler = SCHEDULER:New( self ) + + self:SetEventPriority( 2 ) + + return self + end + + --- Finds an @{Core.Base#BASE} object based on the object Name. -- @param #SET_BASE self - -- @param #string From - -- @param #string Event - -- @param #string To - -- @param #string ObjectName The name of the object. - -- @param Object The object. + -- @param #string ObjectName + -- @return Core.Base#BASE The Object found. + function SET_BASE:_Find( ObjectName ) - self:AddTransition( "*", "Removed", "*" ) - - self.YieldInterval = 10 - self.TimeInterval = 0.001 - - self.Set = {} - self.Index = {} - - self.CallScheduler = SCHEDULER:New( self ) - - self:SetEventPriority( 2 ) - - return self -end - ---- Finds an @{Core.Base#BASE} object based on the object Name. --- @param #SET_BASE self --- @param #string ObjectName --- @return Core.Base#BASE The Object found. -function SET_BASE:_Find( ObjectName ) - - local ObjectFound = self.Set[ObjectName] - return ObjectFound -end - - ---- Gets the Set. --- @param #SET_BASE self --- @return #SET_BASE self -function SET_BASE:GetSet() - self:F2() - - return self.Set -end - ---- Gets a list of the Names of the Objects in the Set. --- @param #SET_BASE self --- @return #SET_BASE self -function SET_BASE:GetSetNames() -- R2.3 - self:F2() - - local Names = {} - - for Name, Object in pairs( self.Set ) do - table.insert( Names, Name ) + local ObjectFound = self.Set[ObjectName] + return ObjectFound end - return Names -end - - ---- Gets a list of the Objects in the Set. --- @param #SET_BASE self --- @return #SET_BASE self -function SET_BASE:GetSetObjects() -- R2.3 - self:F2() - local Objects = {} - - for Name, Object in pairs( self.Set ) do - table.insert( Objects, Object ) + --- Gets the Set. + -- @param #SET_BASE self + -- @return #SET_BASE self + function SET_BASE:GetSet() + self:F2() + + return self.Set end - return Objects -end - - ---- Removes a @{Core.Base#BASE} object from the @{Core.Set#SET_BASE} and derived classes, based on the Object Name. --- @param #SET_BASE self --- @param #string ObjectName --- @param NoTriggerEvent (optional) When `true`, the :Remove() method will not trigger a **Removed** event. -function SET_BASE:Remove( ObjectName, NoTriggerEvent ) - self:F2( { ObjectName = ObjectName } ) - - local Object = self.Set[ObjectName] - - if Object then - for Index, Key in ipairs( self.Index ) do - if Key == ObjectName then - table.remove( self.Index, Index ) - self.Set[ObjectName] = nil - break - end + --- Gets a list of the Names of the Objects in the Set. + -- @param #SET_BASE self + -- @return #SET_BASE self + function SET_BASE:GetSetNames() -- R2.3 + self:F2() + + local Names = {} + + for Name, Object in pairs( self.Set ) do + table.insert( Names, Name ) end - -- When NoTriggerEvent is true, then no Removed event will be triggered. - if not NoTriggerEvent then - self:Removed( ObjectName, Object ) - end - end -end - - ---- Adds a @{Core.Base#BASE} object in the @{Core.Set#SET_BASE}, using a given ObjectName as the index. --- @param #SET_BASE self --- @param #string ObjectName --- @param Core.Base#BASE Object --- @return Core.Base#BASE The added BASE Object. -function SET_BASE:Add( ObjectName, Object ) - self:F2( { 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 - self:Remove( ObjectName, true ) - end - self.Set[ObjectName] = Object - table.insert( self.Index, ObjectName ) - - self:Added( ObjectName, Object ) -end - ---- Adds a @{Core.Base#BASE} object in the @{Core.Set#SET_BASE}, using the Object Name as the index. --- @param #SET_BASE self --- @param Wrapper.Object#OBJECT Object --- @return Core.Base#BASE The added BASE Object. -function SET_BASE:AddObject( Object ) - self:F2( Object.ObjectName ) - - self:T( Object.UnitName ) - self:T( Object.ObjectName ) - self:Add( Object.ObjectName, Object ) - -end - - - - ---- Gets a @{Core.Base#BASE} object from the @{Core.Set#SET_BASE} and derived classes, based on the Object Name. --- @param #SET_BASE self --- @param #string ObjectName --- @return Core.Base#BASE -function SET_BASE:Get( ObjectName ) - self:F( ObjectName ) - - local Object = self.Set[ObjectName] - - self:T3( { ObjectName, Object } ) - return Object -end - ---- Gets the first object from the @{Core.Set#SET_BASE} and derived classes. --- @param #SET_BASE self --- @return Core.Base#BASE -function SET_BASE:GetFirst() - - local ObjectName = self.Index[1] - local FirstObject = self.Set[ObjectName] - self:T3( { FirstObject } ) - return FirstObject -end - ---- Gets the last object from the @{Core.Set#SET_BASE} and derived classes. --- @param #SET_BASE self --- @return Core.Base#BASE -function SET_BASE:GetLast() - - local ObjectName = self.Index[#self.Index] - local LastObject = self.Set[ObjectName] - self:T3( { LastObject } ) - return LastObject -end - ---- Gets a random object from the @{Core.Set#SET_BASE} and derived classes. --- @param #SET_BASE self --- @return Core.Base#BASE -function SET_BASE:GetRandom() - - local RandomItem = self.Set[self.Index[math.random(#self.Index)]] - self:T3( { RandomItem } ) - return RandomItem -end - - ---- Retrieves the amount of objects in the @{Core.Set#SET_BASE} and derived classes. --- @param #SET_BASE self --- @return #number Count -function SET_BASE:Count() - - return self.Index and #self.Index or 0 -end - - ---- Copies the Filter criteria from a given Set (for rebuilding a new Set based on an existing Set). --- @param #SET_BASE self --- @param #SET_BASE BaseSet --- @return #SET_BASE -function SET_BASE:SetDatabase( BaseSet ) - - -- Copy the filter criteria of the BaseSet - local OtherFilter = routines.utils.deepCopy( BaseSet.Filter ) - self.Filter = OtherFilter - - -- Now base the new Set on the BaseSet - self.Database = BaseSet:GetSet() - return self -end - - - ---- Define the SET iterator **"yield interval"** and the **"time interval"**. --- @param #SET_BASE self --- @param #number YieldInterval Sets the frequency when the iterator loop will yield after the number of objects processed. The default frequency is 10 objects processed. --- @param #number TimeInterval Sets the time in seconds when the main logic will resume the iterator loop. The default time is 0.001 seconds. --- @return #SET_BASE self -function SET_BASE:SetIteratorIntervals( YieldInterval, TimeInterval ) - - self.YieldInterval = YieldInterval - self.TimeInterval = TimeInterval - - return self -end - - ---- Filters for the defined collection. --- @param #SET_BASE self --- @return #SET_BASE self -function SET_BASE:FilterOnce() - - for ObjectName, Object in pairs( self.Database ) do - - if self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) + + return Names + end + + + --- Gets a list of the Objects in the Set. + -- @param #SET_BASE self + -- @return #SET_BASE self + function SET_BASE:GetSetObjects() -- R2.3 + self:F2() + + local Objects = {} + + for Name, Object in pairs( self.Set ) do + table.insert( Objects, Object ) end + + return Objects end - return self -end - ---- Starts the filtering for the defined collection. --- @param #SET_BASE self --- @return #SET_BASE self -function SET_BASE:_FilterStart() - - for ObjectName, Object in pairs( self.Database ) do - - if self:IsIncludeObject( Object ) then - self:E( { "Adding Object:", ObjectName } ) - self:Add( ObjectName, Object ) - end - end - -- Follow alive players and clients - --self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) - --self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit ) + --- Removes a @{Core.Base#BASE} object from the @{Core.Set#SET_BASE} and derived classes, based on the Object Name. + -- @param #SET_BASE self + -- @param #string ObjectName + -- @param NoTriggerEvent (optional) When `true`, the :Remove() method will not trigger a **Removed** event. + function SET_BASE:Remove( ObjectName, NoTriggerEvent ) + self:F2( { ObjectName = ObjectName } ) - - return self -end - ---- Starts the filtering of the Dead events for the collection. --- @param #SET_BASE self --- @return #SET_BASE self -function SET_BASE:FilterDeads() --R2.1 allow deads to be filtered to automatically handle deads in the collection. - - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) - - return self -end - ---- Starts the filtering of the Crash events for the collection. --- @param #SET_BASE self --- @return #SET_BASE self -function SET_BASE:FilterCrashes() --R2.1 allow crashes to be filtered to automatically handle crashes in the collection. - - self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) - - return self -end - ---- Stops the filtering for the defined collection. --- @param #SET_BASE self --- @return #SET_BASE self -function SET_BASE:FilterStop() - - self:UnHandleEvent( EVENTS.Birth ) - self:UnHandleEvent( EVENTS.Dead ) - self:UnHandleEvent( EVENTS.Crash ) - - return self -end - ---- Iterate the SET_BASE while identifying the nearest object from a @{Core.Point#POINT_VEC2}. --- @param #SET_BASE self --- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest object in the set. --- @return Core.Base#BASE The closest object. -function SET_BASE:FindNearestObjectFromPointVec2( PointVec2 ) - self:F2( PointVec2 ) - - local NearestObject = nil - local ClosestDistance = nil - - for ObjectID, ObjectData in pairs( self.Set ) do - if NearestObject == nil then - NearestObject = ObjectData - ClosestDistance = PointVec2:DistanceFromVec2( ObjectData:GetVec2() ) - else - local Distance = PointVec2:DistanceFromVec2( ObjectData:GetVec2() ) - if Distance < ClosestDistance then - NearestObject = ObjectData - ClosestDistance = Distance - end - end - end - - return NearestObject -end - - - ------ Private method that registers all alive players in the mission. ----- @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 ) } --- for CoalitionId, CoalitionData in pairs( CoalitionsData ) do --- for UnitId, UnitData in pairs( CoalitionData ) do --- self:T3( { "UnitData:", UnitData } ) --- if UnitData and UnitData:isExist() then --- local UnitName = UnitData:getName() --- if not self.PlayersAlive[UnitName] then --- self:E( { "Add player for unit:", UnitName, UnitData:getPlayerName() } ) --- self.PlayersAlive[UnitName] = UnitData:getPlayerName() --- end --- end --- end --- end --- --- return self ---end - ---- Events - ---- Handles the OnBirth event for the Set. --- @param #SET_BASE self --- @param Core.Event#EVENTDATA Event -function SET_BASE:_EventOnBirth( Event ) - self:F3( { Event } ) - - if Event.IniDCSUnit then - local ObjectName, Object = self:AddInDatabase( Event ) - self:T3( ObjectName, Object ) - if Object and self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) - --self:_EventOnPlayerEnterUnit( Event ) - end - end -end - ---- Handles the OnDead or OnCrash event for alive units set. --- @param #SET_BASE self --- @param Core.Event#EVENTDATA Event -function SET_BASE:_EventOnDeadOrCrash( Event ) - self:F( { Event } ) - - if Event.IniDCSUnit then - local ObjectName, Object = self:FindInDatabase( Event ) - if ObjectName then - self:Remove( ObjectName ) - end - end -end - ---- Handles the OnPlayerEnterUnit event to fill the active players table (with the unit filter applied). --- @param #SET_BASE self --- @param Core.Event#EVENTDATA Event ---function SET_BASE:_EventOnPlayerEnterUnit( Event ) --- self:F3( { Event } ) --- --- if Event.IniDCSUnit then --- local ObjectName, Object = self:AddInDatabase( Event ) --- self:T3( ObjectName, Object ) --- if self:IsIncludeObject( Object ) then --- self:Add( ObjectName, Object ) --- --self:_EventOnPlayerEnterUnit( Event ) --- end --- end ---end - ---- Handles the OnPlayerLeaveUnit event to clean the active players table. --- @param #SET_BASE self --- @param Core.Event#EVENTDATA Event ---function SET_BASE:_EventOnPlayerLeaveUnit( Event ) --- self:F3( { Event } ) --- --- local ObjectName = Event.IniDCSUnit --- if Event.IniDCSUnit then --- if Event.IniDCSGroup then --- local GroupUnits = Event.IniDCSGroup:getUnits() --- local PlayerCount = 0 --- for _, DCSUnit in pairs( GroupUnits ) do --- if DCSUnit ~= Event.IniDCSUnit then --- if DCSUnit:getPlayerName() ~= nil then --- PlayerCount = PlayerCount + 1 --- end --- end --- end --- self:E(PlayerCount) --- if PlayerCount == 0 then --- self:Remove( Event.IniDCSGroupName ) --- end --- end --- end ---end - --- Iterators - ---- Iterate the SET_BASE and derived classes and call an iterator function for the given SET_BASE, providing the Object for each element within the set and optional parameters. --- @param #SET_BASE self --- @param #function IteratorFunction The function that will be called. --- @return #SET_BASE self -function SET_BASE:ForEach( IteratorFunction, arg, Set, Function, FunctionArguments ) - self:F3( arg ) - - Set = Set or self:GetSet() - arg = arg or {} - - local function CoRoutine() - local Count = 0 - for ObjectID, ObjectData in pairs( Set ) do - local Object = ObjectData - self:T3( Object ) - if Function then - if Function( unpack( FunctionArguments ), Object ) == true then - IteratorFunction( Object, unpack( arg ) ) - end - else - IteratorFunction( Object, unpack( arg ) ) + local Object = self.Set[ObjectName] + + if Object then + for Index, Key in ipairs( self.Index ) do + if Key == ObjectName then + table.remove( self.Index, Index ) + self.Set[ObjectName] = nil + break end - Count = Count + 1 --- if Count % self.YieldInterval == 0 then --- coroutine.yield( false ) --- end - end - return true - end - --- local co = coroutine.create( CoRoutine ) - local co = CoRoutine - - local function Schedule() - --- local status, res = coroutine.resume( co ) - local status, res = co() - self:T3( { status, res } ) - - if status == false then - error( res ) - end - if res == false then - return true -- resume next time the loop - end - - return false - end - - --self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) - Schedule() - - return self -end - - ------ Iterate the SET_BASE and call an interator 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. ----- @return #SET_BASE self ---function SET_BASE:ForEachDCSUnitAlive( IteratorFunction, ... ) --- self:F3( arg ) --- --- self:ForEach( IteratorFunction, arg, self.DCSUnitsAlive ) --- --- return self ---end --- ------ Iterate the SET_BASE and call an interator 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. ----- @return #SET_BASE self ---function SET_BASE:ForEachPlayer( IteratorFunction, ... ) --- self:F3( arg ) --- --- self:ForEach( IteratorFunction, arg, self.PlayersAlive ) --- --- return self ---end --- --- ------ Iterate the SET_BASE and call an interator 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. ----- @return #SET_BASE self ---function SET_BASE:ForEachClient( IteratorFunction, ... ) --- self:F3( arg ) --- --- self:ForEach( IteratorFunction, arg, self.Clients ) --- --- return self ---end - - ---- Decides whether to include the Object --- @param #SET_BASE self --- @param #table Object --- @return #SET_BASE self -function SET_BASE:IsIncludeObject( Object ) - self:F3( Object ) - - return true -end - ---- Gets a string with all the object names. --- @param #SET_BASE self --- @return #string A string with the names of the objects. -function SET_BASE:GetObjectNames() - self:F3() - - local ObjectNames = "" - for ObjectName, Object in pairs( self.Set ) do - ObjectNames = ObjectNames .. ObjectName .. ", " - end - - return ObjectNames -end - ---- Flushes the current SET_BASE contents in the log ... (for debugging reasons). --- @param #SET_BASE self --- @param Core.Base#BASE MasterObject (optional) The master object as a reference. --- @return #string A string with the names of the objects. -function SET_BASE:Flush( MasterObject ) - self:F3() - - local ObjectNames = "" - for ObjectName, Object in pairs( self.Set ) do - ObjectNames = ObjectNames .. ObjectName .. ", " - end - self:F( { MasterObject = MasterObject and MasterObject:GetClassNameAndID(), "Objects in Set:", ObjectNames } ) - - return ObjectNames -end - - ---- @type SET_GROUP --- @extends Core.Set#SET_BASE - ---- Mission designers can use the @{Core.Set#SET_GROUP} class to build sets of groups belonging to certain: --- --- * Coalitions --- * Categories --- * Countries --- * Starting with certain prefix strings. --- --- ## SET_GROUP constructor --- --- Create a new SET_GROUP object with the @{#SET_GROUP.New} method: --- --- * @{#SET_GROUP.New}: Creates a new SET_GROUP object. --- --- ## Add or Remove GROUP(s) from SET_GROUP --- --- GROUPS can be added and removed using the @{Core.Set#SET_GROUP.AddGroupsByName} and @{Core.Set#SET_GROUP.RemoveGroupsByName} respectively. --- These methods take a single GROUP name or an array of GROUP names to be added or removed from SET_GROUP. --- --- ## SET_GROUP filter criteria --- --- You can set filter criteria to define the set of groups within the SET_GROUP. --- Filter criteria are defined by: --- --- * @{#SET_GROUP.FilterCoalitions}: Builds the SET_GROUP with the groups belonging to the coalition(s). --- * @{#SET_GROUP.FilterCategories}: Builds the SET_GROUP with the groups belonging to the category(ies). --- * @{#SET_GROUP.FilterCountries}: Builds the SET_GROUP with the gruops belonging to the country(ies). --- * @{#SET_GROUP.FilterPrefixes}: Builds the SET_GROUP with the groups starting with the same prefix string(s). --- * @{#SET_GROUP.FilterActive}: Builds the SET_GROUP with the groups that are only active. Groups that are inactive (late activation) won't be included in the set! --- --- For the Category Filter, extra methods have been added: --- --- * @{#SET_GROUP.FilterCategoryAirplane}: Builds the SET_GROUP from airplanes. --- * @{#SET_GROUP.FilterCategoryHelicopter}: Builds the SET_GROUP from helicopters. --- * @{#SET_GROUP.FilterCategoryGround}: Builds the SET_GROUP from ground vehicles or infantry. --- * @{#SET_GROUP.FilterCategoryShip}: Builds the SET_GROUP from ships. --- * @{#SET_GROUP.FilterCategoryStructure}: Builds the SET_GROUP from structures. --- --- --- Once the filter criteria have been set for the SET_GROUP, you can start filtering using: --- --- * @{#SET_GROUP.FilterStart}: Starts the filtering of the groups within the SET_GROUP and add or remove GROUP objects **dynamically**. --- * @{#SET_GROUP.FilterOnce}: Filters of the groups **once**. --- --- Planned filter criteria within development are (so these are not yet available): --- --- * @{#SET_GROUP.FilterZones}: Builds the SET_GROUP with the groups within a @{Core.Zone#ZONE}. --- --- ## SET_GROUP iterators --- --- Once the filters have been defined and the SET_GROUP has been built, you can iterate the SET_GROUP with the available iterator methods. --- The iterator methods will walk the SET_GROUP set, and call for each element within the set a function that you provide. --- The following iterator methods are currently available within the SET_GROUP: --- --- * @{#SET_GROUP.ForEachGroup}: Calls a function for each alive group it finds within the SET_GROUP. --- * @{#SET_GROUP.ForEachGroupCompletelyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. --- * @{#SET_GROUP.ForEachGroupPartlyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence partly in a @{Zone}, providing the GROUP and optional parameters to the called function. --- * @{#SET_GROUP.ForEachGroupNotInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. --- --- --- ## SET_GROUP trigger events on the GROUP objects. --- --- The SET is derived from the FSM class, which provides extra capabilities to track the contents of the GROUP objects in the SET_GROUP. --- --- ### When a GROUP object crashes or is dead, the SET_GROUP will trigger a **Dead** event. --- --- You can handle the event using the OnBefore and OnAfter event handlers. --- The event handlers need to have the paramters From, Event, To, GroupObject. --- The GroupObject is the GROUP object that is dead and within the SET_GROUP, and is passed as a parameter to the event handler. --- See the following example: --- --- -- Create the SetCarrier SET_GROUP collection. --- --- local SetHelicopter = SET_GROUP:New():FilterPrefixes( "Helicopter" ):FilterStart() --- --- -- Put a Dead event handler on SetCarrier, to ensure that when a carrier is destroyed, that all internal parameters are reset. --- --- function SetHelicopter:OnAfterDead( From, Event, To, GroupObject ) --- self:F( { GroupObject = GroupObject:GetName() } ) --- end --- --- While this is a good example, there is a catch. --- Imageine you want to execute the code above, the the self would need to be from the object declared outside (above) the OnAfterDead method. --- So, the self would need to contain another object. Fortunately, this can be done, but you must use then the **`.`** notation for the method. --- See the modified example: --- --- -- Now we have a constructor of the class AI_CARGO_DISPATCHER, that receives the SetHelicopter as a parameter. --- -- Within that constructor, we want to set an enclosed event handler OnAfterDead for SetHelicopter. --- -- But within the OnAfterDead method, we want to refer to the self variable of the AI_CARGO_DISPATCHER. --- --- function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZones ) --- --- local self = BASE:Inherit( self, FSM:New() ) -- #AI_CARGO_DISPATCHER --- --- -- Put a Dead event handler on SetCarrier, to ensure that when a carrier is destroyed, that all internal parameters are reset. --- -- Note the "." notation, and the explicit declaration of SetHelicopter, which would be using the ":" notation the implicit self variable declaration. --- --- function SetHelicopter.OnAfterDead( SetHelicopter, From, Event, To, GroupObject ) --- SetHelicopter:F( { GroupObject = GroupObject:GetName() } ) --- self.PickupCargo[GroupObject] = nil -- So here I clear the PickupCargo table entry of the self object AI_CARGO_DISPATCHER. --- self.CarrierHome[GroupObject] = nil --- end --- --- end --- --- === --- @field #SET_GROUP SET_GROUP -SET_GROUP = { - ClassName = "SET_GROUP", - Filter = { - Coalitions = nil, - Categories = nil, - Countries = nil, - GroupPrefixes = nil, - }, - FilterMeta = { - Coalitions = { - red = coalition.side.RED, - blue = coalition.side.BLUE, - neutral = coalition.side.NEUTRAL, - }, - Categories = { - plane = Group.Category.AIRPLANE, - helicopter = Group.Category.HELICOPTER, - ground = Group.Category.GROUND, -- R2.2 - ship = Group.Category.SHIP, - structure = Group.Category.STRUCTURE, - }, - }, -} - - ---- Creates a new SET_GROUP object, building a set of groups belonging to a coalitions, categories, countries, types or with defined prefix names. --- @param #SET_GROUP self --- @return #SET_GROUP --- @usage --- -- Define a new SET_GROUP Object. This DBObject will contain a reference to all alive GROUPS. --- DBObject = SET_GROUP:New() -function SET_GROUP:New() - - -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.GROUPS ) ) -- #SET_GROUP - - self:FilterActive( false ) - - return self -end - ---- Gets the Set. --- @param #SET_GROUP self --- @return #SET_GROUP self -function SET_GROUP:GetAliveSet() - self:F2() - - local AliveSet = SET_GROUP:New() - - -- Clean the Set before returning with only the alive Groups. - for GroupName, GroupObject in pairs( self.Set ) do - local GroupObject=GroupObject --Wrapper.Group#GROUP - if GroupObject then - if GroupObject:IsAlive() then - AliveSet:Add( GroupName, GroupObject ) + end + -- When NoTriggerEvent is true, then no Removed event will be triggered. + if not NoTriggerEvent then + self:Removed( ObjectName, Object ) end end end - return AliveSet.Set or {} -end - ---- Add a GROUP to SET_GROUP. --- Note that for each unit in the group that is set, a default cargo bay limit is initialized. --- @param Core.Set#SET_GROUP self --- @param Wrapper.Group#GROUP group The group which should be added to the set. --- @return self -function SET_GROUP:AddGroup( group ) - - self:Add( group:GetName(), group ) - -- I set the default cargo bay weight limit each time a new group is added to the set. - for UnitID, UnitData in pairs( group:GetUnits() ) do - UnitData:SetCargoBayWeightLimit() - end + --- Adds a @{Core.Base#BASE} object in the @{Core.Set#SET_BASE}, using a given ObjectName as the index. + -- @param #SET_BASE self + -- @param #string ObjectName + -- @param Core.Base#BASE Object + -- @return Core.Base#BASE The added BASE Object. + function SET_BASE:Add( ObjectName, Object ) + self:F2( { 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 + self:Remove( ObjectName, true ) + end + self.Set[ObjectName] = Object + table.insert( self.Index, ObjectName ) - return self -end - ---- Add GROUP(s) to SET_GROUP. --- @param Core.Set#SET_GROUP self --- @param #string AddGroupNames A single name or an array of GROUP names. --- @return self -function SET_GROUP:AddGroupsByName( AddGroupNames ) - - local AddGroupNamesArray = ( type( AddGroupNames ) == "table" ) and AddGroupNames or { AddGroupNames } - - for AddGroupID, AddGroupName in pairs( AddGroupNamesArray ) do - self:Add( AddGroupName, GROUP:FindByName( AddGroupName ) ) + self:Added( ObjectName, Object ) end - - return self -end - ---- Remove GROUP(s) from SET_GROUP. --- @param Core.Set#SET_GROUP self --- @param Wrapper.Group#GROUP RemoveGroupNames A single name or an array of GROUP names. --- @return self -function SET_GROUP:RemoveGroupsByName( RemoveGroupNames ) - - local RemoveGroupNamesArray = ( type( RemoveGroupNames ) == "table" ) and RemoveGroupNames or { RemoveGroupNames } - for RemoveGroupID, RemoveGroupName in pairs( RemoveGroupNamesArray ) do - self:Remove( RemoveGroupName.GroupName ) + --- Adds a @{Core.Base#BASE} object in the @{Core.Set#SET_BASE}, using the Object Name as the index. + -- @param #SET_BASE self + -- @param Wrapper.Object#OBJECT Object + -- @return Core.Base#BASE The added BASE Object. + function SET_BASE:AddObject( Object ) + self:F2( Object.ObjectName ) + + self:T( Object.UnitName ) + self:T( Object.ObjectName ) + self:Add( Object.ObjectName, Object ) + end + + + + + --- Gets a @{Core.Base#BASE} object from the @{Core.Set#SET_BASE} and derived classes, based on the Object Name. + -- @param #SET_BASE self + -- @param #string ObjectName + -- @return Core.Base#BASE + function SET_BASE:Get( ObjectName ) + self:F( ObjectName ) + + local Object = self.Set[ObjectName] - return self -end - - - - ---- Finds a Group based on the Group Name. --- @param #SET_GROUP self --- @param #string GroupName --- @return Wrapper.Group#GROUP The found Group. -function SET_GROUP:FindGroup( GroupName ) - - local GroupFound = self.Set[GroupName] - return GroupFound -end - ---- Iterate the SET_GROUP while identifying the nearest object from a @{Core.Point#POINT_VEC2}. --- @param #SET_GROUP self --- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest object in the set. --- @return Wrapper.Group#GROUP The closest group. -function SET_GROUP:FindNearestGroupFromPointVec2( PointVec2 ) - self:F2( PointVec2 ) + self:T3( { ObjectName, Object } ) + return Object + end - local NearestGroup = nil --Wrapper.Group#GROUP - local ClosestDistance = nil + --- Gets the first object from the @{Core.Set#SET_BASE} and derived classes. + -- @param #SET_BASE self + -- @return Core.Base#BASE + function SET_BASE:GetFirst() - for ObjectID, ObjectData in pairs( self.Set ) do - if NearestGroup == nil then - NearestGroup = ObjectData - ClosestDistance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() ) - else - local Distance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() ) - if Distance < ClosestDistance then - NearestGroup = ObjectData - ClosestDistance = Distance + local ObjectName = self.Index[1] + local FirstObject = self.Set[ObjectName] + self:T3( { FirstObject } ) + return FirstObject + end + + --- Gets the last object from the @{Core.Set#SET_BASE} and derived classes. + -- @param #SET_BASE self + -- @return Core.Base#BASE + function SET_BASE:GetLast() + + local ObjectName = self.Index[#self.Index] + local LastObject = self.Set[ObjectName] + self:T3( { LastObject } ) + return LastObject + end + + --- Gets a random object from the @{Core.Set#SET_BASE} and derived classes. + -- @param #SET_BASE self + -- @return Core.Base#BASE + function SET_BASE:GetRandom() + + local RandomItem = self.Set[self.Index[math.random(#self.Index)]] + self:T3( { RandomItem } ) + return RandomItem + end + + + --- Retrieves the amount of objects in the @{Core.Set#SET_BASE} and derived classes. + -- @param #SET_BASE self + -- @return #number Count + function SET_BASE:Count() + + return self.Index and #self.Index or 0 + end + + + --- Copies the Filter criteria from a given Set (for rebuilding a new Set based on an existing Set). + -- @param #SET_BASE self + -- @param #SET_BASE BaseSet + -- @return #SET_BASE + function SET_BASE:SetDatabase( BaseSet ) + + -- Copy the filter criteria of the BaseSet + local OtherFilter = routines.utils.deepCopy( BaseSet.Filter ) + self.Filter = OtherFilter + + -- Now base the new Set on the BaseSet + self.Database = BaseSet:GetSet() + return self + end + + + + --- Define the SET iterator **"yield interval"** and the **"time interval"**. + -- @param #SET_BASE self + -- @param #number YieldInterval Sets the frequency when the iterator loop will yield after the number of objects processed. The default frequency is 10 objects processed. + -- @param #number TimeInterval Sets the time in seconds when the main logic will resume the iterator loop. The default time is 0.001 seconds. + -- @return #SET_BASE self + function SET_BASE:SetIteratorIntervals( YieldInterval, TimeInterval ) + + self.YieldInterval = YieldInterval + self.TimeInterval = TimeInterval + + return self + end + + + --- Filters for the defined collection. + -- @param #SET_BASE self + -- @return #SET_BASE self + function SET_BASE:FilterOnce() + + for ObjectName, Object in pairs( self.Database ) do + + if self:IsIncludeObject( Object ) then + self:Add( ObjectName, Object ) end end + + return self end - return NearestGroup -end - - ---- Builds a set of groups of coalitions. --- Possible current coalitions are red, blue and neutral. --- @param #SET_GROUP self --- @param #string Coalitions Can take the following values: "red", "blue", "neutral". --- @return #SET_GROUP self -function SET_GROUP: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 groups out of categories. --- Possible current categories are plane, helicopter, ground, ship. --- @param #SET_GROUP self --- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". --- @return #SET_GROUP self -function SET_GROUP: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 groups out of ground category. --- @param #SET_GROUP self --- @return #SET_GROUP self -function SET_GROUP:FilterCategoryGround() - self:FilterCategories( "ground" ) - return self -end - ---- Builds a set of groups out of airplane category. --- @param #SET_GROUP self --- @return #SET_GROUP self -function SET_GROUP:FilterCategoryAirplane() - self:FilterCategories( "plane" ) - return self -end - ---- Builds a set of groups out of helicopter category. --- @param #SET_GROUP self --- @return #SET_GROUP self -function SET_GROUP:FilterCategoryHelicopter() - self:FilterCategories( "helicopter" ) - return self -end - ---- Builds a set of groups out of ship category. --- @param #SET_GROUP self --- @return #SET_GROUP self -function SET_GROUP:FilterCategoryShip() - self:FilterCategories( "ship" ) - return self -end - ---- Builds a set of groups out of structure category. --- @param #SET_GROUP self --- @return #SET_GROUP self -function SET_GROUP:FilterCategoryStructure() - self:FilterCategories( "structure" ) - return self -end - - - ---- Builds a set of groups of defined countries. --- Possible current countries are those known within DCS world. --- @param #SET_GROUP self --- @param #string Countries Can take those country strings known within DCS world. --- @return #SET_GROUP self -function SET_GROUP: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 groups of defined GROUP prefixes. --- All the groups starting with the given prefixes will be included within the set. --- @param #SET_GROUP self --- @param #string Prefixes The prefix of which the group name starts with. --- @return #SET_GROUP self -function SET_GROUP:FilterPrefixes( Prefixes ) - if not self.Filter.GroupPrefixes then - self.Filter.GroupPrefixes = {} - end - if type( Prefixes ) ~= "table" then - Prefixes = { Prefixes } - end - for PrefixID, Prefix in pairs( Prefixes ) do - self.Filter.GroupPrefixes[Prefix] = Prefix - end - return self -end - ---- Builds a set of groups that are only active. --- Only the groups that are active will be included within the set. --- @param #SET_GROUP self --- @param #boolean Active (optional) Include only active groups to the set. --- Include inactive groups if you provide false. --- @return #SET_GROUP self --- @usage --- --- -- Include only active groups to the set. --- GroupSet = SET_GROUP:New():FilterActive():FilterStart() --- --- -- Include only active groups to the set of the blue coalition, and filter one time. --- GroupSet = SET_GROUP:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() --- --- -- Include only active groups to the set of the blue coalition, and filter one time. --- -- Later, reset to include back inactive groups to the set. --- GroupSet = SET_GROUP:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() --- ... logic ... --- GroupSet = SET_GROUP:New():FilterActive( false ):FilterCoalition( "blue" ):FilterOnce() --- -function SET_GROUP:FilterActive( Active ) - Active = Active or not ( Active == false ) - self.Filter.Active = Active - return self -end + --- Starts the filtering for the defined collection. + -- @param #SET_BASE self + -- @return #SET_BASE self + function SET_BASE:_FilterStart() + + for ObjectName, Object in pairs( self.Database ) do + + if self:IsIncludeObject( Object ) then + self:E( { "Adding Object:", ObjectName } ) + self:Add( ObjectName, Object ) + end + end + + -- Follow alive players and clients + --self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit ) + --self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventOnPlayerLeaveUnit ) + + + return self + end + + --- Starts the filtering of the Dead events for the collection. + -- @param #SET_BASE self + -- @return #SET_BASE self + function SET_BASE:FilterDeads() --R2.1 allow deads to be filtered to automatically handle deads in the collection. - ---- Starts the filtering. --- @param #SET_GROUP self --- @return #SET_GROUP self -function SET_GROUP:FilterStart() - - if _DATABASE then - self:_FilterStart() - self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) + + return self + end + + --- Starts the filtering of the Crash events for the collection. + -- @param #SET_BASE self + -- @return #SET_BASE self + function SET_BASE:FilterCrashes() --R2.1 allow crashes to be filtered to automatically handle crashes in the collection. + self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash ) + + return self + end + + --- Stops the filtering for the defined collection. + -- @param #SET_BASE self + -- @return #SET_BASE self + function SET_BASE:FilterStop() + + self:UnHandleEvent( EVENTS.Birth ) + self:UnHandleEvent( EVENTS.Dead ) + self:UnHandleEvent( EVENTS.Crash ) + + return self + end + + --- Iterate the SET_BASE while identifying the nearest object from a @{Core.Point#POINT_VEC2}. + -- @param #SET_BASE self + -- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest object in the set. + -- @return Core.Base#BASE The closest object. + function SET_BASE:FindNearestObjectFromPointVec2( PointVec2 ) + self:F2( PointVec2 ) + + local NearestObject = nil + local ClosestDistance = nil + + for ObjectID, ObjectData in pairs( self.Set ) do + if NearestObject == nil then + NearestObject = ObjectData + ClosestDistance = PointVec2:DistanceFromVec2( ObjectData:GetVec2() ) + else + local Distance = PointVec2:DistanceFromVec2( ObjectData:GetVec2() ) + if Distance < ClosestDistance then + NearestObject = ObjectData + ClosestDistance = Distance + end + end + end + + return NearestObject end - return self -end - ---- Handles the OnDead or OnCrash event for alive groups set. --- Note: The GROUP object in the SET_GROUP collection will only be removed if the last unit is destroyed of the GROUP. --- @param #SET_GROUP self --- @param Core.Event#EVENTDATA Event -function SET_GROUP:_EventOnDeadOrCrash( Event ) - self:F( { Event } ) - - if Event.IniDCSUnit then - local ObjectName, Object = self:FindInDatabase( Event ) - if ObjectName then - if Event.IniDCSGroup:getSize() == 1 then -- Only remove if the last unit of the group was destroyed. + ----- Private method that registers all alive players in the mission. + ---- @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 ) } + -- for CoalitionId, CoalitionData in pairs( CoalitionsData ) do + -- for UnitId, UnitData in pairs( CoalitionData ) do + -- self:T3( { "UnitData:", UnitData } ) + -- if UnitData and UnitData:isExist() then + -- local UnitName = UnitData:getName() + -- if not self.PlayersAlive[UnitName] then + -- self:E( { "Add player for unit:", UnitName, UnitData:getPlayerName() } ) + -- self.PlayersAlive[UnitName] = UnitData:getPlayerName() + -- end + -- end + -- end + -- end + -- + -- return self + --end + + --- Events + + --- Handles the OnBirth event for the Set. + -- @param #SET_BASE self + -- @param Core.Event#EVENTDATA Event + function SET_BASE:_EventOnBirth( Event ) + self:F3( { Event } ) + + if Event.IniDCSUnit then + local ObjectName, Object = self:AddInDatabase( Event ) + self:T3( ObjectName, Object ) + if Object and self:IsIncludeObject( Object ) then + self:Add( ObjectName, Object ) + --self:_EventOnPlayerEnterUnit( Event ) + end + end + end + + --- Handles the OnDead or OnCrash event for alive units set. + -- @param #SET_BASE self + -- @param Core.Event#EVENTDATA Event + function SET_BASE:_EventOnDeadOrCrash( Event ) + self:F( { Event } ) + + if Event.IniDCSUnit then + local ObjectName, Object = self:FindInDatabase( Event ) + if ObjectName then self:Remove( ObjectName ) end end end -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_GROUP self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the GROUP --- @return #table The GROUP -function SET_GROUP:AddInDatabase( Event ) - self:F3( { Event } ) - - if Event.IniObjectCategory == 1 then - if not self.Database[Event.IniDCSGroupName] then - self.Database[Event.IniDCSGroupName] = GROUP:Register( Event.IniDCSGroupName ) - self:T3( self.Database[Event.IniDCSGroupName] ) - end - end - return Event.IniDCSGroupName, self.Database[Event.IniDCSGroupName] -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_GROUP self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the GROUP --- @return #table The GROUP -function SET_GROUP:FindInDatabase( Event ) - self:F3( { Event } ) - - return Event.IniDCSGroupName, self.Database[Event.IniDCSGroupName] -end - ---- Iterate the SET_GROUP and call an iterator function for each GROUP object, providing the GROUP and optional parameters. --- @param #SET_GROUP self --- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. --- @return #SET_GROUP self -function SET_GROUP:ForEachGroup( IteratorFunction, ... ) - self:F2( arg ) + --- Handles the OnPlayerEnterUnit event to fill the active players table (with the unit filter applied). + -- @param #SET_BASE self + -- @param Core.Event#EVENTDATA Event + --function SET_BASE:_EventOnPlayerEnterUnit( Event ) + -- self:F3( { Event } ) + -- + -- if Event.IniDCSUnit then + -- local ObjectName, Object = self:AddInDatabase( Event ) + -- self:T3( ObjectName, Object ) + -- if self:IsIncludeObject( Object ) then + -- self:Add( ObjectName, Object ) + -- --self:_EventOnPlayerEnterUnit( Event ) + -- end + -- end + --end - self:ForEach( IteratorFunction, arg, self:GetSet() ) - - return self -end - ---- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP object, providing the GROUP and optional parameters. --- @param #SET_GROUP self --- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. --- @return #SET_GROUP self -function SET_GROUP:ForEachGroupAlive( IteratorFunction, ... ) - self:F2( arg ) + --- Handles the OnPlayerLeaveUnit event to clean the active players table. + -- @param #SET_BASE self + -- @param Core.Event#EVENTDATA Event + --function SET_BASE:_EventOnPlayerLeaveUnit( Event ) + -- self:F3( { Event } ) + -- + -- local ObjectName = Event.IniDCSUnit + -- if Event.IniDCSUnit then + -- if Event.IniDCSGroup then + -- local GroupUnits = Event.IniDCSGroup:getUnits() + -- local PlayerCount = 0 + -- for _, DCSUnit in pairs( GroupUnits ) do + -- if DCSUnit ~= Event.IniDCSUnit then + -- if DCSUnit:getPlayerName() ~= nil then + -- PlayerCount = PlayerCount + 1 + -- end + -- end + -- end + -- self:E(PlayerCount) + -- if PlayerCount == 0 then + -- self:Remove( Event.IniDCSGroupName ) + -- end + -- end + -- end + --end - self:ForEach( IteratorFunction, arg, self:GetAliveSet() ) - - return self -end - ---- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. --- @param #SET_GROUP 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 GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. --- @return #SET_GROUP self -function SET_GROUP:ForEachGroupCompletelyInZone( ZoneObject, IteratorFunction, ... ) - self:F2( arg ) + -- Iterators - self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Group#GROUP GroupObject - function( ZoneObject, GroupObject ) - if GroupObject:IsCompletelyInZone( ZoneObject ) then - return true - else - return false + --- Iterate the SET_BASE and derived classes and call an iterator function for the given SET_BASE, providing the Object for each element within the set and optional parameters. + -- @param #SET_BASE self + -- @param #function IteratorFunction The function that will be called. + -- @return #SET_BASE self + function SET_BASE:ForEach( IteratorFunction, arg, Set, Function, FunctionArguments ) + self:F3( arg ) + + Set = Set or self:GetSet() + arg = arg or {} + + local function CoRoutine() + local Count = 0 + for ObjectID, ObjectData in pairs( Set ) do + local Object = ObjectData + self:T3( Object ) + if Function then + if Function( unpack( FunctionArguments ), Object ) == true then + IteratorFunction( Object, unpack( arg ) ) + end + else + IteratorFunction( Object, unpack( arg ) ) + end + Count = Count + 1 + -- if Count % self.YieldInterval == 0 then + -- coroutine.yield( false ) + -- end end - end, { ZoneObject } ) - - return self -end - ---- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence partly in a @{Zone}, providing the GROUP and optional parameters to the called function. --- @param #SET_GROUP 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 GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. --- @return #SET_GROUP self -function SET_GROUP:ForEachGroupPartlyInZone( ZoneObject, IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Group#GROUP GroupObject - function( ZoneObject, GroupObject ) - if GroupObject:IsPartlyInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) - - return self -end - ---- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. --- @param #SET_GROUP 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 GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. --- @return #SET_GROUP self -function SET_GROUP:ForEachGroupNotInZone( ZoneObject, IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @param Core.Zone#ZONE_BASE ZoneObject - -- @param Wrapper.Group#GROUP GroupObject - function( ZoneObject, GroupObject ) - if GroupObject:IsNotInZone( ZoneObject ) then - return true - else - return false - end - end, { ZoneObject } ) - - return self -end - ---- Iterate the SET_GROUP and return true if all the @{Wrapper.Group#GROUP} are completely in the @{Core.Zone#ZONE} --- @param #SET_GROUP self --- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. --- @return #boolean true if all the @{Wrapper.Group#GROUP} are completly in the @{Core.Zone#ZONE}, false otherwise --- @usage --- local MyZone = ZONE:New("Zone1") --- local MySetGroup = SET_GROUP:New() --- MySetGroup:AddGroupsByName({"Group1", "Group2"}) --- --- if MySetGroup:AllCompletelyInZone(MyZone) then --- MESSAGE:New("All the SET's GROUP are in zone !", 10):ToAll() --- else --- MESSAGE:New("Some or all SET's GROUP are outside zone !", 10):ToAll() --- end -function SET_GROUP:AllCompletelyInZone(Zone) - self:F2(Zone) - local Set = self:GetSet() - for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP - if not GroupData:IsCompletelyInZone(Zone) then - return false - end - end - return true -end - ---- Iterate the SET_GROUP and return true if at least one of the @{Wrapper.Group#GROUP} is completely inside the @{Core.Zone#ZONE} --- @param #SET_GROUP self --- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. --- @return #boolean true if at least one of the @{Wrapper.Group#GROUP} is completly inside the @{Core.Zone#ZONE}, false otherwise. --- @usage --- local MyZone = ZONE:New("Zone1") --- local MySetGroup = SET_GROUP:New() --- MySetGroup:AddGroupsByName({"Group1", "Group2"}) --- --- if MySetGroup:AnyCompletelyInZone(MyZone) then --- MESSAGE:New("At least one GROUP is completely in zone !", 10):ToAll() --- else --- MESSAGE:New("No GROUP is completely in zone !", 10):ToAll() --- end -function SET_GROUP:AnyCompletelyInZone(Zone) - self:F2(Zone) - local Set = self:GetSet() - for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP - if GroupData:IsCompletelyInZone(Zone) then return true end - end - return false -end - ---- Iterate the SET_GROUP and return true if at least one @{#UNIT} of one @{GROUP} of the @{SET_GROUP} is in @{ZONE} --- @param #SET_GROUP self --- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. --- @return #boolean true if at least one of the @{Wrapper.Group#GROUP} is partly or completly inside the @{Core.Zone#ZONE}, false otherwise. --- @usage --- local MyZone = ZONE:New("Zone1") --- local MySetGroup = SET_GROUP:New() --- MySetGroup:AddGroupsByName({"Group1", "Group2"}) --- --- if MySetGroup:AnyPartlyInZone(MyZone) then --- MESSAGE:New("At least one GROUP has at least one UNIT in zone !", 10):ToAll() --- else --- MESSAGE:New("No UNIT of any GROUP is in zone !", 10):ToAll() --- end -function SET_GROUP:AnyInZone(Zone) - self:F2(Zone) - local Set = self:GetSet() - for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP - if GroupData:IsPartlyInZone(Zone) or GroupData:IsCompletelyInZone(Zone) then - return true - end - end - return false -end - ---- Iterate the SET_GROUP and return true if at least one @{GROUP} of the @{SET_GROUP} is partly in @{ZONE}. --- Will return false if a @{GROUP} is fully in the @{ZONE} --- @param #SET_GROUP self --- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. --- @return #boolean true if at least one of the @{Wrapper.Group#GROUP} is partly or completly inside the @{Core.Zone#ZONE}, false otherwise. --- @usage --- local MyZone = ZONE:New("Zone1") --- local MySetGroup = SET_GROUP:New() --- MySetGroup:AddGroupsByName({"Group1", "Group2"}) --- --- if MySetGroup:AnyPartlyInZone(MyZone) then --- MESSAGE:New("At least one GROUP is partially in the zone, but none are fully in it !", 10):ToAll() --- else --- MESSAGE:New("No GROUP are in zone, or one (or more) GROUP is completely in it !", 10):ToAll() --- end -function SET_GROUP:AnyPartlyInZone(Zone) - self:F2(Zone) - local IsPartlyInZone = false - local Set = self:GetSet() - for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP - if GroupData:IsCompletelyInZone(Zone) then + + -- local co = coroutine.create( CoRoutine ) + local co = CoRoutine + + local function Schedule() + + -- local status, res = coroutine.resume( co ) + local status, res = co() + self:T3( { status, res } ) + + if status == false then + error( res ) + end + if res == false then + return true -- resume next time the loop + end + return false - elseif GroupData:IsPartlyInZone(Zone) then - IsPartlyInZone = true -- at least one GROUP is partly in zone end + + --self.CallScheduler:Schedule( self, Schedule, {}, self.TimeInterval, self.TimeInterval, 0 ) + Schedule() + + return self end - if IsPartlyInZone then + + ----- Iterate the SET_BASE and call an interator 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. + ---- @return #SET_BASE self + --function SET_BASE:ForEachDCSUnitAlive( IteratorFunction, ... ) + -- self:F3( arg ) + -- + -- self:ForEach( IteratorFunction, arg, self.DCSUnitsAlive ) + -- + -- return self + --end + -- + ----- Iterate the SET_BASE and call an interator 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. + ---- @return #SET_BASE self + --function SET_BASE:ForEachPlayer( IteratorFunction, ... ) + -- self:F3( arg ) + -- + -- self:ForEach( IteratorFunction, arg, self.PlayersAlive ) + -- + -- return self + --end + -- + -- + ----- Iterate the SET_BASE and call an interator 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. + ---- @return #SET_BASE self + --function SET_BASE:ForEachClient( IteratorFunction, ... ) + -- self:F3( arg ) + -- + -- self:ForEach( IteratorFunction, arg, self.Clients ) + -- + -- return self + --end + + + --- Decides whether to include the Object + -- @param #SET_BASE self + -- @param #table Object + -- @return #SET_BASE self + function SET_BASE:IsIncludeObject( Object ) + self:F3( Object ) + return true - else - return false - end -end - ---- Iterate the SET_GROUP and return true if no @{GROUP} of the @{SET_GROUP} is in @{ZONE} --- This could also be achieved with `not SET_GROUP:AnyPartlyInZone(Zone)`, but it's easier for the --- mission designer to add a dedicated method --- @param #SET_GROUP self --- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. --- @return #boolean true if no @{Wrapper.Group#GROUP} is inside the @{Core.Zone#ZONE} in any way, false otherwise. --- @usage --- local MyZone = ZONE:New("Zone1") --- local MySetGroup = SET_GROUP:New() --- MySetGroup:AddGroupsByName({"Group1", "Group2"}) --- --- if MySetGroup:NoneInZone(MyZone) then --- MESSAGE:New("No GROUP is completely in zone !", 10):ToAll() --- else --- MESSAGE:New("No UNIT of any GROUP is in zone !", 10):ToAll() --- end -function SET_GROUP:NoneInZone(Zone) - self:F2(Zone) - local Set = self:GetSet() - for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP - if not GroupData:IsNotInZone(Zone) then -- If the GROUP is in Zone in any way - return false - end - end - return true -end - ---- Iterate the SET_GROUP and count how many GROUPs are completely in the Zone --- That could easily be done with SET_GROUP:ForEachGroupCompletelyInZone(), but this function --- provides an easy to use shortcut... --- @param #SET_GROUP self --- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. --- @return #number the number of GROUPs completely in the Zone --- @usage --- local MyZone = ZONE:New("Zone1") --- local MySetGroup = SET_GROUP:New() --- MySetGroup:AddGroupsByName({"Group1", "Group2"}) --- --- MESSAGE:New("There are " .. MySetGroup:CountInZone(MyZone) .. " GROUPs in the Zone !", 10):ToAll() -function SET_GROUP:CountInZone(Zone) - self:F2(Zone) - local Count = 0 - local Set = self:GetSet() - for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP - if GroupData:IsCompletelyInZone(Zone) then - Count = Count + 1 - end - end - return Count -end - ---- Iterate the SET_GROUP and count how many UNITs are completely in the Zone --- @param #SET_GROUP self --- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. --- @return #number the number of GROUPs completely in the Zone --- @usage --- local MyZone = ZONE:New("Zone1") --- local MySetGroup = SET_GROUP:New() --- MySetGroup:AddGroupsByName({"Group1", "Group2"}) --- --- MESSAGE:New("There are " .. MySetGroup:CountUnitInZone(MyZone) .. " UNITs in the Zone !", 10):ToAll() -function SET_GROUP:CountUnitInZone(Zone) - self:F2(Zone) - local Count = 0 - local Set = self:GetSet() - for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP - Count = Count + GroupData:CountInZone(Zone) - end - return Count -end - ------ Iterate the SET_GROUP and call an interator 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. ----- @return #SET_GROUP self ---function SET_GROUP:ForEachPlayer( IteratorFunction, ... ) --- self:F2( arg ) --- --- self:ForEach( IteratorFunction, arg, self.PlayersAlive ) --- --- return self ---end --- --- ------ Iterate the SET_GROUP and call an interator 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. ----- @return #SET_GROUP self ---function SET_GROUP:ForEachClient( IteratorFunction, ... ) --- self:F2( arg ) --- --- self:ForEach( IteratorFunction, arg, self.Clients ) --- --- return self ---end - - ---- --- @param #SET_GROUP self --- @param Wrapper.Group#GROUP MGroup The group that is checked for inclusion. --- @return #SET_GROUP self -function SET_GROUP:IsIncludeObject( MGroup ) - self:F2( MGroup ) - local MGroupInclude = true - - if self.Filter.Active ~= nil then - local MGroupActive = false - self:F( { Active = self.Filter.Active } ) - if self.Filter.Active == false or ( self.Filter.Active == true and MGroup:IsActive() == true ) then - MGroupActive = true - end - MGroupInclude = MGroupInclude and MGroupActive end - if self.Filter.Coalitions then - local MGroupCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - self:T3( { "Coalition:", MGroup:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) - if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == MGroup:GetCoalition() then - MGroupCoalition = true - end + --- Gets a string with all the object names. + -- @param #SET_BASE self + -- @return #string A string with the names of the objects. + function SET_BASE:GetObjectNames() + self:F3() + + local ObjectNames = "" + for ObjectName, Object in pairs( self.Set ) do + ObjectNames = ObjectNames .. ObjectName .. ", " end - MGroupInclude = MGroupInclude and MGroupCoalition + + return ObjectNames end - if self.Filter.Categories then - local MGroupCategory = false - for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - self:T3( { "Category:", MGroup:GetCategory(), self.FilterMeta.Categories[CategoryName], CategoryName } ) - if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == MGroup:GetCategory() then - MGroupCategory = true - end - end - MGroupInclude = MGroupInclude and MGroupCategory - end + --- Flushes the current SET_BASE contents in the log ... (for debugging reasons). + -- @param #SET_BASE self + -- @param Core.Base#BASE MasterObject (optional) The master object as a reference. + -- @return #string A string with the names of the objects. + function SET_BASE:Flush( MasterObject ) + self:F3() - if self.Filter.Countries then - local MGroupCountry = false - for CountryID, CountryName in pairs( self.Filter.Countries ) do - self:T3( { "Country:", MGroup:GetCountry(), CountryName } ) - if country.id[CountryName] == MGroup:GetCountry() then - MGroupCountry = true - end + local ObjectNames = "" + for ObjectName, Object in pairs( self.Set ) do + ObjectNames = ObjectNames .. ObjectName .. ", " end - MGroupInclude = MGroupInclude and MGroupCountry + self:F( { MasterObject = MasterObject and MasterObject:GetClassNameAndID(), "Objects in Set:", ObjectNames } ) + + return ObjectNames end - if self.Filter.GroupPrefixes then - local MGroupPrefix = false - for GroupPrefixId, GroupPrefix in pairs( self.Filter.GroupPrefixes ) do - self:T3( { "Prefix:", string.find( MGroup:GetName(), GroupPrefix, 1 ), GroupPrefix } ) - if string.find( MGroup:GetName(), GroupPrefix:gsub ("-", "%%-"), 1 ) then - MGroupPrefix = true - end - end - MGroupInclude = MGroupInclude and MGroupPrefix - end - - self:T2( MGroupInclude ) - return MGroupInclude end ---- Iterate the SET_GROUP and set for each unit the default cargo bay weight limit. --- Because within a group, the type of carriers can differ, each cargo bay weight limit is set on @{Wrapper.Unit} level. --- @param #SET_GROUP self --- @usage --- -- Set the default cargo bay weight limits of the carrier units. --- local MySetGroup = SET_GROUP:New() --- MySetGroup:SetCargoBayWeightLimit() -function SET_GROUP:SetCargoBayWeightLimit() - local Set = self:GetSet() - for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP - for UnitName, UnitData in pairs( GroupData:GetUnits() ) do - --local UnitData = UnitData -- Wrapper.Unit#UNIT +do -- SET_GROUP + + --- @type SET_GROUP + -- @extends Core.Set#SET_BASE + + --- Mission designers can use the @{Core.Set#SET_GROUP} class to build sets of groups belonging to certain: + -- + -- * Coalitions + -- * Categories + -- * Countries + -- * Starting with certain prefix strings. + -- + -- ## SET_GROUP constructor + -- + -- Create a new SET_GROUP object with the @{#SET_GROUP.New} method: + -- + -- * @{#SET_GROUP.New}: Creates a new SET_GROUP object. + -- + -- ## Add or Remove GROUP(s) from SET_GROUP + -- + -- GROUPS can be added and removed using the @{Core.Set#SET_GROUP.AddGroupsByName} and @{Core.Set#SET_GROUP.RemoveGroupsByName} respectively. + -- These methods take a single GROUP name or an array of GROUP names to be added or removed from SET_GROUP. + -- + -- ## SET_GROUP filter criteria + -- + -- You can set filter criteria to define the set of groups within the SET_GROUP. + -- Filter criteria are defined by: + -- + -- * @{#SET_GROUP.FilterCoalitions}: Builds the SET_GROUP with the groups belonging to the coalition(s). + -- * @{#SET_GROUP.FilterCategories}: Builds the SET_GROUP with the groups belonging to the category(ies). + -- * @{#SET_GROUP.FilterCountries}: Builds the SET_GROUP with the gruops belonging to the country(ies). + -- * @{#SET_GROUP.FilterPrefixes}: Builds the SET_GROUP with the groups starting with the same prefix string(s). + -- * @{#SET_GROUP.FilterActive}: Builds the SET_GROUP with the groups that are only active. Groups that are inactive (late activation) won't be included in the set! + -- + -- For the Category Filter, extra methods have been added: + -- + -- * @{#SET_GROUP.FilterCategoryAirplane}: Builds the SET_GROUP from airplanes. + -- * @{#SET_GROUP.FilterCategoryHelicopter}: Builds the SET_GROUP from helicopters. + -- * @{#SET_GROUP.FilterCategoryGround}: Builds the SET_GROUP from ground vehicles or infantry. + -- * @{#SET_GROUP.FilterCategoryShip}: Builds the SET_GROUP from ships. + -- * @{#SET_GROUP.FilterCategoryStructure}: Builds the SET_GROUP from structures. + -- + -- + -- Once the filter criteria have been set for the SET_GROUP, you can start filtering using: + -- + -- * @{#SET_GROUP.FilterStart}: Starts the filtering of the groups within the SET_GROUP and add or remove GROUP objects **dynamically**. + -- * @{#SET_GROUP.FilterOnce}: Filters of the groups **once**. + -- + -- Planned filter criteria within development are (so these are not yet available): + -- + -- * @{#SET_GROUP.FilterZones}: Builds the SET_GROUP with the groups within a @{Core.Zone#ZONE}. + -- + -- ## SET_GROUP iterators + -- + -- Once the filters have been defined and the SET_GROUP has been built, you can iterate the SET_GROUP with the available iterator methods. + -- The iterator methods will walk the SET_GROUP set, and call for each element within the set a function that you provide. + -- The following iterator methods are currently available within the SET_GROUP: + -- + -- * @{#SET_GROUP.ForEachGroup}: Calls a function for each alive group it finds within the SET_GROUP. + -- * @{#SET_GROUP.ForEachGroupCompletelyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. + -- * @{#SET_GROUP.ForEachGroupPartlyInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence partly in a @{Zone}, providing the GROUP and optional parameters to the called function. + -- * @{#SET_GROUP.ForEachGroupNotInZone}: Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. + -- + -- + -- ## SET_GROUP trigger events on the GROUP objects. + -- + -- The SET is derived from the FSM class, which provides extra capabilities to track the contents of the GROUP objects in the SET_GROUP. + -- + -- ### When a GROUP object crashes or is dead, the SET_GROUP will trigger a **Dead** event. + -- + -- You can handle the event using the OnBefore and OnAfter event handlers. + -- The event handlers need to have the paramters From, Event, To, GroupObject. + -- The GroupObject is the GROUP object that is dead and within the SET_GROUP, and is passed as a parameter to the event handler. + -- See the following example: + -- + -- -- Create the SetCarrier SET_GROUP collection. + -- + -- local SetHelicopter = SET_GROUP:New():FilterPrefixes( "Helicopter" ):FilterStart() + -- + -- -- Put a Dead event handler on SetCarrier, to ensure that when a carrier is destroyed, that all internal parameters are reset. + -- + -- function SetHelicopter:OnAfterDead( From, Event, To, GroupObject ) + -- self:F( { GroupObject = GroupObject:GetName() } ) + -- end + -- + -- While this is a good example, there is a catch. + -- Imageine you want to execute the code above, the the self would need to be from the object declared outside (above) the OnAfterDead method. + -- So, the self would need to contain another object. Fortunately, this can be done, but you must use then the **`.`** notation for the method. + -- See the modified example: + -- + -- -- Now we have a constructor of the class AI_CARGO_DISPATCHER, that receives the SetHelicopter as a parameter. + -- -- Within that constructor, we want to set an enclosed event handler OnAfterDead for SetHelicopter. + -- -- But within the OnAfterDead method, we want to refer to the self variable of the AI_CARGO_DISPATCHER. + -- + -- function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZones ) + -- + -- local self = BASE:Inherit( self, FSM:New() ) -- #AI_CARGO_DISPATCHER + -- + -- -- Put a Dead event handler on SetCarrier, to ensure that when a carrier is destroyed, that all internal parameters are reset. + -- -- Note the "." notation, and the explicit declaration of SetHelicopter, which would be using the ":" notation the implicit self variable declaration. + -- + -- function SetHelicopter.OnAfterDead( SetHelicopter, From, Event, To, GroupObject ) + -- SetHelicopter:F( { GroupObject = GroupObject:GetName() } ) + -- self.PickupCargo[GroupObject] = nil -- So here I clear the PickupCargo table entry of the self object AI_CARGO_DISPATCHER. + -- self.CarrierHome[GroupObject] = nil + -- end + -- + -- end + -- + -- === + -- @field #SET_GROUP SET_GROUP + SET_GROUP = { + ClassName = "SET_GROUP", + Filter = { + Coalitions = nil, + Categories = nil, + Countries = nil, + GroupPrefixes = nil, + }, + FilterMeta = { + Coalitions = { + red = coalition.side.RED, + blue = coalition.side.BLUE, + neutral = coalition.side.NEUTRAL, + }, + Categories = { + plane = Group.Category.AIRPLANE, + helicopter = Group.Category.HELICOPTER, + ground = Group.Category.GROUND, -- R2.2 + ship = Group.Category.SHIP, + structure = Group.Category.STRUCTURE, + }, + }, + } + + + --- Creates a new SET_GROUP object, building a set of groups belonging to a coalitions, categories, countries, types or with defined prefix names. + -- @param #SET_GROUP self + -- @return #SET_GROUP + -- @usage + -- -- Define a new SET_GROUP Object. This DBObject will contain a reference to all alive GROUPS. + -- DBObject = SET_GROUP:New() + function SET_GROUP:New() + + -- Inherits from BASE + local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.GROUPS ) ) -- #SET_GROUP + + self:FilterActive( false ) + + return self + end + + --- Gets the Set. + -- @param #SET_GROUP self + -- @return #SET_GROUP self + function SET_GROUP:GetAliveSet() + self:F2() + + local AliveSet = SET_GROUP:New() + + -- Clean the Set before returning with only the alive Groups. + for GroupName, GroupObject in pairs( self.Set ) do + local GroupObject=GroupObject --Wrapper.Group#GROUP + if GroupObject then + if GroupObject:IsAlive() then + AliveSet:Add( GroupName, GroupObject ) + end + end + end + + return AliveSet.Set or {} + end + + --- Add a GROUP to SET_GROUP. + -- Note that for each unit in the group that is set, a default cargo bay limit is initialized. + -- @param Core.Set#SET_GROUP self + -- @param Wrapper.Group#GROUP group The group which should be added to the set. + -- @return self + function SET_GROUP:AddGroup( group ) + + self:Add( group:GetName(), group ) + + -- I set the default cargo bay weight limit each time a new group is added to the set. + for UnitID, UnitData in pairs( group:GetUnits() ) do UnitData:SetCargoBayWeightLimit() end + + return self end + + --- Add GROUP(s) to SET_GROUP. + -- @param Core.Set#SET_GROUP self + -- @param #string AddGroupNames A single name or an array of GROUP names. + -- @return self + function SET_GROUP:AddGroupsByName( AddGroupNames ) + + local AddGroupNamesArray = ( type( AddGroupNames ) == "table" ) and AddGroupNames or { AddGroupNames } + + for AddGroupID, AddGroupName in pairs( AddGroupNamesArray ) do + self:Add( AddGroupName, GROUP:FindByName( AddGroupName ) ) + end + + return self + end + + --- Remove GROUP(s) from SET_GROUP. + -- @param Core.Set#SET_GROUP self + -- @param Wrapper.Group#GROUP RemoveGroupNames A single name or an array of GROUP names. + -- @return self + function SET_GROUP:RemoveGroupsByName( RemoveGroupNames ) + + local RemoveGroupNamesArray = ( type( RemoveGroupNames ) == "table" ) and RemoveGroupNames or { RemoveGroupNames } + + for RemoveGroupID, RemoveGroupName in pairs( RemoveGroupNamesArray ) do + self:Remove( RemoveGroupName.GroupName ) + end + + return self + end + + + + + --- Finds a Group based on the Group Name. + -- @param #SET_GROUP self + -- @param #string GroupName + -- @return Wrapper.Group#GROUP The found Group. + function SET_GROUP:FindGroup( GroupName ) + + local GroupFound = self.Set[GroupName] + return GroupFound + end + + --- Iterate the SET_GROUP while identifying the nearest object from a @{Core.Point#POINT_VEC2}. + -- @param #SET_GROUP self + -- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest object in the set. + -- @return Wrapper.Group#GROUP The closest group. + function SET_GROUP:FindNearestGroupFromPointVec2( PointVec2 ) + self:F2( PointVec2 ) + + local NearestGroup = nil --Wrapper.Group#GROUP + local ClosestDistance = nil + + for ObjectID, ObjectData in pairs( self.Set ) do + if NearestGroup == nil then + NearestGroup = ObjectData + ClosestDistance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() ) + else + local Distance = PointVec2:DistanceFromPointVec2( ObjectData:GetCoordinate() ) + if Distance < ClosestDistance then + NearestGroup = ObjectData + ClosestDistance = Distance + end + end + end + + return NearestGroup + end + + + --- Builds a set of groups of coalitions. + -- Possible current coalitions are red, blue and neutral. + -- @param #SET_GROUP self + -- @param #string Coalitions Can take the following values: "red", "blue", "neutral". + -- @return #SET_GROUP self + function SET_GROUP: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 groups out of categories. + -- Possible current categories are plane, helicopter, ground, ship. + -- @param #SET_GROUP self + -- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". + -- @return #SET_GROUP self + function SET_GROUP: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 groups out of ground category. + -- @param #SET_GROUP self + -- @return #SET_GROUP self + function SET_GROUP:FilterCategoryGround() + self:FilterCategories( "ground" ) + return self + end + + --- Builds a set of groups out of airplane category. + -- @param #SET_GROUP self + -- @return #SET_GROUP self + function SET_GROUP:FilterCategoryAirplane() + self:FilterCategories( "plane" ) + return self + end + + --- Builds a set of groups out of helicopter category. + -- @param #SET_GROUP self + -- @return #SET_GROUP self + function SET_GROUP:FilterCategoryHelicopter() + self:FilterCategories( "helicopter" ) + return self + end + + --- Builds a set of groups out of ship category. + -- @param #SET_GROUP self + -- @return #SET_GROUP self + function SET_GROUP:FilterCategoryShip() + self:FilterCategories( "ship" ) + return self + end + + --- Builds a set of groups out of structure category. + -- @param #SET_GROUP self + -- @return #SET_GROUP self + function SET_GROUP:FilterCategoryStructure() + self:FilterCategories( "structure" ) + return self + end + + + + --- Builds a set of groups of defined countries. + -- Possible current countries are those known within DCS world. + -- @param #SET_GROUP self + -- @param #string Countries Can take those country strings known within DCS world. + -- @return #SET_GROUP self + function SET_GROUP: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 groups of defined GROUP prefixes. + -- All the groups starting with the given prefixes will be included within the set. + -- @param #SET_GROUP self + -- @param #string Prefixes The prefix of which the group name starts with. + -- @return #SET_GROUP self + function SET_GROUP:FilterPrefixes( Prefixes ) + if not self.Filter.GroupPrefixes then + self.Filter.GroupPrefixes = {} + end + if type( Prefixes ) ~= "table" then + Prefixes = { Prefixes } + end + for PrefixID, Prefix in pairs( Prefixes ) do + self.Filter.GroupPrefixes[Prefix] = Prefix + end + return self + end + + --- Builds a set of groups that are only active. + -- Only the groups that are active will be included within the set. + -- @param #SET_GROUP self + -- @param #boolean Active (optional) Include only active groups to the set. + -- Include inactive groups if you provide false. + -- @return #SET_GROUP self + -- @usage + -- + -- -- Include only active groups to the set. + -- GroupSet = SET_GROUP:New():FilterActive():FilterStart() + -- + -- -- Include only active groups to the set of the blue coalition, and filter one time. + -- GroupSet = SET_GROUP:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() + -- + -- -- Include only active groups to the set of the blue coalition, and filter one time. + -- -- Later, reset to include back inactive groups to the set. + -- GroupSet = SET_GROUP:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() + -- ... logic ... + -- GroupSet = SET_GROUP:New():FilterActive( false ):FilterCoalition( "blue" ):FilterOnce() + -- + function SET_GROUP:FilterActive( Active ) + Active = Active or not ( Active == false ) + self.Filter.Active = Active + return self + end + + + --- Starts the filtering. + -- @param #SET_GROUP self + -- @return #SET_GROUP self + function SET_GROUP:FilterStart() + + if _DATABASE then + self:_FilterStart() + self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) + self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) + self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) + self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash ) + end + + + + return self + end + + --- Handles the OnDead or OnCrash event for alive groups set. + -- Note: The GROUP object in the SET_GROUP collection will only be removed if the last unit is destroyed of the GROUP. + -- @param #SET_GROUP self + -- @param Core.Event#EVENTDATA Event + function SET_GROUP:_EventOnDeadOrCrash( Event ) + self:F( { Event } ) + + if Event.IniDCSUnit then + local ObjectName, Object = self:FindInDatabase( Event ) + if ObjectName then + if Event.IniDCSGroup:getSize() == 1 then -- Only remove if the last unit of the group was destroyed. + self:Remove( ObjectName ) + end + end + end + 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_GROUP self + -- @param Core.Event#EVENTDATA Event + -- @return #string The name of the GROUP + -- @return #table The GROUP + function SET_GROUP:AddInDatabase( Event ) + self:F3( { Event } ) + + if Event.IniObjectCategory == 1 then + if not self.Database[Event.IniDCSGroupName] then + self.Database[Event.IniDCSGroupName] = GROUP:Register( Event.IniDCSGroupName ) + self:T3( self.Database[Event.IniDCSGroupName] ) + end + end + + return Event.IniDCSGroupName, self.Database[Event.IniDCSGroupName] + 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_GROUP self + -- @param Core.Event#EVENTDATA Event + -- @return #string The name of the GROUP + -- @return #table The GROUP + function SET_GROUP:FindInDatabase( Event ) + self:F3( { Event } ) + + return Event.IniDCSGroupName, self.Database[Event.IniDCSGroupName] + end + + --- Iterate the SET_GROUP and call an iterator function for each GROUP object, providing the GROUP and optional parameters. + -- @param #SET_GROUP self + -- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. + -- @return #SET_GROUP self + function SET_GROUP:ForEachGroup( IteratorFunction, ... ) + self:F2( arg ) + + self:ForEach( IteratorFunction, arg, self:GetSet() ) + + return self + end + + --- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP object, providing the GROUP and optional parameters. + -- @param #SET_GROUP self + -- @param #function IteratorFunction The function that will be called when there is an alive GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. + -- @return #SET_GROUP self + function SET_GROUP:ForEachGroupAlive( IteratorFunction, ... ) + self:F2( arg ) + + self:ForEach( IteratorFunction, arg, self:GetAliveSet() ) + + return self + end + + --- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence completely in a @{Zone}, providing the GROUP and optional parameters to the called function. + -- @param #SET_GROUP 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 GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. + -- @return #SET_GROUP self + function SET_GROUP:ForEachGroupCompletelyInZone( ZoneObject, IteratorFunction, ... ) + self:F2( arg ) + + self:ForEach( IteratorFunction, arg, self:GetSet(), + --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Wrapper.Group#GROUP GroupObject + function( ZoneObject, GroupObject ) + if GroupObject:IsCompletelyInZone( ZoneObject ) then + return true + else + return false + end + end, { ZoneObject } ) + + return self + end + + --- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence partly in a @{Zone}, providing the GROUP and optional parameters to the called function. + -- @param #SET_GROUP 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 GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. + -- @return #SET_GROUP self + function SET_GROUP:ForEachGroupPartlyInZone( ZoneObject, IteratorFunction, ... ) + self:F2( arg ) + + self:ForEach( IteratorFunction, arg, self:GetSet(), + --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Wrapper.Group#GROUP GroupObject + function( ZoneObject, GroupObject ) + if GroupObject:IsPartlyInZone( ZoneObject ) then + return true + else + return false + end + end, { ZoneObject } ) + + return self + end + + --- Iterate the SET_GROUP and call an iterator function for each **alive** GROUP presence not in a @{Zone}, providing the GROUP and optional parameters to the called function. + -- @param #SET_GROUP 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 GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. + -- @return #SET_GROUP self + function SET_GROUP:ForEachGroupNotInZone( ZoneObject, IteratorFunction, ... ) + self:F2( arg ) + + self:ForEach( IteratorFunction, arg, self:GetSet(), + --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Wrapper.Group#GROUP GroupObject + function( ZoneObject, GroupObject ) + if GroupObject:IsNotInZone( ZoneObject ) then + return true + else + return false + end + end, { ZoneObject } ) + + return self + end + + --- Iterate the SET_GROUP and return true if all the @{Wrapper.Group#GROUP} are completely in the @{Core.Zone#ZONE} + -- @param #SET_GROUP self + -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. + -- @return #boolean true if all the @{Wrapper.Group#GROUP} are completly in the @{Core.Zone#ZONE}, false otherwise + -- @usage + -- local MyZone = ZONE:New("Zone1") + -- local MySetGroup = SET_GROUP:New() + -- MySetGroup:AddGroupsByName({"Group1", "Group2"}) + -- + -- if MySetGroup:AllCompletelyInZone(MyZone) then + -- MESSAGE:New("All the SET's GROUP are in zone !", 10):ToAll() + -- else + -- MESSAGE:New("Some or all SET's GROUP are outside zone !", 10):ToAll() + -- end + function SET_GROUP:AllCompletelyInZone(Zone) + self:F2(Zone) + local Set = self:GetSet() + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if not GroupData:IsCompletelyInZone(Zone) then + return false + end + end + return true + end + + --- Iterate the SET_GROUP and return true if at least one of the @{Wrapper.Group#GROUP} is completely inside the @{Core.Zone#ZONE} + -- @param #SET_GROUP self + -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. + -- @return #boolean true if at least one of the @{Wrapper.Group#GROUP} is completly inside the @{Core.Zone#ZONE}, false otherwise. + -- @usage + -- local MyZone = ZONE:New("Zone1") + -- local MySetGroup = SET_GROUP:New() + -- MySetGroup:AddGroupsByName({"Group1", "Group2"}) + -- + -- if MySetGroup:AnyCompletelyInZone(MyZone) then + -- MESSAGE:New("At least one GROUP is completely in zone !", 10):ToAll() + -- else + -- MESSAGE:New("No GROUP is completely in zone !", 10):ToAll() + -- end + function SET_GROUP:AnyCompletelyInZone(Zone) + self:F2(Zone) + local Set = self:GetSet() + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if GroupData:IsCompletelyInZone(Zone) then + return true + end + end + return false + end + + --- Iterate the SET_GROUP and return true if at least one @{#UNIT} of one @{GROUP} of the @{SET_GROUP} is in @{ZONE} + -- @param #SET_GROUP self + -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. + -- @return #boolean true if at least one of the @{Wrapper.Group#GROUP} is partly or completly inside the @{Core.Zone#ZONE}, false otherwise. + -- @usage + -- local MyZone = ZONE:New("Zone1") + -- local MySetGroup = SET_GROUP:New() + -- MySetGroup:AddGroupsByName({"Group1", "Group2"}) + -- + -- if MySetGroup:AnyPartlyInZone(MyZone) then + -- MESSAGE:New("At least one GROUP has at least one UNIT in zone !", 10):ToAll() + -- else + -- MESSAGE:New("No UNIT of any GROUP is in zone !", 10):ToAll() + -- end + function SET_GROUP:AnyInZone(Zone) + self:F2(Zone) + local Set = self:GetSet() + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if GroupData:IsPartlyInZone(Zone) or GroupData:IsCompletelyInZone(Zone) then + return true + end + end + return false + end + + --- Iterate the SET_GROUP and return true if at least one @{GROUP} of the @{SET_GROUP} is partly in @{ZONE}. + -- Will return false if a @{GROUP} is fully in the @{ZONE} + -- @param #SET_GROUP self + -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. + -- @return #boolean true if at least one of the @{Wrapper.Group#GROUP} is partly or completly inside the @{Core.Zone#ZONE}, false otherwise. + -- @usage + -- local MyZone = ZONE:New("Zone1") + -- local MySetGroup = SET_GROUP:New() + -- MySetGroup:AddGroupsByName({"Group1", "Group2"}) + -- + -- if MySetGroup:AnyPartlyInZone(MyZone) then + -- MESSAGE:New("At least one GROUP is partially in the zone, but none are fully in it !", 10):ToAll() + -- else + -- MESSAGE:New("No GROUP are in zone, or one (or more) GROUP is completely in it !", 10):ToAll() + -- end + function SET_GROUP:AnyPartlyInZone(Zone) + self:F2(Zone) + local IsPartlyInZone = false + local Set = self:GetSet() + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if GroupData:IsCompletelyInZone(Zone) then + return false + elseif GroupData:IsPartlyInZone(Zone) then + IsPartlyInZone = true -- at least one GROUP is partly in zone + end + end + + if IsPartlyInZone then + return true + else + return false + end + end + + --- Iterate the SET_GROUP and return true if no @{GROUP} of the @{SET_GROUP} is in @{ZONE} + -- This could also be achieved with `not SET_GROUP:AnyPartlyInZone(Zone)`, but it's easier for the + -- mission designer to add a dedicated method + -- @param #SET_GROUP self + -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. + -- @return #boolean true if no @{Wrapper.Group#GROUP} is inside the @{Core.Zone#ZONE} in any way, false otherwise. + -- @usage + -- local MyZone = ZONE:New("Zone1") + -- local MySetGroup = SET_GROUP:New() + -- MySetGroup:AddGroupsByName({"Group1", "Group2"}) + -- + -- if MySetGroup:NoneInZone(MyZone) then + -- MESSAGE:New("No GROUP is completely in zone !", 10):ToAll() + -- else + -- MESSAGE:New("No UNIT of any GROUP is in zone !", 10):ToAll() + -- end + function SET_GROUP:NoneInZone(Zone) + self:F2(Zone) + local Set = self:GetSet() + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if not GroupData:IsNotInZone(Zone) then -- If the GROUP is in Zone in any way + return false + end + end + return true + end + + --- Iterate the SET_GROUP and count how many GROUPs are completely in the Zone + -- That could easily be done with SET_GROUP:ForEachGroupCompletelyInZone(), but this function + -- provides an easy to use shortcut... + -- @param #SET_GROUP self + -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. + -- @return #number the number of GROUPs completely in the Zone + -- @usage + -- local MyZone = ZONE:New("Zone1") + -- local MySetGroup = SET_GROUP:New() + -- MySetGroup:AddGroupsByName({"Group1", "Group2"}) + -- + -- MESSAGE:New("There are " .. MySetGroup:CountInZone(MyZone) .. " GROUPs in the Zone !", 10):ToAll() + function SET_GROUP:CountInZone(Zone) + self:F2(Zone) + local Count = 0 + local Set = self:GetSet() + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + if GroupData:IsCompletelyInZone(Zone) then + Count = Count + 1 + end + end + return Count + end + + --- Iterate the SET_GROUP and count how many UNITs are completely in the Zone + -- @param #SET_GROUP self + -- @param Core.Zone#ZONE ZoneObject The Zone to be tested for. + -- @return #number the number of GROUPs completely in the Zone + -- @usage + -- local MyZone = ZONE:New("Zone1") + -- local MySetGroup = SET_GROUP:New() + -- MySetGroup:AddGroupsByName({"Group1", "Group2"}) + -- + -- MESSAGE:New("There are " .. MySetGroup:CountUnitInZone(MyZone) .. " UNITs in the Zone !", 10):ToAll() + function SET_GROUP:CountUnitInZone(Zone) + self:F2(Zone) + local Count = 0 + local Set = self:GetSet() + for GroupID, GroupData in pairs(Set) do -- For each GROUP in SET_GROUP + Count = Count + GroupData:CountInZone(Zone) + end + return Count + end + + ----- Iterate the SET_GROUP and call an interator 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. + ---- @return #SET_GROUP self + --function SET_GROUP:ForEachPlayer( IteratorFunction, ... ) + -- self:F2( arg ) + -- + -- self:ForEach( IteratorFunction, arg, self.PlayersAlive ) + -- + -- return self + --end + -- + -- + ----- Iterate the SET_GROUP and call an interator 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. + ---- @return #SET_GROUP self + --function SET_GROUP:ForEachClient( IteratorFunction, ... ) + -- self:F2( arg ) + -- + -- self:ForEach( IteratorFunction, arg, self.Clients ) + -- + -- return self + --end + + + --- + -- @param #SET_GROUP self + -- @param Wrapper.Group#GROUP MGroup The group that is checked for inclusion. + -- @return #SET_GROUP self + function SET_GROUP:IsIncludeObject( MGroup ) + self:F2( MGroup ) + local MGroupInclude = true + + if self.Filter.Active ~= nil then + local MGroupActive = false + self:F( { Active = self.Filter.Active } ) + if self.Filter.Active == false or ( self.Filter.Active == true and MGroup:IsActive() == true ) then + MGroupActive = true + end + MGroupInclude = MGroupInclude and MGroupActive + end + + if self.Filter.Coalitions then + local MGroupCoalition = false + for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do + self:T3( { "Coalition:", MGroup:GetCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == MGroup:GetCoalition() then + MGroupCoalition = true + end + end + MGroupInclude = MGroupInclude and MGroupCoalition + end + + if self.Filter.Categories then + local MGroupCategory = false + for CategoryID, CategoryName in pairs( self.Filter.Categories ) do + self:T3( { "Category:", MGroup:GetCategory(), self.FilterMeta.Categories[CategoryName], CategoryName } ) + if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == MGroup:GetCategory() then + MGroupCategory = true + end + end + MGroupInclude = MGroupInclude and MGroupCategory + end + + if self.Filter.Countries then + local MGroupCountry = false + for CountryID, CountryName in pairs( self.Filter.Countries ) do + self:T3( { "Country:", MGroup:GetCountry(), CountryName } ) + if country.id[CountryName] == MGroup:GetCountry() then + MGroupCountry = true + end + end + MGroupInclude = MGroupInclude and MGroupCountry + end + + if self.Filter.GroupPrefixes then + local MGroupPrefix = false + for GroupPrefixId, GroupPrefix in pairs( self.Filter.GroupPrefixes ) do + self:T3( { "Prefix:", string.find( MGroup:GetName(), GroupPrefix, 1 ), GroupPrefix } ) + if string.find( MGroup:GetName(), GroupPrefix:gsub ("-", "%%-"), 1 ) then + MGroupPrefix = true + end + end + MGroupInclude = MGroupInclude and MGroupPrefix + end + + self:T2( MGroupInclude ) + return MGroupInclude + end + + + --- Iterate the SET_GROUP and set for each unit the default cargo bay weight limit. + -- Because within a group, the type of carriers can differ, each cargo bay weight limit is set on @{Wrapper.Unit} level. + -- @param #SET_GROUP self + -- @usage + -- -- Set the default cargo bay weight limits of the carrier units. + -- local MySetGroup = SET_GROUP:New() + -- MySetGroup:SetCargoBayWeightLimit() + function SET_GROUP:SetCargoBayWeightLimit() + local Set = self:GetSet() + for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP + for UnitName, UnitData in pairs( GroupData:GetUnits() ) do + --local UnitData = UnitData -- Wrapper.Unit#UNIT + UnitData:SetCargoBayWeightLimit() + end + end + end + end @@ -3206,1937 +3214,1984 @@ do -- SET_STATIC end ---- SET_CLIENT ---- @type SET_CLIENT --- @extends Core.Set#SET_BASE +do -- SET_CLIENT - ---- Mission designers can use the @{Core.Set#SET_CLIENT} class to build sets of units belonging to certain: --- --- * Coalitions --- * Categories --- * Countries --- * Client types --- * Starting with certain prefix strings. --- --- ## 1) SET_CLIENT constructor --- --- Create a new SET_CLIENT object with the @{#SET_CLIENT.New} method: --- --- * @{#SET_CLIENT.New}: Creates a new SET_CLIENT object. --- --- ## 2) Add or Remove CLIENT(s) from SET_CLIENT --- --- CLIENTs can be added and removed using the @{Core.Set#SET_CLIENT.AddClientsByName} and @{Core.Set#SET_CLIENT.RemoveClientsByName} respectively. --- These methods take a single CLIENT name or an array of CLIENT names to be added or removed from SET_CLIENT. --- --- ## 3) SET_CLIENT filter criteria --- --- You can set filter criteria to define the set of clients within the SET_CLIENT. --- Filter criteria are defined by: --- --- * @{#SET_CLIENT.FilterCoalitions}: Builds the SET_CLIENT with the clients belonging to the coalition(s). --- * @{#SET_CLIENT.FilterCategories}: Builds the SET_CLIENT with the clients belonging to the category(ies). --- * @{#SET_CLIENT.FilterTypes}: Builds the SET_CLIENT with the clients belonging to the client type(s). --- * @{#SET_CLIENT.FilterCountries}: Builds the SET_CLIENT with the clients belonging to the country(ies). --- * @{#SET_CLIENT.FilterPrefixes}: Builds the SET_CLIENT with the clients starting with the same prefix string(s). --- * @{#SET_CLIENT.FilterActive}: Builds the SET_CLIENT with the units that are only active. Units that are inactive (late activation) won't be included in the set! --- --- Once the filter criteria have been set for the SET_CLIENT, you can start filtering using: --- --- * @{#SET_CLIENT.FilterStart}: Starts the filtering of the clients **dynamically**. --- * @{#SET_CLIENT.FilterOnce}: Filters the clients **once**. --- --- Planned filter criteria within development are (so these are not yet available): --- --- * @{#SET_CLIENT.FilterZones}: Builds the SET_CLIENT with the clients within a @{Core.Zone#ZONE}. --- --- ## 4) SET_CLIENT iterators --- --- Once the filters have been defined and the SET_CLIENT has been built, you can iterate the SET_CLIENT with the available iterator methods. --- The iterator methods will walk the SET_CLIENT set, and call for each element within the set a function that you provide. --- The following iterator methods are currently available within the SET_CLIENT: --- --- * @{#SET_CLIENT.ForEachClient}: Calls a function for each alive client it finds within the SET_CLIENT. --- --- === --- @field #SET_CLIENT SET_CLIENT -SET_CLIENT = { - ClassName = "SET_CLIENT", - 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, + --- @type SET_CLIENT + -- @extends Core.Set#SET_BASE + + + + --- Mission designers can use the @{Core.Set#SET_CLIENT} class to build sets of units belonging to certain: + -- + -- * Coalitions + -- * Categories + -- * Countries + -- * Client types + -- * Starting with certain prefix strings. + -- + -- ## 1) SET_CLIENT constructor + -- + -- Create a new SET_CLIENT object with the @{#SET_CLIENT.New} method: + -- + -- * @{#SET_CLIENT.New}: Creates a new SET_CLIENT object. + -- + -- ## 2) Add or Remove CLIENT(s) from SET_CLIENT + -- + -- CLIENTs can be added and removed using the @{Core.Set#SET_CLIENT.AddClientsByName} and @{Core.Set#SET_CLIENT.RemoveClientsByName} respectively. + -- These methods take a single CLIENT name or an array of CLIENT names to be added or removed from SET_CLIENT. + -- + -- ## 3) SET_CLIENT filter criteria + -- + -- You can set filter criteria to define the set of clients within the SET_CLIENT. + -- Filter criteria are defined by: + -- + -- * @{#SET_CLIENT.FilterCoalitions}: Builds the SET_CLIENT with the clients belonging to the coalition(s). + -- * @{#SET_CLIENT.FilterCategories}: Builds the SET_CLIENT with the clients belonging to the category(ies). + -- * @{#SET_CLIENT.FilterTypes}: Builds the SET_CLIENT with the clients belonging to the client type(s). + -- * @{#SET_CLIENT.FilterCountries}: Builds the SET_CLIENT with the clients belonging to the country(ies). + -- * @{#SET_CLIENT.FilterPrefixes}: Builds the SET_CLIENT with the clients starting with the same prefix string(s). + -- * @{#SET_CLIENT.FilterActive}: Builds the SET_CLIENT with the units that are only active. Units that are inactive (late activation) won't be included in the set! + -- + -- Once the filter criteria have been set for the SET_CLIENT, you can start filtering using: + -- + -- * @{#SET_CLIENT.FilterStart}: Starts the filtering of the clients **dynamically**. + -- * @{#SET_CLIENT.FilterOnce}: Filters the clients **once**. + -- + -- Planned filter criteria within development are (so these are not yet available): + -- + -- * @{#SET_CLIENT.FilterZones}: Builds the SET_CLIENT with the clients within a @{Core.Zone#ZONE}. + -- + -- ## 4) SET_CLIENT iterators + -- + -- Once the filters have been defined and the SET_CLIENT has been built, you can iterate the SET_CLIENT with the available iterator methods. + -- The iterator methods will walk the SET_CLIENT set, and call for each element within the set a function that you provide. + -- The following iterator methods are currently available within the SET_CLIENT: + -- + -- * @{#SET_CLIENT.ForEachClient}: Calls a function for each alive client it finds within the SET_CLIENT. + -- + -- === + -- @field #SET_CLIENT SET_CLIENT + SET_CLIENT = { + ClassName = "SET_CLIENT", + Clients = {}, + Filter = { + Coalitions = nil, + Categories = nil, + Types = nil, + Countries = nil, + ClientPrefixes = nil, }, - Categories = { - plane = Unit.Category.AIRPLANE, - helicopter = Unit.Category.HELICOPTER, - ground = Unit.Category.GROUND_UNIT, - ship = Unit.Category.SHIP, - structure = Unit.Category.STRUCTURE, + 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_CLIENT object, building a set of clients belonging to a coalitions, categories, countries, types or with defined prefix names. --- @param #SET_CLIENT self --- @return #SET_CLIENT --- @usage --- -- Define a new SET_CLIENT Object. This DBObject will contain a reference to all Clients. --- DBObject = SET_CLIENT:New() -function SET_CLIENT:New() - -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.CLIENTS ) ) -- #SET_CLIENT - - self:FilterActive( false ) - - return self -end - ---- Add CLIENT(s) to SET_CLIENT. --- @param Core.Set#SET_CLIENT self --- @param #string AddClientNames A single name or an array of CLIENT names. --- @return self -function SET_CLIENT:AddClientsByName( AddClientNames ) - - local AddClientNamesArray = ( type( AddClientNames ) == "table" ) and AddClientNames or { AddClientNames } + } - for AddClientID, AddClientName in pairs( AddClientNamesArray ) do - self:Add( AddClientName, CLIENT:FindByName( AddClientName ) ) + + --- Creates a new SET_CLIENT object, building a set of clients belonging to a coalitions, categories, countries, types or with defined prefix names. + -- @param #SET_CLIENT self + -- @return #SET_CLIENT + -- @usage + -- -- Define a new SET_CLIENT Object. This DBObject will contain a reference to all Clients. + -- DBObject = SET_CLIENT:New() + function SET_CLIENT:New() + -- Inherits from BASE + local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.CLIENTS ) ) -- #SET_CLIENT + + self:FilterActive( false ) + + return self end + + --- Add CLIENT(s) to SET_CLIENT. + -- @param Core.Set#SET_CLIENT self + -- @param #string AddClientNames A single name or an array of CLIENT names. + -- @return self + function SET_CLIENT:AddClientsByName( AddClientNames ) + + local AddClientNamesArray = ( type( AddClientNames ) == "table" ) and AddClientNames or { AddClientNames } - return self -end - ---- 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. --- @return self -function SET_CLIENT: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 Client Name. --- @param #SET_CLIENT self --- @param #string ClientName --- @return Wrapper.Client#CLIENT The found Client. -function SET_CLIENT:FindClient( ClientName ) - - local ClientFound = self.Set[ClientName] - return ClientFound -end - - - ---- Builds a set of clients of coalitions. --- Possible current coalitions are red, blue and neutral. --- @param #SET_CLIENT self --- @param #string Coalitions Can take the following values: "red", "blue", "neutral". --- @return #SET_CLIENT self -function SET_CLIENT: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. --- Possible current categories are plane, helicopter, ground, ship. --- @param #SET_CLIENT self --- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". --- @return #SET_CLIENT self -function SET_CLIENT: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. --- Possible current types are those types known within DCS world. --- @param #SET_CLIENT self --- @param #string Types Can take those type strings known within DCS world. --- @return #SET_CLIENT self -function SET_CLIENT: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_CLIENT self --- @param #string Countries Can take those country strings known within DCS world. --- @return #SET_CLIENT self -function SET_CLIENT: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_CLIENT self --- @param #string Prefixes The prefix of which the client name starts with. --- @return #SET_CLIENT self -function SET_CLIENT: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 - ---- Builds a set of clients that are only active. --- Only the clients that are active will be included within the set. --- @param #SET_CLIENT self --- @param #boolean Active (optional) Include only active clients to the set. --- Include inactive clients if you provide false. --- @return #SET_CLIENT self --- @usage --- --- -- Include only active clients to the set. --- ClientSet = SET_CLIENT:New():FilterActive():FilterStart() --- --- -- Include only active clients to the set of the blue coalition, and filter one time. --- ClientSet = SET_CLIENT:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() --- --- -- Include only active clients to the set of the blue coalition, and filter one time. --- -- Later, reset to include back inactive clients to the set. --- ClientSet = SET_CLIENT:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() --- ... logic ... --- ClientSet = SET_CLIENT:New():FilterActive( false ):FilterCoalition( "blue" ):FilterOnce() --- -function SET_CLIENT:FilterActive( Active ) - Active = Active or not ( Active == false ) - self.Filter.Active = Active - return self -end - - - ---- Starts the filtering. --- @param #SET_CLIENT self --- @return #SET_CLIENT self -function SET_CLIENT:FilterStart() - - if _DATABASE then - self:_FilterStart() - self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) - 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_CLIENT self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the CLIENT --- @return #table The CLIENT -function SET_CLIENT: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_CLIENT self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the CLIENT --- @return #table The CLIENT -function SET_CLIENT:FindInDatabase( Event ) - self:F3( { Event } ) - - return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] -end - ---- Iterate the SET_CLIENT and call an interator function for each **alive** CLIENT, providing the CLIENT and optional parameters. --- @param #SET_CLIENT self --- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_CLIENT. The function needs to accept a CLIENT parameter. --- @return #SET_CLIENT self -function SET_CLIENT:ForEachClient( IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self:GetSet() ) - - return self -end - ---- Iterate the SET_CLIENT 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_CLIENT 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_CLIENT. The function needs to accept a CLIENT parameter. --- @return #SET_CLIENT self -function SET_CLIENT:ForEachClientInZone( ZoneObject, IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @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_CLIENT 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_CLIENT 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_CLIENT. The function needs to accept a CLIENT parameter. --- @return #SET_CLIENT self -function SET_CLIENT:ForEachClientNotInZone( ZoneObject, IteratorFunction, ... ) - self:F2( arg ) - - self:ForEach( IteratorFunction, arg, self:GetSet(), - --- @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_CLIENT self --- @param Wrapper.Client#CLIENT MClient --- @return #SET_CLIENT self -function SET_CLIENT:IsIncludeObject( MClient ) - self:F2( MClient ) - - local MClientInclude = true - - if MClient then - local MClientName = MClient.UnitName - - if self.Filter.Active ~= nil then - local MClientActive = false - if self.Filter.Active == false or ( self.Filter.Active == true and MClient:IsActive() == true ) then - MClientActive = true - end - MClientInclude = MClientInclude and MClientActive - end - - 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 - ---- 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: --- --- ## SET_PLAYER constructor --- --- Create a new SET_PLAYER object with the @{#SET_PLAYER.New} method: --- --- * @{#SET_PLAYER.New}: Creates a new SET_PLAYER object. --- --- ## 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 @{Core.Zone#ZONE}. --- --- ## 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() - self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) - self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) - self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) - 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:GetSet() ) - - 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:GetSet(), - --- @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:GetSet(), - --- @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 - ---- Mission designers can use the @{Core.Set#SET_AIRBASE} class to build sets of airbases optionally belonging to certain: --- --- * Coalitions --- --- ## SET_AIRBASE constructor --- --- Create a new SET_AIRBASE object with the @{#SET_AIRBASE.New} method: --- --- * @{#SET_AIRBASE.New}: Creates a new SET_AIRBASE object. --- --- ## Add or Remove AIRBASEs from SET_AIRBASE --- --- AIRBASEs can be added and removed using the @{Core.Set#SET_AIRBASE.AddAirbasesByName} and @{Core.Set#SET_AIRBASE.RemoveAirbasesByName} respectively. --- These methods take a single AIRBASE name or an array of AIRBASE names to be added or removed from SET_AIRBASE. --- --- ## SET_AIRBASE filter criteria --- --- You can set filter criteria to define the set of clients within the SET_AIRBASE. --- Filter criteria are defined by: --- --- * @{#SET_AIRBASE.FilterCoalitions}: Builds the SET_AIRBASE with the airbases belonging to the coalition(s). --- --- Once the filter criteria have been set for the SET_AIRBASE, you can start filtering using: --- --- * @{#SET_AIRBASE.FilterStart}: Starts the filtering of the airbases within the SET_AIRBASE. --- --- ## SET_AIRBASE iterators --- --- Once the filters have been defined and the SET_AIRBASE has been built, you can iterate the SET_AIRBASE with the available iterator methods. --- The iterator methods will walk the SET_AIRBASE set, and call for each airbase within the set a function that you provide. --- The following iterator methods are currently available within the SET_AIRBASE: --- --- * @{#SET_AIRBASE.ForEachAirbase}: Calls a function for each airbase it finds within the SET_AIRBASE. --- --- === --- @field #SET_AIRBASE SET_AIRBASE -SET_AIRBASE = { - ClassName = "SET_AIRBASE", - Airbases = {}, - Filter = { - Coalitions = nil, - }, - FilterMeta = { - Coalitions = { - red = coalition.side.RED, - blue = coalition.side.BLUE, - neutral = coalition.side.NEUTRAL, - }, - Categories = { - airdrome = Airbase.Category.AIRDROME, - helipad = Airbase.Category.HELIPAD, - ship = Airbase.Category.SHIP, - }, - }, -} - - ---- Creates a new SET_AIRBASE object, building a set of airbases belonging to a coalitions and categories. --- @param #SET_AIRBASE self --- @return #SET_AIRBASE self --- @usage --- -- Define a new SET_AIRBASE Object. The DatabaseSet will contain a reference to all Airbases. --- DatabaseSet = SET_AIRBASE:New() -function SET_AIRBASE:New() - -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.AIRBASES ) ) - - return self -end - ---- Add an AIRBASE object to SET_AIRBASE. --- @param Core.Set#SET_AIRBASE self --- @param Wrapper.Airbase#AIRBASE airbase Airbase that should be added to the set. --- @return self -function SET_AIRBASE:AddAirbase( airbase ) - - self:Add( airbase:GetName(), airbase ) - - return self -end - ---- Add AIRBASEs to SET_AIRBASE. --- @param Core.Set#SET_AIRBASE self --- @param #string AddAirbaseNames A single name or an array of AIRBASE names. --- @return self -function SET_AIRBASE:AddAirbasesByName( AddAirbaseNames ) - - local AddAirbaseNamesArray = ( type( AddAirbaseNames ) == "table" ) and AddAirbaseNames or { AddAirbaseNames } - - for AddAirbaseID, AddAirbaseName in pairs( AddAirbaseNamesArray ) do - self:Add( AddAirbaseName, AIRBASE:FindByName( AddAirbaseName ) ) - end - - return self -end - ---- Remove AIRBASEs from SET_AIRBASE. --- @param Core.Set#SET_AIRBASE self --- @param Wrapper.Airbase#AIRBASE RemoveAirbaseNames A single name or an array of AIRBASE names. --- @return self -function SET_AIRBASE:RemoveAirbasesByName( RemoveAirbaseNames ) - - local RemoveAirbaseNamesArray = ( type( RemoveAirbaseNames ) == "table" ) and RemoveAirbaseNames or { RemoveAirbaseNames } - - for RemoveAirbaseID, RemoveAirbaseName in pairs( RemoveAirbaseNamesArray ) do - self:Remove( RemoveAirbaseName ) - end - - return self -end - - ---- Finds a Airbase based on the Airbase Name. --- @param #SET_AIRBASE self --- @param #string AirbaseName --- @return Wrapper.Airbase#AIRBASE The found Airbase. -function SET_AIRBASE:FindAirbase( AirbaseName ) - - local AirbaseFound = self.Set[AirbaseName] - return AirbaseFound -end - - ---- Finds an Airbase in range of a coordinate. --- @param #SET_AIRBASE self --- @param Core.Point#COORDINATE Coordinate --- @param #number Range --- @return Wrapper.Airbase#AIRBASE The found Airbase. -function SET_AIRBASE:FindAirbaseInRange( Coordinate, Range ) - - local AirbaseFound = nil - - for AirbaseName, AirbaseObject in pairs( self.Set ) do - - local AirbaseCoordinate = AirbaseObject:GetCoordinate() - local Distance = Coordinate:Get2DDistance( AirbaseCoordinate ) - - self:F({Distance=Distance}) - - if Distance <= Range then - AirbaseFound = AirbaseObject - break + for AddClientID, AddClientName in pairs( AddClientNamesArray ) do + self:Add( AddClientName, CLIENT:FindByName( AddClientName ) ) end + return self end - - return AirbaseFound -end - - ---- Finds a random Airbase in the set. --- @param #SET_AIRBASE self --- @return Wrapper.Airbase#AIRBASE The found Airbase. -function SET_AIRBASE:GetRandomAirbase() - - local RandomAirbase = self:GetRandom() - self:F( { RandomAirbase = RandomAirbase:GetName() } ) - - return RandomAirbase -end - - - ---- Builds a set of airbases of coalitions. --- Possible current coalitions are red, blue and neutral. --- @param #SET_AIRBASE self --- @param #string Coalitions Can take the following values: "red", "blue", "neutral". --- @return #SET_AIRBASE self -function SET_AIRBASE: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 airbases out of categories. --- Possible current categories are plane, helicopter, ground, ship. --- @param #SET_AIRBASE self --- @param #string Categories Can take the following values: "airdrome", "helipad", "ship". --- @return #SET_AIRBASE self -function SET_AIRBASE: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 - ---- Starts the filtering. --- @param #SET_AIRBASE self --- @return #SET_AIRBASE self -function SET_AIRBASE:FilterStart() - - if _DATABASE then - -- We use the BaseCaptured event, which is generated by DCS when a base got captured. - self:HandleEvent( EVENTS.BaseCaptured ) + --- 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. + -- @return self + function SET_CLIENT: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 Client Name. + -- @param #SET_CLIENT self + -- @param #string ClientName + -- @return Wrapper.Client#CLIENT The found Client. + function SET_CLIENT:FindClient( ClientName ) + + local ClientFound = self.Set[ClientName] + return ClientFound + end + + + + --- Builds a set of clients of coalitions. + -- Possible current coalitions are red, blue and neutral. + -- @param #SET_CLIENT self + -- @param #string Coalitions Can take the following values: "red", "blue", "neutral". + -- @return #SET_CLIENT self + function SET_CLIENT: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. + -- Possible current categories are plane, helicopter, ground, ship. + -- @param #SET_CLIENT self + -- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship". + -- @return #SET_CLIENT self + function SET_CLIENT: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. + -- Possible current types are those types known within DCS world. + -- @param #SET_CLIENT self + -- @param #string Types Can take those type strings known within DCS world. + -- @return #SET_CLIENT self + function SET_CLIENT: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_CLIENT self + -- @param #string Countries Can take those country strings known within DCS world. + -- @return #SET_CLIENT self + function SET_CLIENT: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_CLIENT self + -- @param #string Prefixes The prefix of which the client name starts with. + -- @return #SET_CLIENT self + function SET_CLIENT: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 + + --- Builds a set of clients that are only active. + -- Only the clients that are active will be included within the set. + -- @param #SET_CLIENT self + -- @param #boolean Active (optional) Include only active clients to the set. + -- Include inactive clients if you provide false. + -- @return #SET_CLIENT self + -- @usage + -- + -- -- Include only active clients to the set. + -- ClientSet = SET_CLIENT:New():FilterActive():FilterStart() + -- + -- -- Include only active clients to the set of the blue coalition, and filter one time. + -- ClientSet = SET_CLIENT:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() + -- + -- -- Include only active clients to the set of the blue coalition, and filter one time. + -- -- Later, reset to include back inactive clients to the set. + -- ClientSet = SET_CLIENT:New():FilterActive():FilterCoalition( "blue" ):FilterOnce() + -- ... logic ... + -- ClientSet = SET_CLIENT:New():FilterActive( false ):FilterCoalition( "blue" ):FilterOnce() + -- + function SET_CLIENT:FilterActive( Active ) + Active = Active or not ( Active == false ) + self.Filter.Active = Active + return self + end + + + + --- Starts the filtering. + -- @param #SET_CLIENT self + -- @return #SET_CLIENT self + function SET_CLIENT:FilterStart() + + if _DATABASE then + self:_FilterStart() + self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) + self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) + self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) + 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_CLIENT self + -- @param Core.Event#EVENTDATA Event + -- @return #string The name of the CLIENT + -- @return #table The CLIENT + function SET_CLIENT: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_CLIENT self + -- @param Core.Event#EVENTDATA Event + -- @return #string The name of the CLIENT + -- @return #table The CLIENT + function SET_CLIENT:FindInDatabase( Event ) + self:F3( { Event } ) + + return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] + end + + --- Iterate the SET_CLIENT and call an interator function for each **alive** CLIENT, providing the CLIENT and optional parameters. + -- @param #SET_CLIENT self + -- @param #function IteratorFunction The function that will be called when there is an alive CLIENT in the SET_CLIENT. The function needs to accept a CLIENT parameter. + -- @return #SET_CLIENT self + function SET_CLIENT:ForEachClient( IteratorFunction, ... ) + self:F2( arg ) + + self:ForEach( IteratorFunction, arg, self:GetSet() ) + + return self + end + + --- Iterate the SET_CLIENT 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_CLIENT 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_CLIENT. The function needs to accept a CLIENT parameter. + -- @return #SET_CLIENT self + function SET_CLIENT:ForEachClientInZone( ZoneObject, IteratorFunction, ... ) + self:F2( arg ) + + self:ForEach( IteratorFunction, arg, self:GetSet(), + --- @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_CLIENT 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_CLIENT 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_CLIENT. The function needs to accept a CLIENT parameter. + -- @return #SET_CLIENT self + function SET_CLIENT:ForEachClientNotInZone( ZoneObject, IteratorFunction, ... ) + self:F2( arg ) + + self:ForEach( IteratorFunction, arg, self:GetSet(), + --- @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_CLIENT self + -- @param Wrapper.Client#CLIENT MClient + -- @return #SET_CLIENT self + function SET_CLIENT:IsIncludeObject( MClient ) + self:F2( MClient ) + + local MClientInclude = true + + if MClient then + local MClientName = MClient.UnitName + + if self.Filter.Active ~= nil then + local MClientActive = false + if self.Filter.Active == false or ( self.Filter.Active == true and MClient:IsActive() == true ) then + MClientActive = true + end + MClientInclude = MClientInclude and MClientActive + end + + 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 - -- We initialize the first set. +end + + + + +do -- 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: + -- + -- ## SET_PLAYER constructor + -- + -- Create a new SET_PLAYER object with the @{#SET_PLAYER.New} method: + -- + -- * @{#SET_PLAYER.New}: Creates a new SET_PLAYER object. + -- + -- ## 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 @{Core.Zone#ZONE}. + -- + -- ## 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() + self:HandleEvent( EVENTS.Birth, self._EventOnBirth ) + self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash ) + self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash ) + 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:GetSet() ) + + 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:GetSet(), + --- @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:GetSet(), + --- @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 + +end + + + + +do -- 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: + -- + -- * Coalitions + -- + -- ## SET_AIRBASE constructor + -- + -- Create a new SET_AIRBASE object with the @{#SET_AIRBASE.New} method: + -- + -- * @{#SET_AIRBASE.New}: Creates a new SET_AIRBASE object. + -- + -- ## Add or Remove AIRBASEs from SET_AIRBASE + -- + -- AIRBASEs can be added and removed using the @{Core.Set#SET_AIRBASE.AddAirbasesByName} and @{Core.Set#SET_AIRBASE.RemoveAirbasesByName} respectively. + -- These methods take a single AIRBASE name or an array of AIRBASE names to be added or removed from SET_AIRBASE. + -- + -- ## SET_AIRBASE filter criteria + -- + -- You can set filter criteria to define the set of clients within the SET_AIRBASE. + -- Filter criteria are defined by: + -- + -- * @{#SET_AIRBASE.FilterCoalitions}: Builds the SET_AIRBASE with the airbases belonging to the coalition(s). + -- + -- Once the filter criteria have been set for the SET_AIRBASE, you can start filtering using: + -- + -- * @{#SET_AIRBASE.FilterStart}: Starts the filtering of the airbases within the SET_AIRBASE. + -- + -- ## SET_AIRBASE iterators + -- + -- Once the filters have been defined and the SET_AIRBASE has been built, you can iterate the SET_AIRBASE with the available iterator methods. + -- The iterator methods will walk the SET_AIRBASE set, and call for each airbase within the set a function that you provide. + -- The following iterator methods are currently available within the SET_AIRBASE: + -- + -- * @{#SET_AIRBASE.ForEachAirbase}: Calls a function for each airbase it finds within the SET_AIRBASE. + -- + -- === + -- @field #SET_AIRBASE SET_AIRBASE + SET_AIRBASE = { + ClassName = "SET_AIRBASE", + Airbases = {}, + Filter = { + Coalitions = nil, + }, + FilterMeta = { + Coalitions = { + red = coalition.side.RED, + blue = coalition.side.BLUE, + neutral = coalition.side.NEUTRAL, + }, + Categories = { + airdrome = Airbase.Category.AIRDROME, + helipad = Airbase.Category.HELIPAD, + ship = Airbase.Category.SHIP, + }, + }, + } + + + --- Creates a new SET_AIRBASE object, building a set of airbases belonging to a coalitions and categories. + -- @param #SET_AIRBASE self + -- @return #SET_AIRBASE self + -- @usage + -- -- Define a new SET_AIRBASE Object. The DatabaseSet will contain a reference to all Airbases. + -- DatabaseSet = SET_AIRBASE:New() + function SET_AIRBASE:New() + -- Inherits from BASE + local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.AIRBASES ) ) + + return self + end + + --- Add an AIRBASE object to SET_AIRBASE. + -- @param Core.Set#SET_AIRBASE self + -- @param Wrapper.Airbase#AIRBASE airbase Airbase that should be added to the set. + -- @return self + function SET_AIRBASE:AddAirbase( airbase ) + + self:Add( airbase:GetName(), airbase ) + + return self + end + + --- Add AIRBASEs to SET_AIRBASE. + -- @param Core.Set#SET_AIRBASE self + -- @param #string AddAirbaseNames A single name or an array of AIRBASE names. + -- @return self + function SET_AIRBASE:AddAirbasesByName( AddAirbaseNames ) + + local AddAirbaseNamesArray = ( type( AddAirbaseNames ) == "table" ) and AddAirbaseNames or { AddAirbaseNames } + + for AddAirbaseID, AddAirbaseName in pairs( AddAirbaseNamesArray ) do + self:Add( AddAirbaseName, AIRBASE:FindByName( AddAirbaseName ) ) + end + + return self + end + + --- Remove AIRBASEs from SET_AIRBASE. + -- @param Core.Set#SET_AIRBASE self + -- @param Wrapper.Airbase#AIRBASE RemoveAirbaseNames A single name or an array of AIRBASE names. + -- @return self + function SET_AIRBASE:RemoveAirbasesByName( RemoveAirbaseNames ) + + local RemoveAirbaseNamesArray = ( type( RemoveAirbaseNames ) == "table" ) and RemoveAirbaseNames or { RemoveAirbaseNames } + + for RemoveAirbaseID, RemoveAirbaseName in pairs( RemoveAirbaseNamesArray ) do + self:Remove( RemoveAirbaseName ) + end + + return self + end + + + --- Finds a Airbase based on the Airbase Name. + -- @param #SET_AIRBASE self + -- @param #string AirbaseName + -- @return Wrapper.Airbase#AIRBASE The found Airbase. + function SET_AIRBASE:FindAirbase( AirbaseName ) + + local AirbaseFound = self.Set[AirbaseName] + return AirbaseFound + end + + + --- Finds an Airbase in range of a coordinate. + -- @param #SET_AIRBASE self + -- @param Core.Point#COORDINATE Coordinate + -- @param #number Range + -- @return Wrapper.Airbase#AIRBASE The found Airbase. + function SET_AIRBASE:FindAirbaseInRange( Coordinate, Range ) + + local AirbaseFound = nil + + for AirbaseName, AirbaseObject in pairs( self.Set ) do + + local AirbaseCoordinate = AirbaseObject:GetCoordinate() + local Distance = Coordinate:Get2DDistance( AirbaseCoordinate ) + + self:F({Distance=Distance}) + + if Distance <= Range then + AirbaseFound = AirbaseObject + break + end + + end + + return AirbaseFound + end + + + --- Finds a random Airbase in the set. + -- @param #SET_AIRBASE self + -- @return Wrapper.Airbase#AIRBASE The found Airbase. + function SET_AIRBASE:GetRandomAirbase() + + local RandomAirbase = self:GetRandom() + self:F( { RandomAirbase = RandomAirbase:GetName() } ) + + return RandomAirbase + end + + + + --- Builds a set of airbases of coalitions. + -- Possible current coalitions are red, blue and neutral. + -- @param #SET_AIRBASE self + -- @param #string Coalitions Can take the following values: "red", "blue", "neutral". + -- @return #SET_AIRBASE self + function SET_AIRBASE: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 airbases out of categories. + -- Possible current categories are plane, helicopter, ground, ship. + -- @param #SET_AIRBASE self + -- @param #string Categories Can take the following values: "airdrome", "helipad", "ship". + -- @return #SET_AIRBASE self + function SET_AIRBASE: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 + + --- Starts the filtering. + -- @param #SET_AIRBASE self + -- @return #SET_AIRBASE self + function SET_AIRBASE:FilterStart() + + if _DATABASE then + + -- We use the BaseCaptured event, which is generated by DCS when a base got captured. + self:HandleEvent( EVENTS.BaseCaptured ) + + -- We initialize the first set. + for ObjectName, Object in pairs( self.Database ) do + if self:IsIncludeObject( Object ) then + self:Add( ObjectName, Object ) + else + self:RemoveAirbasesByName( ObjectName ) + end + end + end + + return self + end + + --- Starts the filtering. + -- @param #SET_AIRBASE self + -- @param Core.Event#EVENT EventData + -- @return #SET_AIRBASE self + function SET_AIRBASE:OnEventBaseCaptured(EventData) + + -- When a base got captured, we reevaluate the set. for ObjectName, Object in pairs( self.Database ) do if self:IsIncludeObject( Object ) then + -- We add captured bases on yet in the set. self:Add( ObjectName, Object ) else + -- We remove captured bases that are not anymore part of the set. self:RemoveAirbasesByName( ObjectName ) end end + end - return self -end - ---- Starts the filtering. --- @param #SET_AIRBASE self --- @param Core.Event#EVENT EventData --- @return #SET_AIRBASE self -function SET_AIRBASE:OnEventBaseCaptured(EventData) - - -- When a base got captured, we reevaluate the set. - for ObjectName, Object in pairs( self.Database ) do - if self:IsIncludeObject( Object ) then - -- We add captured bases on yet in the set. - self:Add( ObjectName, Object ) - else - -- We remove captured bases that are not anymore part of the set. - self:RemoveAirbasesByName( ObjectName ) - 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_AIRBASE self + -- @param Core.Event#EVENTDATA Event + -- @return #string The name of the AIRBASE + -- @return #table The AIRBASE + function SET_AIRBASE:AddInDatabase( Event ) + self:F3( { Event } ) + + return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] end - -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_AIRBASE self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the AIRBASE --- @return #table The AIRBASE -function SET_AIRBASE: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_AIRBASE self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the AIRBASE --- @return #table The AIRBASE -function SET_AIRBASE:FindInDatabase( Event ) - self:F3( { Event } ) - - return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] -end - ---- Iterate the SET_AIRBASE and call an interator function for each AIRBASE, providing the AIRBASE and optional parameters. --- @param #SET_AIRBASE self --- @param #function IteratorFunction The function that will be called when there is an alive AIRBASE in the SET_AIRBASE. The function needs to accept a AIRBASE parameter. --- @return #SET_AIRBASE self -function SET_AIRBASE:ForEachAirbase( IteratorFunction, ... ) - self:F2( arg ) - self:ForEach( IteratorFunction, arg, self:GetSet() ) - - return self -end - ---- Iterate the SET_AIRBASE while identifying the nearest @{Wrapper.Airbase#AIRBASE} from a @{Core.Point#POINT_VEC2}. --- @param #SET_AIRBASE self --- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest @{Wrapper.Airbase#AIRBASE}. --- @return Wrapper.Airbase#AIRBASE The closest @{Wrapper.Airbase#AIRBASE}. -function SET_AIRBASE:FindNearestAirbaseFromPointVec2( PointVec2 ) - self:F2( PointVec2 ) + --- 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_AIRBASE self + -- @param Core.Event#EVENTDATA Event + -- @return #string The name of the AIRBASE + -- @return #table The AIRBASE + function SET_AIRBASE:FindInDatabase( Event ) + self:F3( { Event } ) - local NearestAirbase = self:FindNearestObjectFromPointVec2( PointVec2 ) - return NearestAirbase -end - - - ---- --- @param #SET_AIRBASE self --- @param Wrapper.Airbase#AIRBASE MAirbase --- @return #SET_AIRBASE self -function SET_AIRBASE:IsIncludeObject( MAirbase ) - self:F2( MAirbase ) - - local MAirbaseInclude = true - - if MAirbase then - local MAirbaseName = MAirbase:GetName() + return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] + end - if self.Filter.Coalitions then - local MAirbaseCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - local AirbaseCoalitionID = _DATABASE:GetCoalitionFromAirbase( MAirbaseName ) - self:T3( { "Coalition:", AirbaseCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) - if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == AirbaseCoalitionID then - MAirbaseCoalition = true - end - end - self:T( { "Evaluated Coalition", MAirbaseCoalition } ) - MAirbaseInclude = MAirbaseInclude and MAirbaseCoalition - end + --- Iterate the SET_AIRBASE and call an interator function for each AIRBASE, providing the AIRBASE and optional parameters. + -- @param #SET_AIRBASE self + -- @param #function IteratorFunction The function that will be called when there is an alive AIRBASE in the SET_AIRBASE. The function needs to accept a AIRBASE parameter. + -- @return #SET_AIRBASE self + function SET_AIRBASE:ForEachAirbase( IteratorFunction, ... ) + self:F2( arg ) - if self.Filter.Categories then - local MAirbaseCategory = false - for CategoryID, CategoryName in pairs( self.Filter.Categories ) do - local AirbaseCategoryID = _DATABASE:GetCategoryFromAirbase( MAirbaseName ) - self:T3( { "Category:", AirbaseCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName } ) - if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == AirbaseCategoryID then - MAirbaseCategory = true - end - end - self:T( { "Evaluated Category", MAirbaseCategory } ) - MAirbaseInclude = MAirbaseInclude and MAirbaseCategory - end + self:ForEach( IteratorFunction, arg, self:GetSet() ) + + return self end - - self:T2( MAirbaseInclude ) - return MAirbaseInclude + + --- Iterate the SET_AIRBASE while identifying the nearest @{Wrapper.Airbase#AIRBASE} from a @{Core.Point#POINT_VEC2}. + -- @param #SET_AIRBASE self + -- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest @{Wrapper.Airbase#AIRBASE}. + -- @return Wrapper.Airbase#AIRBASE The closest @{Wrapper.Airbase#AIRBASE}. + function SET_AIRBASE:FindNearestAirbaseFromPointVec2( PointVec2 ) + self:F2( PointVec2 ) + + local NearestAirbase = self:FindNearestObjectFromPointVec2( PointVec2 ) + return NearestAirbase + end + + + + --- + -- @param #SET_AIRBASE self + -- @param Wrapper.Airbase#AIRBASE MAirbase + -- @return #SET_AIRBASE self + function SET_AIRBASE:IsIncludeObject( MAirbase ) + self:F2( MAirbase ) + + local MAirbaseInclude = true + + if MAirbase then + local MAirbaseName = MAirbase:GetName() + + if self.Filter.Coalitions then + local MAirbaseCoalition = false + for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do + local AirbaseCoalitionID = _DATABASE:GetCoalitionFromAirbase( MAirbaseName ) + self:T3( { "Coalition:", AirbaseCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == AirbaseCoalitionID then + MAirbaseCoalition = true + end + end + self:T( { "Evaluated Coalition", MAirbaseCoalition } ) + MAirbaseInclude = MAirbaseInclude and MAirbaseCoalition + end + + if self.Filter.Categories then + local MAirbaseCategory = false + for CategoryID, CategoryName in pairs( self.Filter.Categories ) do + local AirbaseCategoryID = _DATABASE:GetCategoryFromAirbase( MAirbaseName ) + self:T3( { "Category:", AirbaseCategoryID, self.FilterMeta.Categories[CategoryName], CategoryName } ) + if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == AirbaseCategoryID then + MAirbaseCategory = true + end + end + self:T( { "Evaluated Category", MAirbaseCategory } ) + MAirbaseInclude = MAirbaseInclude and MAirbaseCategory + end + end + + self:T2( MAirbaseInclude ) + return MAirbaseInclude + end + end ---- @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: --- --- * Coalitions --- * Types --- * Name or Prefix --- --- ## SET_CARGO constructor --- --- Create a new SET_CARGO object with the @{#SET_CARGO.New} method: --- --- * @{#SET_CARGO.New}: Creates a new SET_CARGO object. --- --- ## Add or Remove CARGOs from SET_CARGO --- --- CARGOs can be added and removed using the @{Core.Set#SET_CARGO.AddCargosByName} and @{Core.Set#SET_CARGO.RemoveCargosByName} respectively. --- These methods take a single CARGO name or an array of CARGO names to be added or removed from SET_CARGO. --- --- ## SET_CARGO filter criteria --- --- You can set filter criteria to automatically maintain the SET_CARGO contents. --- Filter criteria are defined by: --- --- * @{#SET_CARGO.FilterCoalitions}: Builds the SET_CARGO with the cargos belonging to the coalition(s). --- * @{#SET_CARGO.FilterPrefixes}: Builds the SET_CARGO with the cargos containing the prefix string(s). --- * @{#SET_CARGO.FilterTypes}: Builds the SET_CARGO with the cargos belonging to the cargo type(s). --- * @{#SET_CARGO.FilterCountries}: Builds the SET_CARGO with the cargos belonging to the country(ies). --- --- Once the filter criteria have been set for the SET_CARGO, you can start filtering using: --- --- * @{#SET_CARGO.FilterStart}: Starts the filtering of the cargos within the SET_CARGO. --- --- ## SET_CARGO iterators --- --- Once the filters have been defined and the SET_CARGO has been built, you can iterate the SET_CARGO with the available iterator methods. --- The iterator methods will walk the SET_CARGO set, and call for each cargo within the set a function that you provide. --- The following iterator methods are currently available within the SET_CARGO: --- --- * @{#SET_CARGO.ForEachCargo}: Calls a function for each cargo it finds within the SET_CARGO. --- --- @field #SET_CARGO SET_CARGO --- -SET_CARGO = { - ClassName = "SET_CARGO", - Cargos = {}, - Filter = { - Coalitions = nil, - Types = nil, - Countries = nil, - ClientPrefixes = nil, - }, - FilterMeta = { - Coalitions = { - red = coalition.side.RED, - blue = coalition.side.BLUE, - neutral = coalition.side.NEUTRAL, + + +do -- 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: + -- + -- * Coalitions + -- * Types + -- * Name or Prefix + -- + -- ## SET_CARGO constructor + -- + -- Create a new SET_CARGO object with the @{#SET_CARGO.New} method: + -- + -- * @{#SET_CARGO.New}: Creates a new SET_CARGO object. + -- + -- ## Add or Remove CARGOs from SET_CARGO + -- + -- CARGOs can be added and removed using the @{Core.Set#SET_CARGO.AddCargosByName} and @{Core.Set#SET_CARGO.RemoveCargosByName} respectively. + -- These methods take a single CARGO name or an array of CARGO names to be added or removed from SET_CARGO. + -- + -- ## SET_CARGO filter criteria + -- + -- You can set filter criteria to automatically maintain the SET_CARGO contents. + -- Filter criteria are defined by: + -- + -- * @{#SET_CARGO.FilterCoalitions}: Builds the SET_CARGO with the cargos belonging to the coalition(s). + -- * @{#SET_CARGO.FilterPrefixes}: Builds the SET_CARGO with the cargos containing the prefix string(s). + -- * @{#SET_CARGO.FilterTypes}: Builds the SET_CARGO with the cargos belonging to the cargo type(s). + -- * @{#SET_CARGO.FilterCountries}: Builds the SET_CARGO with the cargos belonging to the country(ies). + -- + -- Once the filter criteria have been set for the SET_CARGO, you can start filtering using: + -- + -- * @{#SET_CARGO.FilterStart}: Starts the filtering of the cargos within the SET_CARGO. + -- + -- ## SET_CARGO iterators + -- + -- Once the filters have been defined and the SET_CARGO has been built, you can iterate the SET_CARGO with the available iterator methods. + -- The iterator methods will walk the SET_CARGO set, and call for each cargo within the set a function that you provide. + -- The following iterator methods are currently available within the SET_CARGO: + -- + -- * @{#SET_CARGO.ForEachCargo}: Calls a function for each cargo it finds within the SET_CARGO. + -- + -- @field #SET_CARGO SET_CARGO + -- + SET_CARGO = { + ClassName = "SET_CARGO", + Cargos = {}, + Filter = { + Coalitions = nil, + Types = nil, + Countries = nil, + ClientPrefixes = nil, }, - }, -} - - ---- Creates a new SET_CARGO object, building a set of cargos belonging to a coalitions and categories. --- @param #SET_CARGO self --- @return #SET_CARGO --- @usage --- -- Define a new SET_CARGO Object. The DatabaseSet will contain a reference to all Cargos. --- DatabaseSet = SET_CARGO:New() -function SET_CARGO:New() --R2.1 - -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.CARGOS ) ) -- #SET_CARGO - - return self -end - - ---- (R2.1) Add CARGO to SET_CARGO. --- @param Core.Set#SET_CARGO self --- @param Cargo.Cargo#CARGO Cargo A single cargo. --- @return self -function SET_CARGO:AddCargo( Cargo ) --R2.4 - - self:Add( Cargo:GetName(), Cargo ) + FilterMeta = { + Coalitions = { + red = coalition.side.RED, + blue = coalition.side.BLUE, + neutral = coalition.side.NEUTRAL, + }, + }, + } + + + --- Creates a new SET_CARGO object, building a set of cargos belonging to a coalitions and categories. + -- @param #SET_CARGO self + -- @return #SET_CARGO + -- @usage + -- -- Define a new SET_CARGO Object. The DatabaseSet will contain a reference to all Cargos. + -- DatabaseSet = SET_CARGO:New() + function SET_CARGO:New() --R2.1 + -- Inherits from BASE + local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.CARGOS ) ) -- #SET_CARGO + + return self + end + + + --- (R2.1) Add CARGO to SET_CARGO. + -- @param Core.Set#SET_CARGO self + -- @param Cargo.Cargo#CARGO Cargo A single cargo. + -- @return self + function SET_CARGO:AddCargo( Cargo ) --R2.4 + + self:Add( Cargo:GetName(), Cargo ) + + return self + end + + + --- (R2.1) Add CARGOs to SET_CARGO. + -- @param Core.Set#SET_CARGO self + -- @param #string AddCargoNames A single name or an array of CARGO names. + -- @return self + function SET_CARGO:AddCargosByName( AddCargoNames ) --R2.1 + + local AddCargoNamesArray = ( type( AddCargoNames ) == "table" ) and AddCargoNames or { AddCargoNames } - return self -end - - ---- (R2.1) Add CARGOs to SET_CARGO. --- @param Core.Set#SET_CARGO self --- @param #string AddCargoNames A single name or an array of CARGO names. --- @return self -function SET_CARGO:AddCargosByName( AddCargoNames ) --R2.1 - - local AddCargoNamesArray = ( type( AddCargoNames ) == "table" ) and AddCargoNames or { AddCargoNames } - - for AddCargoID, AddCargoName in pairs( AddCargoNamesArray ) do - self:Add( AddCargoName, CARGO:FindByName( AddCargoName ) ) + for AddCargoID, AddCargoName in pairs( AddCargoNamesArray ) do + self:Add( AddCargoName, CARGO:FindByName( AddCargoName ) ) + end + + return self end + + --- (R2.1) Remove CARGOs from SET_CARGO. + -- @param Core.Set#SET_CARGO self + -- @param Wrapper.Cargo#CARGO RemoveCargoNames A single name or an array of CARGO names. + -- @return self + function SET_CARGO:RemoveCargosByName( RemoveCargoNames ) --R2.1 + + local RemoveCargoNamesArray = ( type( RemoveCargoNames ) == "table" ) and RemoveCargoNames or { RemoveCargoNames } - return self -end - ---- (R2.1) Remove CARGOs from SET_CARGO. --- @param Core.Set#SET_CARGO self --- @param Wrapper.Cargo#CARGO RemoveCargoNames A single name or an array of CARGO names. --- @return self -function SET_CARGO:RemoveCargosByName( RemoveCargoNames ) --R2.1 - - local RemoveCargoNamesArray = ( type( RemoveCargoNames ) == "table" ) and RemoveCargoNames or { RemoveCargoNames } - - for RemoveCargoID, RemoveCargoName in pairs( RemoveCargoNamesArray ) do - self:Remove( RemoveCargoName.CargoName ) + for RemoveCargoID, RemoveCargoName in pairs( RemoveCargoNamesArray ) do + self:Remove( RemoveCargoName.CargoName ) + end + + return self end + + + --- (R2.1) Finds a Cargo based on the Cargo Name. + -- @param #SET_CARGO self + -- @param #string CargoName + -- @return Wrapper.Cargo#CARGO The found Cargo. + function SET_CARGO:FindCargo( CargoName ) --R2.1 + + local CargoFound = self.Set[CargoName] + return CargoFound + end + + + + --- (R2.1) Builds a set of cargos of coalitions. + -- Possible current coalitions are red, blue and neutral. + -- @param #SET_CARGO self + -- @param #string Coalitions Can take the following values: "red", "blue", "neutral". + -- @return #SET_CARGO self + function SET_CARGO:FilterCoalitions( Coalitions ) --R2.1 + 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 + + --- (R2.1) Builds a set of cargos of defined cargo types. + -- Possible current types are those types known within DCS world. + -- @param #SET_CARGO self + -- @param #string Types Can take those type strings known within DCS world. + -- @return #SET_CARGO self + function SET_CARGO:FilterTypes( Types ) --R2.1 + 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 + + + --- (R2.1) Builds a set of cargos of defined countries. + -- Possible current countries are those known within DCS world. + -- @param #SET_CARGO self + -- @param #string Countries Can take those country strings known within DCS world. + -- @return #SET_CARGO self + function SET_CARGO:FilterCountries( Countries ) --R2.1 + 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 + + + --- (R2.1) Builds a set of cargos of defined cargo prefixes. + -- All the cargos starting with the given prefixes will be included within the set. + -- @param #SET_CARGO self + -- @param #string Prefixes The prefix of which the cargo name starts with. + -- @return #SET_CARGO self + function SET_CARGO:FilterPrefixes( Prefixes ) --R2.1 + if not self.Filter.CargoPrefixes then + self.Filter.CargoPrefixes = {} + end + if type( Prefixes ) ~= "table" then + Prefixes = { Prefixes } + end + for PrefixID, Prefix in pairs( Prefixes ) do + self.Filter.CargoPrefixes[Prefix] = Prefix + end + return self + end + + + + --- (R2.1) Starts the filtering. + -- @param #SET_CARGO self + -- @return #SET_CARGO self + function SET_CARGO:FilterStart() --R2.1 + + if _DATABASE then + self:_FilterStart() + self:HandleEvent( EVENTS.NewCargo ) + self:HandleEvent( EVENTS.DeleteCargo ) + end - return self -end - - ---- (R2.1) Finds a Cargo based on the Cargo Name. --- @param #SET_CARGO self --- @param #string CargoName --- @return Wrapper.Cargo#CARGO The found Cargo. -function SET_CARGO:FindCargo( CargoName ) --R2.1 - - local CargoFound = self.Set[CargoName] - return CargoFound -end - - - ---- (R2.1) Builds a set of cargos of coalitions. --- Possible current coalitions are red, blue and neutral. --- @param #SET_CARGO self --- @param #string Coalitions Can take the following values: "red", "blue", "neutral". --- @return #SET_CARGO self -function SET_CARGO:FilterCoalitions( Coalitions ) --R2.1 - 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 - ---- (R2.1) Builds a set of cargos of defined cargo types. --- Possible current types are those types known within DCS world. --- @param #SET_CARGO self --- @param #string Types Can take those type strings known within DCS world. --- @return #SET_CARGO self -function SET_CARGO:FilterTypes( Types ) --R2.1 - 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 - - ---- (R2.1) Builds a set of cargos of defined countries. --- Possible current countries are those known within DCS world. --- @param #SET_CARGO self --- @param #string Countries Can take those country strings known within DCS world. --- @return #SET_CARGO self -function SET_CARGO:FilterCountries( Countries ) --R2.1 - 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 - - ---- (R2.1) Builds a set of cargos of defined cargo prefixes. --- All the cargos starting with the given prefixes will be included within the set. --- @param #SET_CARGO self --- @param #string Prefixes The prefix of which the cargo name starts with. --- @return #SET_CARGO self -function SET_CARGO:FilterPrefixes( Prefixes ) --R2.1 - if not self.Filter.CargoPrefixes then - self.Filter.CargoPrefixes = {} - end - if type( Prefixes ) ~= "table" then - Prefixes = { Prefixes } - end - for PrefixID, Prefix in pairs( Prefixes ) do - self.Filter.CargoPrefixes[Prefix] = Prefix - end - return self -end - - - ---- (R2.1) Starts the filtering. --- @param #SET_CARGO self --- @return #SET_CARGO self -function SET_CARGO:FilterStart() --R2.1 - - if _DATABASE then - self:_FilterStart() - self:HandleEvent( EVENTS.NewCargo ) - self:HandleEvent( EVENTS.DeleteCargo ) + return self end - return self -end - - ---- (R2.1) 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_CARGO self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the CARGO --- @return #table The CARGO -function SET_CARGO:AddInDatabase( Event ) --R2.1 - self:F3( { Event } ) - - return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] -end - ---- (R2.1) 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_CARGO self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the CARGO --- @return #table The CARGO -function SET_CARGO:FindInDatabase( Event ) --R2.1 - self:F3( { Event } ) - - return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] -end - ---- (R2.1) Iterate the SET_CARGO and call an interator function for each CARGO, providing the CARGO and optional parameters. --- @param #SET_CARGO self --- @param #function IteratorFunction The function that will be called when there is an alive CARGO in the SET_CARGO. The function needs to accept a CARGO parameter. --- @return #SET_CARGO self -function SET_CARGO:ForEachCargo( IteratorFunction, ... ) --R2.1 - self:F2( arg ) + --- Stops the filtering for the defined collection. + -- @param #SET_CARGO self + -- @return #SET_CARGO self + function SET_CARGO:FilterStop() - self:ForEach( IteratorFunction, arg, self:GetSet() ) - - return self -end - ---- (R2.1) Iterate the SET_CARGO while identifying the nearest @{Cargo.Cargo#CARGO} from a @{Core.Point#POINT_VEC2}. --- @param #SET_CARGO self --- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest @{Cargo.Cargo#CARGO}. --- @return Wrapper.Cargo#CARGO The closest @{Cargo.Cargo#CARGO}. -function SET_CARGO:FindNearestCargoFromPointVec2( PointVec2 ) --R2.1 - self:F2( PointVec2 ) + self:UnHandleEvent( EVENTS.NewCargo ) + self:UnHandleEvent( EVENTS.DeleteCargo ) + + return self + end - local NearestCargo = self:FindNearestObjectFromPointVec2( PointVec2 ) - return NearestCargo -end - -function SET_CARGO:FirstCargoWithState( State ) - local FirstCargo = nil + --- (R2.1) 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_CARGO self + -- @param Core.Event#EVENTDATA Event + -- @return #string The name of the CARGO + -- @return #table The CARGO + function SET_CARGO:AddInDatabase( Event ) --R2.1 + self:F3( { Event } ) - for CargoName, Cargo in pairs( self.Set ) do - if Cargo:Is( State ) then - FirstCargo = Cargo - break + return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] + end + + --- (R2.1) 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_CARGO self + -- @param Core.Event#EVENTDATA Event + -- @return #string The name of the CARGO + -- @return #table The CARGO + function SET_CARGO:FindInDatabase( Event ) --R2.1 + self:F3( { Event } ) + + return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] + end + + --- (R2.1) Iterate the SET_CARGO and call an interator function for each CARGO, providing the CARGO and optional parameters. + -- @param #SET_CARGO self + -- @param #function IteratorFunction The function that will be called when there is an alive CARGO in the SET_CARGO. The function needs to accept a CARGO parameter. + -- @return #SET_CARGO self + function SET_CARGO:ForEachCargo( IteratorFunction, ... ) --R2.1 + self:F2( arg ) + + self:ForEach( IteratorFunction, arg, self:GetSet() ) + + return self + end + + --- (R2.1) Iterate the SET_CARGO while identifying the nearest @{Cargo.Cargo#CARGO} from a @{Core.Point#POINT_VEC2}. + -- @param #SET_CARGO self + -- @param Core.Point#POINT_VEC2 PointVec2 A @{Core.Point#POINT_VEC2} object from where to evaluate the closest @{Cargo.Cargo#CARGO}. + -- @return Wrapper.Cargo#CARGO The closest @{Cargo.Cargo#CARGO}. + function SET_CARGO:FindNearestCargoFromPointVec2( PointVec2 ) --R2.1 + self:F2( PointVec2 ) + + local NearestCargo = self:FindNearestObjectFromPointVec2( PointVec2 ) + return NearestCargo + end + + function SET_CARGO:FirstCargoWithState( State ) + + local FirstCargo = nil + + for CargoName, Cargo in pairs( self.Set ) do + if Cargo:Is( State ) then + FirstCargo = Cargo + break + end + end + + return FirstCargo + end + + function SET_CARGO:FirstCargoWithStateAndNotDeployed( State ) + + local FirstCargo = nil + + for CargoName, Cargo in pairs( self.Set ) do + if Cargo:Is( State ) and not Cargo:IsDeployed() then + FirstCargo = Cargo + break + end + end + + return FirstCargo + end + + + --- Iterate the SET_CARGO while identifying the first @{Cargo.Cargo#CARGO} that is UnLoaded. + -- @param #SET_CARGO self + -- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. + function SET_CARGO:FirstCargoUnLoaded() + local FirstCargo = self:FirstCargoWithState( "UnLoaded" ) + return FirstCargo + end + + + --- Iterate the SET_CARGO while identifying the first @{Cargo.Cargo#CARGO} that is UnLoaded and not Deployed. + -- @param #SET_CARGO self + -- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. + function SET_CARGO:FirstCargoUnLoadedAndNotDeployed() + local FirstCargo = self:FirstCargoWithStateAndNotDeployed( "UnLoaded" ) + return FirstCargo + end + + + --- Iterate the SET_CARGO while identifying the first @{Cargo.Cargo#CARGO} that is Loaded. + -- @param #SET_CARGO self + -- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. + function SET_CARGO:FirstCargoLoaded() + local FirstCargo = self:FirstCargoWithState( "Loaded" ) + return FirstCargo + end + + + --- Iterate the SET_CARGO while identifying the first @{Cargo.Cargo#CARGO} that is Deployed. + -- @param #SET_CARGO self + -- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. + function SET_CARGO:FirstCargoDeployed() + local FirstCargo = self:FirstCargoWithState( "Deployed" ) + return FirstCargo + end + + + + + --- (R2.1) + -- @param #SET_CARGO self + -- @param AI.AI_Cargo#AI_CARGO MCargo + -- @return #SET_CARGO self + function SET_CARGO:IsIncludeObject( MCargo ) --R2.1 + self:F2( MCargo ) + + local MCargoInclude = true + + if MCargo then + local MCargoName = MCargo:GetName() + + if self.Filter.Coalitions then + local MCargoCoalition = false + for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do + local CargoCoalitionID = MCargo:GetCoalition() + self:T3( { "Coalition:", CargoCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) + if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == CargoCoalitionID then + MCargoCoalition = true + end + end + self:F( { "Evaluated Coalition", MCargoCoalition } ) + MCargoInclude = MCargoInclude and MCargoCoalition + end + + if self.Filter.Types then + local MCargoType = false + for TypeID, TypeName in pairs( self.Filter.Types ) do + self:T3( { "Type:", MCargo:GetType(), TypeName } ) + if TypeName == MCargo:GetType() then + MCargoType = true + end + end + self:F( { "Evaluated Type", MCargoType } ) + MCargoInclude = MCargoInclude and MCargoType + end + + if self.Filter.CargoPrefixes then + local MCargoPrefix = false + for CargoPrefixId, CargoPrefix in pairs( self.Filter.CargoPrefixes ) do + self:T3( { "Prefix:", string.find( MCargo.Name, CargoPrefix, 1 ), CargoPrefix } ) + if string.find( MCargo.Name, CargoPrefix, 1 ) then + MCargoPrefix = true + end + end + self:F( { "Evaluated Prefix", MCargoPrefix } ) + MCargoInclude = MCargoInclude and MCargoPrefix + end + end + + self:T2( MCargoInclude ) + return MCargoInclude + end + + --- (R2.1) Handles the OnEventNewCargo event for the Set. + -- @param #SET_CARGO self + -- @param Core.Event#EVENTDATA EventData + function SET_CARGO:OnEventNewCargo( EventData ) --R2.1 + + self:F( { "New Cargo", EventData } ) + + if EventData.Cargo then + if EventData.Cargo and self:IsIncludeObject( EventData.Cargo ) then + self:Add( EventData.Cargo.Name , EventData.Cargo ) + end end end - return FirstCargo -end - -function SET_CARGO:FirstCargoWithStateAndNotDeployed( State ) + --- (R2.1) Handles the OnDead or OnCrash event for alive units set. + -- @param #SET_CARGO self + -- @param Core.Event#EVENTDATA EventData + function SET_CARGO:OnEventDeleteCargo( EventData ) --R2.1 + self:F3( { EventData } ) - local FirstCargo = nil + if EventData.Cargo then + local Cargo = _DATABASE:FindCargo( EventData.Cargo.Name ) + if Cargo and Cargo.Name then - for CargoName, Cargo in pairs( self.Set ) do - if Cargo:Is( State ) and not Cargo:IsDeployed() then - FirstCargo = Cargo - break - end - end - - return FirstCargo -end - - ---- Iterate the SET_CARGO while identifying the first @{Cargo.Cargo#CARGO} that is UnLoaded. --- @param #SET_CARGO self --- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. -function SET_CARGO:FirstCargoUnLoaded() - local FirstCargo = self:FirstCargoWithState( "UnLoaded" ) - return FirstCargo -end - - ---- Iterate the SET_CARGO while identifying the first @{Cargo.Cargo#CARGO} that is UnLoaded and not Deployed. --- @param #SET_CARGO self --- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. -function SET_CARGO:FirstCargoUnLoadedAndNotDeployed() - local FirstCargo = self:FirstCargoWithStateAndNotDeployed( "UnLoaded" ) - return FirstCargo -end - - ---- Iterate the SET_CARGO while identifying the first @{Cargo.Cargo#CARGO} that is Loaded. --- @param #SET_CARGO self --- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. -function SET_CARGO:FirstCargoLoaded() - local FirstCargo = self:FirstCargoWithState( "Loaded" ) - return FirstCargo -end - - ---- Iterate the SET_CARGO while identifying the first @{Cargo.Cargo#CARGO} that is Deployed. --- @param #SET_CARGO self --- @return Cargo.Cargo#CARGO The first @{Cargo.Cargo#CARGO}. -function SET_CARGO:FirstCargoDeployed() - local FirstCargo = self:FirstCargoWithState( "Deployed" ) - return FirstCargo -end - - - - ---- (R2.1) --- @param #SET_CARGO self --- @param AI.AI_Cargo#AI_CARGO MCargo --- @return #SET_CARGO self -function SET_CARGO:IsIncludeObject( MCargo ) --R2.1 - self:F2( MCargo ) - - local MCargoInclude = true - - if MCargo then - local MCargoName = MCargo:GetName() - - if self.Filter.Coalitions then - local MCargoCoalition = false - for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do - local CargoCoalitionID = MCargo:GetCoalition() - self:T3( { "Coalition:", CargoCoalitionID, self.FilterMeta.Coalitions[CoalitionName], CoalitionName } ) - if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == CargoCoalitionID then - MCargoCoalition = true + -- When cargo was deleted, it may probably be because of an S_EVENT_DEAD. + -- However, in the loading logic, an S_EVENT_DEAD is also generated after a Destroy() call. + -- And this is a problem because it will remove all entries from the SET_CARGOs. + -- To prevent this from happening, the Cargo object has a flag NoDestroy. + -- When true, the SET_CARGO won't Remove the Cargo object from the set. + -- This flag is switched off after the event handlers have been called in the EVENT class. + self:F( { CargoNoDestroy=Cargo.NoDestroy } ) + if Cargo.NoDestroy then + else + self:Remove( Cargo.Name ) end end - self:F( { "Evaluated Coalition", MCargoCoalition } ) - MCargoInclude = MCargoInclude and MCargoCoalition - end - - if self.Filter.Types then - local MCargoType = false - for TypeID, TypeName in pairs( self.Filter.Types ) do - self:T3( { "Type:", MCargo:GetType(), TypeName } ) - if TypeName == MCargo:GetType() then - MCargoType = true - end - end - self:F( { "Evaluated Type", MCargoType } ) - MCargoInclude = MCargoInclude and MCargoType - end - - if self.Filter.CargoPrefixes then - local MCargoPrefix = false - for CargoPrefixId, CargoPrefix in pairs( self.Filter.CargoPrefixes ) do - self:T3( { "Prefix:", string.find( MCargo.Name, CargoPrefix, 1 ), CargoPrefix } ) - if string.find( MCargo.Name, CargoPrefix, 1 ) then - MCargoPrefix = true - end - end - self:F( { "Evaluated Prefix", MCargoPrefix } ) - MCargoInclude = MCargoInclude and MCargoPrefix end end - - self:T2( MCargoInclude ) - return MCargoInclude -end ---- (R2.1) Handles the OnEventNewCargo event for the Set. --- @param #SET_CARGO self --- @param Core.Event#EVENTDATA EventData -function SET_CARGO:OnEventNewCargo( EventData ) --R2.1 - - self:F( { "New Cargo", EventData } ) - - if EventData.Cargo then - if EventData.Cargo and self:IsIncludeObject( EventData.Cargo ) then - self:Add( EventData.Cargo.Name , EventData.Cargo ) - end - end -end - ---- (R2.1) Handles the OnDead or OnCrash event for alive units set. --- @param #SET_CARGO self --- @param Core.Event#EVENTDATA EventData -function SET_CARGO:OnEventDeleteCargo( EventData ) --R2.1 - self:F3( { EventData } ) - - if EventData.Cargo then - local Cargo = _DATABASE:FindCargo( EventData.Cargo.Name ) - if Cargo and Cargo.Name then - - -- When cargo was deleted, it may probably be because of an S_EVENT_DEAD. - -- However, in the loading logic, an S_EVENT_DEAD is also generated after a Destroy() call. - -- And this is a problem because it will remove all entries from the SET_CARGOs. - -- To prevent this from happening, the Cargo object has a flag NoDestroy. - -- When true, the SET_CARGO won't Remove the Cargo object from the set. - -- This flag is switched off after the event handlers have been called in the EVENT class. - self:F( { CargoNoDestroy=Cargo.NoDestroy } ) - if Cargo.NoDestroy then - else - self:Remove( Cargo.Name ) - end - end - end end +do -- 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. --- --- ## SET_ZONE constructor --- --- Create a new SET_ZONE object with the @{#SET_ZONE.New} method: --- --- * @{#SET_ZONE.New}: Creates a new SET_ZONE object. --- --- ## Add or Remove ZONEs from SET_ZONE --- --- ZONEs can be added and removed using the @{Core.Set#SET_ZONE.AddZonesByName} and @{Core.Set#SET_ZONE.RemoveZonesByName} respectively. --- These methods take a single ZONE name or an array of ZONE names to be added or removed from SET_ZONE. --- --- ## SET_ZONE filter criteria --- --- You can set filter criteria to build the collection of zones in SET_ZONE. --- Filter criteria are defined by: --- --- * @{#SET_ZONE.FilterPrefixes}: Builds the SET_ZONE with the zones having a certain text pattern of prefix. --- --- Once the filter criteria have been set for the SET_ZONE, you can start filtering using: --- --- * @{#SET_ZONE.FilterStart}: Starts the filtering of the zones within the SET_ZONE. --- --- ## SET_ZONE iterators --- --- Once the filters have been defined and the SET_ZONE has been built, you can iterate the SET_ZONE with the available iterator methods. --- The iterator methods will walk the SET_ZONE set, and call for each airbase within the set a function that you provide. --- The following iterator methods are currently available within the SET_ZONE: --- --- * @{#SET_ZONE.ForEachZone}: Calls a function for each zone it finds within the SET_ZONE. --- --- === --- @field #SET_ZONE SET_ZONE -SET_ZONE = { - ClassName = "SET_ZONE", - Zones = {}, - Filter = { - Prefixes = nil, - }, - FilterMeta = { - }, -} - - ---- Creates a new SET_ZONE object, building a set of zones. --- @param #SET_ZONE self --- @return #SET_ZONE self --- @usage --- -- Define a new SET_ZONE Object. The DatabaseSet will contain a reference to all Zones. --- DatabaseSet = SET_ZONE:New() -function SET_ZONE:New() - -- Inherits from BASE - local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.ZONES ) ) - - return self -end - ---- Add ZONEs by a search name to SET_ZONE. --- @param Core.Set#SET_ZONE self --- @param #string AddZoneNames A single name or an array of ZONE_BASE names. --- @return self -function SET_ZONE:AddZonesByName( AddZoneNames ) - - local AddZoneNamesArray = ( type( AddZoneNames ) == "table" ) and AddZoneNames or { AddZoneNames } + --- @type SET_ZONE + -- @extends Core.Set#SET_BASE - for AddAirbaseID, AddZoneName in pairs( AddZoneNamesArray ) do - self:Add( AddZoneName, ZONE:FindByName( AddZoneName ) ) - end - - return self -end - ---- Add ZONEs to SET_ZONE. --- @param Core.Set#SET_ZONE self --- @param Core.Zone#ZONE_BASE Zone A ZONE_BASE object. --- @return self -function SET_ZONE:AddZone( Zone ) - - self:Add( Zone:GetName(), Zone ) - - return self -end - - ---- Remove ZONEs from SET_ZONE. --- @param Core.Set#SET_ZONE self --- @param Core.Zone#ZONE_BASE RemoveZoneNames A single name or an array of ZONE_BASE names. --- @return self -function SET_ZONE:RemoveZonesByName( RemoveZoneNames ) - - local RemoveZoneNamesArray = ( type( RemoveZoneNames ) == "table" ) and RemoveZoneNames or { RemoveZoneNames } + --- Mission designers can use the @{Core.Set#SET_ZONE} class to build sets of zones of various types. + -- + -- ## SET_ZONE constructor + -- + -- Create a new SET_ZONE object with the @{#SET_ZONE.New} method: + -- + -- * @{#SET_ZONE.New}: Creates a new SET_ZONE object. + -- + -- ## Add or Remove ZONEs from SET_ZONE + -- + -- ZONEs can be added and removed using the @{Core.Set#SET_ZONE.AddZonesByName} and @{Core.Set#SET_ZONE.RemoveZonesByName} respectively. + -- These methods take a single ZONE name or an array of ZONE names to be added or removed from SET_ZONE. + -- + -- ## SET_ZONE filter criteria + -- + -- You can set filter criteria to build the collection of zones in SET_ZONE. + -- Filter criteria are defined by: + -- + -- * @{#SET_ZONE.FilterPrefixes}: Builds the SET_ZONE with the zones having a certain text pattern of prefix. + -- + -- Once the filter criteria have been set for the SET_ZONE, you can start filtering using: + -- + -- * @{#SET_ZONE.FilterStart}: Starts the filtering of the zones within the SET_ZONE. + -- + -- ## SET_ZONE iterators + -- + -- Once the filters have been defined and the SET_ZONE has been built, you can iterate the SET_ZONE with the available iterator methods. + -- The iterator methods will walk the SET_ZONE set, and call for each airbase within the set a function that you provide. + -- The following iterator methods are currently available within the SET_ZONE: + -- + -- * @{#SET_ZONE.ForEachZone}: Calls a function for each zone it finds within the SET_ZONE. + -- + -- === + -- @field #SET_ZONE SET_ZONE + SET_ZONE = { + ClassName = "SET_ZONE", + Zones = {}, + Filter = { + Prefixes = nil, + }, + FilterMeta = { + }, + } - for RemoveZoneID, RemoveZoneName in pairs( RemoveZoneNamesArray ) do - self:Remove( RemoveZoneName ) + + --- Creates a new SET_ZONE object, building a set of zones. + -- @param #SET_ZONE self + -- @return #SET_ZONE self + -- @usage + -- -- Define a new SET_ZONE Object. The DatabaseSet will contain a reference to all Zones. + -- DatabaseSet = SET_ZONE:New() + function SET_ZONE:New() + -- Inherits from BASE + local self = BASE:Inherit( self, SET_BASE:New( _DATABASE.ZONES ) ) + + return self end + + --- Add ZONEs by a search name to SET_ZONE. + -- @param Core.Set#SET_ZONE self + -- @param #string AddZoneNames A single name or an array of ZONE_BASE names. + -- @return self + function SET_ZONE:AddZonesByName( AddZoneNames ) + + local AddZoneNamesArray = ( type( AddZoneNames ) == "table" ) and AddZoneNames or { AddZoneNames } - return self -end - - ---- Finds a Zone based on the Zone Name. --- @param #SET_ZONE self --- @param #string ZoneName --- @return Core.Zone#ZONE_BASE The found Zone. -function SET_ZONE:FindZone( ZoneName ) - - local ZoneFound = self.Set[ZoneName] - return ZoneFound -end - - ---- Get a random zone from the set. --- @param #SET_ZONE self --- @return Core.Zone#ZONE_BASE The random Zone. --- @return #nil if no zone in the collection. -function SET_ZONE:GetRandomZone() - - if self:Count() ~= 0 then - - local Index = self.Index - local ZoneFound = nil -- Core.Zone#ZONE_BASE - - -- Loop until a zone has been found. - -- The :GetZoneMaybe() call will evaluate the probability for the zone to be selected. - -- If the zone is not selected, then nil is returned by :GetZoneMaybe() and the loop continues! - while not ZoneFound do - local ZoneRandom = math.random( 1, #Index ) - ZoneFound = self.Set[Index[ZoneRandom]]:GetZoneMaybe() + for AddAirbaseID, AddZoneName in pairs( AddZoneNamesArray ) do + self:Add( AddZoneName, ZONE:FindByName( AddZoneName ) ) end + + return self + end + --- Add ZONEs to SET_ZONE. + -- @param Core.Set#SET_ZONE self + -- @param Core.Zone#ZONE_BASE Zone A ZONE_BASE object. + -- @return self + function SET_ZONE:AddZone( Zone ) + + self:Add( Zone:GetName(), Zone ) + + return self + end + + + --- Remove ZONEs from SET_ZONE. + -- @param Core.Set#SET_ZONE self + -- @param Core.Zone#ZONE_BASE RemoveZoneNames A single name or an array of ZONE_BASE names. + -- @return self + function SET_ZONE:RemoveZonesByName( RemoveZoneNames ) + + local RemoveZoneNamesArray = ( type( RemoveZoneNames ) == "table" ) and RemoveZoneNames or { RemoveZoneNames } + + for RemoveZoneID, RemoveZoneName in pairs( RemoveZoneNamesArray ) do + self:Remove( RemoveZoneName ) + end + + return self + end + + + --- Finds a Zone based on the Zone Name. + -- @param #SET_ZONE self + -- @param #string ZoneName + -- @return Core.Zone#ZONE_BASE The found Zone. + function SET_ZONE:FindZone( ZoneName ) + + local ZoneFound = self.Set[ZoneName] return ZoneFound end - return nil -end - - ---- Set a zone probability. --- @param #SET_ZONE self --- @param #string ZoneName The name of the zone. -function SET_ZONE:SetZoneProbability( ZoneName, ZoneProbability ) - local Zone = self:FindZone( ZoneName ) - Zone:SetZoneProbability( ZoneProbability ) -end - - - - ---- Builds a set of zones of defined zone prefixes. --- All the zones starting with the given prefixes will be included within the set. --- @param #SET_ZONE self --- @param #string Prefixes The prefix of which the zone name starts with. --- @return #SET_ZONE self -function SET_ZONE:FilterPrefixes( Prefixes ) - if not self.Filter.Prefixes then - self.Filter.Prefixes = {} - end - if type( Prefixes ) ~= "table" then - Prefixes = { Prefixes } - end - for PrefixID, Prefix in pairs( Prefixes ) do - self.Filter.Prefixes[Prefix] = Prefix - end - return self -end - - ---- Starts the filtering. --- @param #SET_ZONE self --- @return #SET_ZONE self -function SET_ZONE:FilterStart() - - if _DATABASE then - -- We initialize the first set. - for ObjectName, Object in pairs( self.Database ) do - if self:IsIncludeObject( Object ) then - self:Add( ObjectName, Object ) - else - self:RemoveZonesByName( ObjectName ) + --- Get a random zone from the set. + -- @param #SET_ZONE self + -- @return Core.Zone#ZONE_BASE The random Zone. + -- @return #nil if no zone in the collection. + function SET_ZONE:GetRandomZone() + + if self:Count() ~= 0 then + + local Index = self.Index + local ZoneFound = nil -- Core.Zone#ZONE_BASE + + -- Loop until a zone has been found. + -- The :GetZoneMaybe() call will evaluate the probability for the zone to be selected. + -- If the zone is not selected, then nil is returned by :GetZoneMaybe() and the loop continues! + while not ZoneFound do + local ZoneRandom = math.random( 1, #Index ) + ZoneFound = self.Set[Index[ZoneRandom]]:GetZoneMaybe() end + + return ZoneFound end + + return nil end - - self:HandleEvent( EVENTS.NewZone ) - self:HandleEvent( EVENTS.DeleteZone ) - 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_ZONE self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the AIRBASE --- @return #table The AIRBASE -function SET_ZONE: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_ZONE self --- @param Core.Event#EVENTDATA Event --- @return #string The name of the AIRBASE --- @return #table The AIRBASE -function SET_ZONE:FindInDatabase( Event ) - self:F3( { Event } ) - - return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] -end - ---- Iterate the SET_ZONE and call an interator function for each ZONE, providing the ZONE and optional parameters. --- @param #SET_ZONE self --- @param #function IteratorFunction The function that will be called when there is an alive ZONE in the SET_ZONE. The function needs to accept a AIRBASE parameter. --- @return #SET_ZONE self -function SET_ZONE:ForEachZone( IteratorFunction, ... ) - self:F2( arg ) - self:ForEach( IteratorFunction, arg, self:GetSet() ) - - return self -end - - ---- --- @param #SET_ZONE self --- @param Core.Zone#ZONE_BASE MZone --- @return #SET_ZONE self -function SET_ZONE:IsIncludeObject( MZone ) - self:F2( MZone ) - - local MZoneInclude = true - - if MZone then - local MZoneName = MZone:GetName() + --- Set a zone probability. + -- @param #SET_ZONE self + -- @param #string ZoneName The name of the zone. + function SET_ZONE:SetZoneProbability( ZoneName, ZoneProbability ) + local Zone = self:FindZone( ZoneName ) + Zone:SetZoneProbability( ZoneProbability ) + end - if self.Filter.Prefixes then - local MZonePrefix = false - for ZonePrefixId, ZonePrefix in pairs( self.Filter.Prefixes ) do - self:T3( { "Prefix:", string.find( MZoneName, ZonePrefix, 1 ), ZonePrefix } ) - if string.find( MZoneName, ZonePrefix, 1 ) then - MZonePrefix = true + + + + --- Builds a set of zones of defined zone prefixes. + -- All the zones starting with the given prefixes will be included within the set. + -- @param #SET_ZONE self + -- @param #string Prefixes The prefix of which the zone name starts with. + -- @return #SET_ZONE self + function SET_ZONE:FilterPrefixes( Prefixes ) + if not self.Filter.Prefixes then + self.Filter.Prefixes = {} + end + if type( Prefixes ) ~= "table" then + Prefixes = { Prefixes } + end + for PrefixID, Prefix in pairs( Prefixes ) do + self.Filter.Prefixes[Prefix] = Prefix + end + return self + end + + + --- Starts the filtering. + -- @param #SET_ZONE self + -- @return #SET_ZONE self + function SET_ZONE:FilterStart() + + if _DATABASE then + + -- We initialize the first set. + for ObjectName, Object in pairs( self.Database ) do + if self:IsIncludeObject( Object ) then + self:Add( ObjectName, Object ) + else + self:RemoveZonesByName( ObjectName ) end end - self:T( { "Evaluated Prefix", MZonePrefix } ) - MZoneInclude = MZoneInclude and MZonePrefix end + + self:HandleEvent( EVENTS.NewZone ) + self:HandleEvent( EVENTS.DeleteZone ) + + return self end - - self:T2( MZoneInclude ) - return MZoneInclude -end - ---- Handles the OnEventNewZone event for the Set. --- @param #SET_ZONE self --- @param Core.Event#EVENTDATA EventData -function SET_ZONE:OnEventNewZone( EventData ) --R2.1 - - self:F( { "New Zone", EventData } ) - - if EventData.Zone then - if EventData.Zone and self:IsIncludeObject( EventData.Zone ) then - self:Add( EventData.Zone.ZoneName , EventData.Zone ) + + --- Stops the filtering for the defined collection. + -- @param #SET_ZONE self + -- @return #SET_ZONE self + function SET_ZONE:FilterStop() + + self:UnHandleEvent( EVENTS.NewZone ) + self:UnHandleEvent( EVENTS.DeleteZone ) + + 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_ZONE self + -- @param Core.Event#EVENTDATA Event + -- @return #string The name of the AIRBASE + -- @return #table The AIRBASE + function SET_ZONE: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_ZONE self + -- @param Core.Event#EVENTDATA Event + -- @return #string The name of the AIRBASE + -- @return #table The AIRBASE + function SET_ZONE:FindInDatabase( Event ) + self:F3( { Event } ) + + return Event.IniDCSUnitName, self.Database[Event.IniDCSUnitName] + end + + --- Iterate the SET_ZONE and call an interator function for each ZONE, providing the ZONE and optional parameters. + -- @param #SET_ZONE self + -- @param #function IteratorFunction The function that will be called when there is an alive ZONE in the SET_ZONE. The function needs to accept a AIRBASE parameter. + -- @return #SET_ZONE self + function SET_ZONE:ForEachZone( IteratorFunction, ... ) + self:F2( arg ) + + self:ForEach( IteratorFunction, arg, self:GetSet() ) + + return self + end + + + --- + -- @param #SET_ZONE self + -- @param Core.Zone#ZONE_BASE MZone + -- @return #SET_ZONE self + function SET_ZONE:IsIncludeObject( MZone ) + self:F2( MZone ) + + local MZoneInclude = true + + if MZone then + local MZoneName = MZone:GetName() + + if self.Filter.Prefixes then + local MZonePrefix = false + for ZonePrefixId, ZonePrefix in pairs( self.Filter.Prefixes ) do + self:T3( { "Prefix:", string.find( MZoneName, ZonePrefix, 1 ), ZonePrefix } ) + if string.find( MZoneName, ZonePrefix, 1 ) then + MZonePrefix = true + end + end + self:T( { "Evaluated Prefix", MZonePrefix } ) + MZoneInclude = MZoneInclude and MZonePrefix + end end + + self:T2( MZoneInclude ) + return MZoneInclude end -end - ---- Handles the OnDead or OnCrash event for alive units set. --- @param #SET_ZONE self --- @param Core.Event#EVENTDATA EventData -function SET_ZONE:OnEventDeleteZone( EventData ) --R2.1 - self:F3( { EventData } ) - - if EventData.Zone then - local Zone = _DATABASE:FindZone( EventData.Zone.ZoneName ) - if Zone and Zone.ZoneName then - - -- When cargo was deleted, it may probably be because of an S_EVENT_DEAD. - -- However, in the loading logic, an S_EVENT_DEAD is also generated after a Destroy() call. - -- And this is a problem because it will remove all entries from the SET_ZONEs. - -- To prevent this from happening, the Zone object has a flag NoDestroy. - -- When true, the SET_ZONE won't Remove the Zone object from the set. - -- This flag is switched off after the event handlers have been called in the EVENT class. - self:F( { ZoneNoDestroy=Zone.NoDestroy } ) - if Zone.NoDestroy then - else - self:Remove( Zone.ZoneName ) + + --- Handles the OnEventNewZone event for the Set. + -- @param #SET_ZONE self + -- @param Core.Event#EVENTDATA EventData + function SET_ZONE:OnEventNewZone( EventData ) --R2.1 + + self:F( { "New Zone", EventData } ) + + if EventData.Zone then + if EventData.Zone and self:IsIncludeObject( EventData.Zone ) then + self:Add( EventData.Zone.ZoneName , EventData.Zone ) end end end -end - ---- Validate if a coordinate is in one of the zones in the set. --- Returns the ZONE object where the coordiante is located. --- If zones overlap, the first zone that validates the test is returned. --- @param #SET_ZONE self --- @param Core.Point#COORDINATE Coordinate The coordinate to be searched. --- @return Core.Zone#ZONE_BASE The zone that validates the coordinate location. --- @return #nil No zone has been found. -function SET_ZONE:IsCoordinateInZone( Coordinate ) - - for _, Zone in pairs( self:GetSet() ) do - local Zone = Zone -- Core.Zone#ZONE_BASE - if Zone:IsCoordinateInZone( Coordinate ) then - return Zone + + --- Handles the OnDead or OnCrash event for alive units set. + -- @param #SET_ZONE self + -- @param Core.Event#EVENTDATA EventData + function SET_ZONE:OnEventDeleteZone( EventData ) --R2.1 + self:F3( { EventData } ) + + if EventData.Zone then + local Zone = _DATABASE:FindZone( EventData.Zone.ZoneName ) + if Zone and Zone.ZoneName then + + -- When cargo was deleted, it may probably be because of an S_EVENT_DEAD. + -- However, in the loading logic, an S_EVENT_DEAD is also generated after a Destroy() call. + -- And this is a problem because it will remove all entries from the SET_ZONEs. + -- To prevent this from happening, the Zone object has a flag NoDestroy. + -- When true, the SET_ZONE won't Remove the Zone object from the set. + -- This flag is switched off after the event handlers have been called in the EVENT class. + self:F( { ZoneNoDestroy=Zone.NoDestroy } ) + if Zone.NoDestroy then + else + self:Remove( Zone.ZoneName ) + end + end end end + + --- Validate if a coordinate is in one of the zones in the set. + -- Returns the ZONE object where the coordiante is located. + -- If zones overlap, the first zone that validates the test is returned. + -- @param #SET_ZONE self + -- @param Core.Point#COORDINATE Coordinate The coordinate to be searched. + -- @return Core.Zone#ZONE_BASE The zone that validates the coordinate location. + -- @return #nil No zone has been found. + function SET_ZONE:IsCoordinateInZone( Coordinate ) + + for _, Zone in pairs( self:GetSet() ) do + local Zone = Zone -- Core.Zone#ZONE_BASE + if Zone:IsCoordinateInZone( Coordinate ) then + return Zone + end + end + + return nil + end - return nil -end +end \ No newline at end of file From 7cd40e2d2cdf8a0d3ed986f895186aca7acef513 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 18 Sep 2018 07:59:43 +0200 Subject: [PATCH 373/420] Some code cleanup. --- Moose Development/Moose/Core/Set.lua | 9 +-------- Moose Development/Moose/Core/SpawnStatic.lua | 1 - Moose Development/Moose/DCS.lua | 2 -- Moose Development/Moose/Functional/CleanUp.lua | 4 ++-- Moose Development/Moose/Wrapper/Client.lua | 6 +----- 5 files changed, 4 insertions(+), 18 deletions(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index c5d6c1efe..e541a66fd 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -2543,6 +2543,7 @@ do -- SET_UNIT end + do -- SET_STATIC --- @type SET_STATIC @@ -3214,8 +3215,6 @@ do -- SET_STATIC end - - do -- SET_CLIENT @@ -3667,8 +3666,6 @@ do -- SET_CLIENT end - - do -- SET_PLAYER --- @type SET_PLAYER @@ -4071,8 +4068,6 @@ do -- SET_PLAYER end - - do -- SET_AIRBASE --- @type SET_AIRBASE @@ -4415,8 +4410,6 @@ do -- SET_AIRBASE end - - do -- SET_CARGO --- @type SET_CARGO diff --git a/Moose Development/Moose/Core/SpawnStatic.lua b/Moose Development/Moose/Core/SpawnStatic.lua index ac3b776ef..7e17d64c2 100644 --- a/Moose Development/Moose/Core/SpawnStatic.lua +++ b/Moose Development/Moose/Core/SpawnStatic.lua @@ -149,7 +149,6 @@ function SPAWNSTATIC:Spawn( Heading, NewName ) --R2.3 end - --- Creates a new @{Static} from a POINT_VEC2. -- @param #SPAWNSTATIC self -- @param Core.Point#POINT_VEC2 PointVec2 The 2D coordinate where to spawn the static. diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua index c8e997f5c..f0c290904 100644 --- a/Moose Development/Moose/DCS.lua +++ b/Moose Development/Moose/DCS.lua @@ -984,8 +984,6 @@ do -- Group -- @param #Group self -- @return #Group.Category - --TODO check coalition.side - --- Returns the coalition of the group. -- @function [parent=#Group] getCoalition -- @param #Group self diff --git a/Moose Development/Moose/Functional/CleanUp.lua b/Moose Development/Moose/Functional/CleanUp.lua index 63bda13d6..23a018d29 100644 --- a/Moose Development/Moose/Functional/CleanUp.lua +++ b/Moose Development/Moose/Functional/CleanUp.lua @@ -180,7 +180,7 @@ function CLEANUP_AIRBASE.__:DestroyUnit( CleanUpUnit ) if CleanUpUnit then local CleanUpUnitName = CleanUpUnit:GetName() local CleanUpGroup = CleanUpUnit:GetGroup() - -- TODO Client bug in 1.5.3 + -- TODO DCS BUG - Client bug in 1.5.3 if CleanUpGroup:IsAlive() then local CleanUpGroupUnits = CleanUpGroup:GetUnits() if #CleanUpGroupUnits == 1 then @@ -229,7 +229,7 @@ end function CLEANUP_AIRBASE.__:OnEventCrash( Event ) self:F( { Event } ) - --TODO: This stuff is not working due to a DCS bug. Burning units cannot be destroyed. + --TODO: DCS BUG - This stuff is not working due to a DCS bug. Burning units cannot be destroyed. -- self:T("before getGroup") -- local _grp = Unit.getGroup(event.initiator)-- Identify the group that fired -- self:T("after getGroup") diff --git a/Moose Development/Moose/Wrapper/Client.lua b/Moose Development/Moose/Wrapper/Client.lua index 7df3c0586..462816e2c 100644 --- a/Moose Development/Moose/Wrapper/Client.lua +++ b/Moose Development/Moose/Wrapper/Client.lua @@ -433,11 +433,7 @@ function CLIENT:ShowCargo() end --- TODO (1) I urgently need to revise this. ---- A local function called by the DCS World Menu system to switch off messages. -function CLIENT.SwitchMessages( PrmTable ) - PrmTable[1].MessageSwitch = PrmTable[2] -end + --- The main message driver for the CLIENT. -- This function displays various messages to the Player logged into the CLIENT through the DCS World Messaging system. From e0c8d55a5dec6bbc72ea53f8bd53684f65356edd Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 18 Sep 2018 10:25:13 +0200 Subject: [PATCH 374/420] Updated to CARGO TASKING model. Now capture from the dispatcher the CargoPickedUp and CargoDeployed events. --- .../Moose/Tasking/Task_CARGO.lua | 15 ++- .../Moose/Tasking/Task_Cargo_CSAR.lua | 108 +++++++++++++++ .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 123 ++++++++++++++++- .../Moose/Tasking/Task_Cargo_Transport.lua | 125 +++++++++++++++++- 4 files changed, 362 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 7cd0c5399..d3f830c65 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -367,6 +367,15 @@ -- -- **Deploying a cargo within a deployment zone, may complete a deployment task! So ensure that you deploy the right cargo at the right deployment zone!** -- +-- ## 4) Cargo tasking from a mission designer perspective. +-- +-- Please consult the documentation how to implement the derived classes of SET_CARGO in: +-- +-- - @{Tasking.Task_Cargo#TASK_CARGO}: Documents the main methods how to handle the cargo tasking from a mission designer perspective. +-- - @{Tasking.Task_Cargo#TASK_CARGO_TRANSPORT}: Documents the specific methods how to handle the cargo transportation tasking from a mission designer perspective. +-- - @{Tasking.Task_Cargo#TASK_CARGO_CSAR}: Documents the specific methods how to handle the cargo CSAR tasking from a mission designer perspective. +-- +-- -- === -- -- ### Author: **FlightControl** @@ -445,6 +454,8 @@ do -- TASK_CARGO -- * **Success**: The cargo task is successfully completed. -- * **Failed**: The cargo task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ. -- + -- + -- -- === -- -- @field #TASK_CARGO @@ -716,11 +727,11 @@ do -- TASK_CARGO -- Cargo in deployzones are flagged as deployed. for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do if Cargo:IsInZone( DeployZone ) then - Task:E( { CargoIsDeployed = Task.CargoDeployed and "true" or "false" } ) + Task:I( { CargoIsDeployed = Task.CargoDeployed and "true" or "false" } ) if Cargo:IsDeployed() == false then Cargo:SetDeployed( true ) -- Now we call a callback method to handle the CargoDeployed event. - Task:E( { CargoIsAlive = Cargo:IsAlive() and "true" or "false" } ) + Task:I( { CargoIsAlive = Cargo:IsAlive() and "true" or "false" } ) if Cargo:IsAlive() then Task:CargoDeployed( TaskUnit, Cargo, DeployZone ) end diff --git a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua index 9d74b0743..88cc801cc 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua @@ -152,6 +152,114 @@ do -- TASK_CARGO_CSAR -- It is better you use the cargo dispatcher to generate CSAR tasks and it will work as it is intended. -- By doing this, CSAR tasking will become a dynamic experience. -- + -- # 2) Create a task using the @{Tasking.Task_Cargo_Dispatcher} module. + -- + -- Actually, it is better to **GENERATE** these tasks using the @{Tasking.Task_Cargo_Dispatcher} module. + -- Using the dispatcher module, transport tasks can be created much more easy. + -- + -- Find below an example how to use the TASK_CARGO_DISPATCHER class: + -- + -- + -- -- Find the HQ group. + -- HQ = GROUP:FindByName( "HQ", "Bravo" ) + -- + -- -- Create the command center with the name "Lima". + -- CommandCenter = COMMANDCENTER + -- :New( HQ, "Lima" ) + -- + -- -- Create the mission, for the command center, with the name "CSAR Mission", a "Tactical" mission, with the mission briefing "Rescue downed pilots.", for the RED coalition. + -- Mission = MISSION + -- :New( CommandCenter, "CSAR Mission", "Tactical", "Rescue downed pilots.", coalition.side.RED ) + -- + -- -- Create the SET of GROUPs containing clients (players) that will transport the cargo. + -- -- These are have a name that start with "Rescue" and are of the "red" coalition. + -- AttackGroups = SET_GROUP:New():FilterCoalitions( "red" ):FilterPrefixes( "Rescue" ):FilterStart() + -- + -- + -- -- Here we create the TASK_CARGO_DISPATCHER object! This is where we assign the dispatcher to generate tasks in the Mission for the AttackGroups. + -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, AttackGroups ) + -- + -- + -- -- Here the task dispatcher will generate automatically CSAR tasks once a pilot ejects. + -- TaskDispatcher:StartCSARTasks( + -- "CSAR", + -- { ZONE_UNIT:New( "Hospital", STATIC:FindByName( "Hospital" ), 100 ) }, + -- "One of our pilots has ejected. Go out to Search and Rescue our pilot!\n" .. + -- "Use the radio menu to let the command center assist you with the CSAR tasking." + -- ) + -- + -- # 3) Handle cargo task events. + -- + -- When a player is picking up and deploying cargo using his carrier, events are generated by the tasks. These events can be captured and tailored with your own code. + -- + -- In order to properly capture the events and avoid mistakes using the documentation, it is advised that you execute the following actions: + -- + -- * **Copy / Paste** the code section into your script. + -- * **Change** the CLASS literal to the task object name you have in your script. + -- * Within the function, you can now **write your own code**! + -- * **IntelliSense** will recognize the type of the variables provided by the function. Note: the From, Event and To variables can be safely ignored, + -- but you need to declare them as they are automatically provided by the event handling system of MOOSE. + -- + -- You can send messages or fire off any other events within the code section. The sky is the limit! + -- + -- NOTE: CSAR tasks are actually automatically created by the TASK_CARGO_DISPATCHER. So the underlying is not really applicable for mission designers as they will use the dispatcher instead + -- of capturing these events from manually created CSAR tasks! + -- + -- ## 3.1) Handle the **CargoPickedUp** event. + -- + -- Find below an example how to tailor the **CargoPickedUp** event, generated by the CSARTask: + -- + -- function CSARTask:OnAfterCargoPickedUp( From, Event, To, TaskUnit, Cargo ) + -- + -- MESSAGE:NewType( "Unit " .. TaskUnit:GetName().. " has picked up cargo.", MESSAGE.Type.Information ):ToAll() + -- + -- end + -- + -- If you want to code your own event handler, use this code fragment to tailor the event when a player carrier has picked up a cargo object in the CarrierGroup. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- + -- --- CargoPickedUp event handler OnAfter for CLASS. + -- -- @param #CLASS self + -- -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- -- @param #string To A string that contains the "*to state name*" when the event was triggered. + -- -- @param Wrapper.Unit#UNIT TaskUnit The unit (client) of the player that has picked up the cargo. + -- -- @param Cargo.Cargo#CARGO Cargo The cargo object that has been picked up. Note that this can be a CARGO_GROUP, CARGO_CRATE or CARGO_SLINGLOAD object! + -- function CLASS:OnAfterCargoPickedUp( From, Event, To, TaskUnit, Cargo ) + -- + -- -- Write here your own code. + -- + -- end + -- + -- + -- ## 3.2) Handle the **CargoDeployed** event. + -- + -- Find below an example how to tailor the **CargoDeployed** event, generated by the CSARTask: + -- + -- function CSARTask:OnAfterCargoDeployed( From, Event, To, TaskUnit, Cargo, DeployZone ) + -- + -- MESSAGE:NewType( "Unit " .. TaskUnit:GetName().. " has deployed cargo at zone " .. DeployZone:GetName(), MESSAGE.Type.Information ):ToAll() + -- + -- end + -- + -- If you want to code your own event handler, use this code fragment to tailor the event when a player carrier has deployed a cargo object from the CarrierGroup. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- + -- + -- --- CargoDeployed event handler OnAfter for CLASS. + -- -- @param #CLASS self + -- -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- -- @param #string To A string that contains the "*to state name*" when the event was triggered. + -- -- @param Wrapper.Unit#UNIT TaskUnit The unit (client) of the player that has deployed the cargo. + -- -- @param Cargo.Cargo#CARGO Cargo The cargo object that has been deployed. Note that this can be a CARGO_GROUP, CARGO_CRATE or CARGO_SLINGLOAD object! + -- -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. + -- function CLASS:OnAfterCargoDeployed( From, Event, To, TaskUnit, Cargo, DeployZone ) + -- + -- -- Write here your own code. + -- + -- end + -- -- === -- -- @field #TASK_CARGO_CSAR diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index e82ac8ba3..9f81a5d79 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -261,6 +261,97 @@ do -- TASK_CARGO_DISPATCHER -- Use the @{#TASK_CARGO_DISPATCHER.SetCSARDeployZone}() to setup one deployment zone, and @{#TASK_CARGO_DISPATCHER.SetCSARDeployZones}() to setup multiple default deployment zones in one call. -- -- + -- # 5) Handle cargo task events. + -- + -- When a player is picking up and deploying cargo using his carrier, events are generated by the dispatcher. These events can be captured and tailored with your own code. + -- + -- In order to properly capture the events and avoid mistakes using the documentation, it is advised that you execute the following actions: + -- + -- * **Copy / Paste** the code section into your script. + -- * **Change** the CLASS literal to the task object name you have in your script. + -- * Within the function, you can now **write your own code**! + -- * **IntelliSense** will recognize the type of the variables provided by the function. Note: the From, Event and To variables can be safely ignored, + -- but you need to declare them as they are automatically provided by the event handling system of MOOSE. + -- + -- You can send messages or fire off any other events within the code section. The sky is the limit! + -- + -- First, we need to create a TASK_CARGO_DISPATCHER object. + -- + -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, PilotGroupSet ) + -- + -- Second, we create a new cargo transport task for the transportation of workmaterials. + -- + -- TaskDispatcher:AddTransportTask( + -- "Transport workmaterials", + -- WorkmaterialsCargoSet, + -- "Transport the workers, engineers and the equipment near the Workplace." ) + -- + -- Note that we don't really need to keep the resulting task, it is kept internally also in the dispatcher. + -- + -- Using the `TaskDispatcher` object, we can now cpature the CargoPickedUp and CargoDeployed events. + -- + -- ## 5.1) Handle the **CargoPickedUp** event. + -- + -- Find below an example how to tailor the **CargoPickedUp** event, generated by the `TaskDispatcher`: + -- + -- function TaskDispatcher:OnAfterCargoPickedUp( From, Event, To, Task, TaskPrefix, TaskUnit, Cargo ) + -- + -- MESSAGE:NewType( "Unit " .. TaskUnit:GetName().. " has picked up cargo for task " .. Task:GetName() .. ".", MESSAGE.Type.Information ):ToAll() + -- + -- end + -- + -- If you want to code your own event handler, use this code fragment to tailor the event when a player carrier has picked up a cargo object in the CarrierGroup. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- + -- --- CargoPickedUp event handler OnAfter for CLASS. + -- -- @param #CLASS self + -- -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- -- @param #string To A string that contains the "*to state name*" when the event was triggered. + -- -- @param Tasking.Task_Cargo#TASK_CARGO Task The cargo task for which the cargo has been picked up. Note that this will be a derived TAKS_CARGO object! + -- -- @param #string TaskPrefix The prefix of the task that was provided when the task was created. + -- -- @param Wrapper.Unit#UNIT TaskUnit The unit (client) of the player that has picked up the cargo. + -- -- @param Cargo.Cargo#CARGO Cargo The cargo object that has been picked up. Note that this can be a CARGO_GROUP, CARGO_CRATE or CARGO_SLINGLOAD object! + -- function CLASS:OnAfterCargoPickedUp( From, Event, To, Task, TaskPrefix, TaskUnit, Cargo ) + -- + -- -- Write here your own code. + -- + -- end + -- + -- + -- ## 5.2) Handle the **CargoDeployed** event. + -- + -- Find below an example how to tailor the **CargoDeployed** event, generated by the `TaskDispatcher`: + -- + -- function WorkplaceTask:OnAfterCargoDeployed( From, Event, To, Task, TaskPrefix, TaskUnit, Cargo, DeployZone ) + -- + -- MESSAGE:NewType( "Unit " .. TaskUnit:GetName().. " has deployed cargo at zone " .. DeployZone:GetName() .. " for task " .. Task:GetName() .. ".", MESSAGE.Type.Information ):ToAll() + -- + -- Helos[ math.random(1,#Helos) ]:Spawn() + -- EnemyHelos[ math.random(1,#EnemyHelos) ]:Spawn() + -- end + -- + -- If you want to code your own event handler, use this code fragment to tailor the event when a player carrier has deployed a cargo object from the CarrierGroup. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- + -- + -- --- CargoDeployed event handler OnAfter for CLASS. + -- -- @param #CLASS self + -- -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- -- @param #string To A string that contains the "*to state name*" when the event was triggered. + -- -- @param Tasking.Task_Cargo#TASK_CARGO Task The cargo task for which the cargo has been deployed. Note that this will be a derived TAKS_CARGO object! + -- -- @param #string TaskPrefix The prefix of the task that was provided when the task was created. + -- -- @param Wrapper.Unit#UNIT TaskUnit The unit (client) of the player that has deployed the cargo. + -- -- @param Cargo.Cargo#CARGO Cargo The cargo object that has been deployed. Note that this can be a CARGO_GROUP, CARGO_CRATE or CARGO_SLINGLOAD object! + -- -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. + -- function CLASS:OnAfterCargoDeployed( From, Event, To, Task, TaskPrefix, TaskUnit, Cargo, DeployZone ) + -- + -- -- Write here your own code. + -- + -- end + -- + -- -- -- @field #TASK_CARGO_DISPATCHER TASK_CARGO_DISPATCHER = { @@ -288,6 +379,8 @@ do -- TASK_CARGO_DISPATCHER self.Mission = Mission self:AddTransition( "Started", "Assign", "Started" ) + self:AddTransition( "Started", "CargoPickedUp", "Started" ) + self:AddTransition( "Started", "CargoDeployed", "Started" ) --- OnAfter Transition Handler for Event Assign. -- @function [parent=#TASK_CARGO_DISPATCHER] OnAfterAssign @@ -462,6 +555,7 @@ do -- TASK_CARGO_DISPATCHER self.CSAR[CSARTaskName].PilotGroup = CSARGroup self.CSAR[CSARTaskName].Briefing = CSARBriefing self.CSAR[CSARTaskName].Task = nil + self.CSAR[CSARTaskName].TaskPrefix = CSARTaskPrefix return CSARTaskName end @@ -547,16 +641,17 @@ do -- TASK_CARGO_DISPATCHER -- -- Here we set a TransportDeployZone. We use the WorkplaceTask as the reference, and provide a ZONE object. -- TaskDispatcher:SetTransportDeployZone( WorkplaceTask, ZONE:New( "Workplace" ) ) -- - function TASK_CARGO_DISPATCHER:AddTransportTask( TaskName, SetCargo, Briefing ) + function TASK_CARGO_DISPATCHER:AddTransportTask( TaskPrefix, SetCargo, Briefing ) self.TransportCount = self.TransportCount + 1 - local TaskName = string.format( ( TaskName or "Transport" ) .. ".%03d", self.TransportCount ) + local TaskName = string.format( ( TaskPrefix or "Transport" ) .. ".%03d", self.TransportCount ) self.Transport[TaskName] = {} self.Transport[TaskName].SetCargo = SetCargo self.Transport[TaskName].Briefing = Briefing self.Transport[TaskName].Task = nil + self.Transport[TaskName].TaskPrefix = TaskPrefix self:ManageTasks() @@ -663,6 +758,7 @@ do -- TASK_CARGO_DISPATCHER -- New CSAR Task local SetCargo = self:EvaluateCSAR( CSAR.PilotGroup ) CSAR.Task = TASK_CARGO_CSAR:New( Mission, self.SetGroup, CSARName, SetCargo, CSAR.Briefing ) + CSAR.Task.TaskPrefix = CSAR.TaskPrefix -- We keep the TaskPrefix for further reference! Mission:AddTask( CSAR.Task ) TaskReport:Add( CSARName ) if CSAR.DeployZones then @@ -670,6 +766,17 @@ do -- TASK_CARGO_DISPATCHER else CSAR.Task:SetDeployZones( self.DefaultDeployZones or {} ) end + + -- Now broadcast the onafterCargoPickedUp event to the Task Cargo Dispatcher. + function CSAR.Task.OnAfterCargoPickedUp( Task, From, Event, To, TaskUnit, Cargo ) + self:CargoPickedUp( Task, Task.TaskPrefix, TaskUnit, Cargo ) + end + + -- Now broadcast the onafterCargoDeployed event to the Task Cargo Dispatcher. + function CSAR.Task.OnAfterCargoDeployed( Task, From, Event, To, TaskUnit, Cargo, DeployZone ) + self:CargoDeployed( Task, Task.TaskPrefix, TaskUnit, Cargo, DeployZone ) + end + end end @@ -680,6 +787,7 @@ do -- TASK_CARGO_DISPATCHER if not Transport.Task then -- New Transport Task Transport.Task = TASK_CARGO_TRANSPORT:New( Mission, self.SetGroup, TransportName, Transport.SetCargo, Transport.Briefing ) + Transport.Task.TaskPrefix = Transport.TaskPrefix -- We keep the TaskPrefix for further reference! Mission:AddTask( Transport.Task ) TaskReport:Add( TransportName ) function Transport.Task.OnEnterSuccess( Task, From, Event, To ) @@ -697,6 +805,17 @@ do -- TASK_CARGO_DISPATCHER function Transport.Task.OnEnterAborted( Task, From, Event, To ) self:Aborted( Task ) end + + -- Now broadcast the onafterCargoPickedUp event to the Task Cargo Dispatcher. + function Transport.Task.OnAfterCargoPickedUp( Task, From, Event, To, TaskUnit, Cargo ) + self:CargoPickedUp( Task, Task.TaskPrefix, TaskUnit, Cargo ) + end + + -- Now broadcast the onafterCargoDeployed event to the Task Cargo Dispatcher. + function Transport.Task.OnAfterCargoDeployed( Task, From, Event, To, TaskUnit, Cargo, DeployZone ) + self:CargoDeployed( Task, Task.TaskPrefix, TaskUnit, Cargo, DeployZone ) + end + end if Transport.DeployZones then diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua index 621be319d..484363978 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Transport.lua @@ -67,11 +67,8 @@ do -- TASK_CARGO_TRANSPORT -- -- === -- - -- A transport task can be created manually, but actually, it is better to **GENERATE** these tasks using the - -- @{Tasking.Task_Cargo_Dispatcher} module. - -- - -- Using the dispatcher, transport tasks can be created much more easy. - -- + -- A transport task can be created manually. + -- -- # 1) Create a transport task manually (code it). -- -- Although it is recommended to use the dispatcher, you can create a transport task yourself as a mission designer. @@ -141,6 +138,124 @@ do -- TASK_CARGO_TRANSPORT -- It is better you use the cargo dispatcher to create transport tasks and it will work as it is intended. -- By doing this, cargo transport tasking will become a dynamic experience. -- + -- + -- # 2) Create a task using the @{Tasking.Task_Cargo_Dispatcher} module. + -- + -- Actually, it is better to **GENERATE** these tasks using the @{Tasking.Task_Cargo_Dispatcher} module. + -- Using the dispatcher module, transport tasks can be created much more easy. + -- + -- Find below an example how to use the TASK_CARGO_DISPATCHER class: + -- + -- + -- -- Find the HQ group. + -- HQ = GROUP:FindByName( "HQ", "Bravo" ) + -- + -- -- Create the command center with the name "Lima". + -- CommandCenter = COMMANDCENTER + -- :New( HQ, "Lima" ) + -- + -- -- Create the mission, for the command center, with the name "Operation Cargo Fun", a "Tactical" mission, with the mission briefing "Transport Cargo", for the BLUE coalition. + -- Mission = MISSION + -- :New( CommandCenter, "Operation Cargo Fun", "Tactical", "Transport Cargo", coalition.side.BLUE ) + -- + -- -- Create the SET of GROUPs containing clients (players) that will transport the cargo. + -- -- These are have a name that start with "Transport" and are of the "blue" coalition. + -- TransportGroups = SET_GROUP:New():FilterCoalitions( "blue" ):FilterPrefixes( "Transport" ):FilterStart() + -- + -- + -- -- Here we create the TASK_CARGO_DISPATCHER object! This is where we assign the dispatcher to generate tasks in the Mission for the TransportGroups. + -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, TransportGroups ) + -- + -- + -- -- Here we declare the SET of CARGOs called "Workmaterials". + -- local CargoSetWorkmaterials = SET_CARGO:New():FilterTypes( "Workmaterials" ):FilterStart() + -- + -- -- Here we declare (add) CARGO_GROUP objects of various types, that are filtered and added in the CargoSetworkmaterials cargo set. + -- -- These cargo objects have the type "Workmaterials" which is exactly the type of cargo the CargoSetworkmaterials is filtering on. + -- local EngineerCargoGroup = CARGO_GROUP:New( GROUP:FindByName( "Engineers" ), "Workmaterials", "Engineers", 250 ) + -- local ConcreteCargo = CARGO_SLINGLOAD:New( STATIC:FindByName( "Concrete" ), "Workmaterials", "Concrete", 150, 50 ) + -- local CrateCargo = CARGO_CRATE:New( STATIC:FindByName( "Crate" ), "Workmaterials", "Crate", 150, 50 ) + -- local EnginesCargo = CARGO_CRATE:New( STATIC:FindByName( "Engines" ), "Workmaterials", "Engines", 150, 50 ) + -- local MetalCargo = CARGO_CRATE:New( STATIC:FindByName( "Metal" ), "Workmaterials", "Metal", 150, 50 ) + -- + -- -- And here we create a new WorkplaceTask, using the :AddTransportTask method of the TaskDispatcher. + -- local WorkplaceTask = TaskDispatcher:AddTransportTask( "Build a Workplace", CargoSetWorkmaterials, "Transport the workers, engineers and the equipment near the Workplace." ) + -- TaskDispatcher:SetTransportDeployZone( WorkplaceTask, ZONE:New( "Workplace" ) ) + -- + -- # 3) Handle cargo task events. + -- + -- When a player is picking up and deploying cargo using his carrier, events are generated by the tasks. These events can be captured and tailored with your own code. + -- + -- In order to properly capture the events and avoid mistakes using the documentation, it is advised that you execute the following actions: + -- + -- * **Copy / Paste** the code section into your script. + -- * **Change** the CLASS literal to the task object name you have in your script. + -- * Within the function, you can now **write your own code**! + -- * **IntelliSense** will recognize the type of the variables provided by the function. Note: the From, Event and To variables can be safely ignored, + -- but you need to declare them as they are automatically provided by the event handling system of MOOSE. + -- + -- You can send messages or fire off any other events within the code section. The sky is the limit! + -- + -- + -- ## 3.1) Handle the CargoPickedUp event. + -- + -- Find below an example how to tailor the **CargoPickedUp** event, generated by the WorkplaceTask: + -- + -- function WorkplaceTask:OnAfterCargoPickedUp( From, Event, To, TaskUnit, Cargo ) + -- + -- MESSAGE:NewType( "Unit " .. TaskUnit:GetName().. " has picked up cargo.", MESSAGE.Type.Information ):ToAll() + -- + -- end + -- + -- If you want to code your own event handler, use this code fragment to tailor the event when a player carrier has picked up a cargo object in the CarrierGroup. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- + -- --- CargoPickedUp event handler OnAfter for CLASS. + -- -- @param #CLASS self + -- -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- -- @param #string To A string that contains the "*to state name*" when the event was triggered. + -- -- @param Wrapper.Unit#UNIT TaskUnit The unit (client) of the player that has picked up the cargo. + -- -- @param Cargo.Cargo#CARGO Cargo The cargo object that has been picked up. Note that this can be a CARGO_GROUP, CARGO_CRATE or CARGO_SLINGLOAD object! + -- function CLASS:OnAfterCargoPickedUp( From, Event, To, TaskUnit, Cargo ) + -- + -- -- Write here your own code. + -- + -- end + -- + -- + -- ## 3.2) Handle the CargoDeployed event. + -- + -- Find below an example how to tailor the **CargoDeployed** event, generated by the WorkplaceTask: + -- + -- function WorkplaceTask:OnAfterCargoDeployed( From, Event, To, TaskUnit, Cargo, DeployZone ) + -- + -- MESSAGE:NewType( "Unit " .. TaskUnit:GetName().. " has deployed cargo at zone " .. DeployZone:GetName(), MESSAGE.Type.Information ):ToAll() + -- + -- Helos[ math.random(1,#Helos) ]:Spawn() + -- EnemyHelos[ math.random(1,#EnemyHelos) ]:Spawn() + -- end + -- + -- If you want to code your own event handler, use this code fragment to tailor the event when a player carrier has deployed a cargo object from the CarrierGroup. + -- You can use this event handler to post messages to players, or provide status updates etc. + -- + -- + -- --- CargoDeployed event handler OnAfter for CLASS. + -- -- @param #CLASS self + -- -- @param #string From A string that contains the "*from state name*" when the event was triggered. + -- -- @param #string Event A string that contains the "*event name*" when the event was triggered. + -- -- @param #string To A string that contains the "*to state name*" when the event was triggered. + -- -- @param Wrapper.Unit#UNIT TaskUnit The unit (client) of the player that has deployed the cargo. + -- -- @param Cargo.Cargo#CARGO Cargo The cargo object that has been deployed. Note that this can be a CARGO_GROUP, CARGO_CRATE or CARGO_SLINGLOAD object! + -- -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. + -- function CLASS:OnAfterCargoDeployed( From, Event, To, TaskUnit, Cargo, DeployZone ) + -- + -- -- Write here your own code. + -- + -- end + -- + -- + -- -- === -- -- @field #TASK_CARGO_TRANSPORT From ab5f08019134bbf598fb0604eea446f4c8fec763 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Tue, 18 Sep 2018 11:39:36 +0200 Subject: [PATCH 375/420] Warehouse self:I --> self:T --- Moose Development/Moose/Functional/Warehouse.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index cdfb5c6ec..eed4d3424 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -3199,7 +3199,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- No transport unit requested. Assets go by themselfes. if Request.transporttype==WAREHOUSE.TransportType.SELFPROPELLED then - self:I(self.wid..string.format("Got selfpropelled request for %d assets.",_spawngroups:Count())) + self:T2(self.wid..string.format("Got selfpropelled request for %d assets.",_spawngroups:Count())) for _,_spawngroup in pairs(_spawngroups:GetSetObjects()) do @@ -3209,7 +3209,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Route cargo to their destination. if _cargocategory==Group.Category.GROUND then - self:I(self.wid..string.format("Route ground group %s.", group:GetName())) + self:T2(self.wid..string.format("Route ground group %s.", group:GetName())) -- Random place in the spawn zone of the requesting warehouse. local ToCoordinate=Request.warehouse.spawnzone:GetRandomCoordinate() @@ -3219,20 +3219,20 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) self:_RouteGround(group, Request) elseif _cargocategory==Group.Category.AIRPLANE or _cargocategory==Group.Category.HELICOPTER then - self:I(self.wid..string.format("Route airborne group %s.", group:GetName())) + self:T2(self.wid..string.format("Route airborne group %s.", group:GetName())) -- Route plane to the requesting warehouses airbase. -- Actually, the route is already set. We only need to activate the uncontrolled group. self:_RouteAir(group, Request.airbase) elseif _cargocategory==Group.Category.SHIP then - self:I(self.wid..string.format("Route naval group %s.", group:GetName())) + self:T2(self.wid..string.format("Route naval group %s.", group:GetName())) -- Route plane to the requesting warehouses airbase. self:_RouteNaval(group, Request) elseif _cargocategory==Group.Category.TRAIN then - self:I(self.wid..string.format("Route train group %s.", group:GetName())) + self:T2(self.wid..string.format("Route train group %s.", group:GetName())) -- Route train to the rail connection of the requesting warehouse. self:_RouteTrain(group, Request.warehouse.rail) From 02aff87b9fb317ef9edf5bcb10258f2f1cbcbad6 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 18 Sep 2018 13:08:02 +0200 Subject: [PATCH 376/420] Frank, pushing a fix for APCs, please test. Helicopters will be adapted and planes too later. --- Moose Development/Moose/AI/AI_Cargo.lua | 36 +++++++++++++++++++++ Moose Development/Moose/AI/AI_Cargo_APC.lua | 12 +++---- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua index 9b0a8e6a1..9b743d555 100644 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -166,6 +166,37 @@ function AI_CARGO:IsRelocating() end +--- On after Pickup event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP APC +-- @param From +-- @param Event +-- @param To +-- @param Core.Point#COORDINATE Coordinate of the pickup point. +-- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. +-- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. +function AI_CARGO:onafterPickup( APC, From, Event, To, Coordinate, Speed, PickupZone ) + + self.Transporting = false + self.Relocating = true + +end + + +--- On after Deploy event. +-- @param #AI_CARGO self +-- @param Wrapper.Group#GROUP APC +-- @param From +-- @param Event +-- @param To +-- @param Core.Point#COORDINATE Coordinate Deploy place. +-- @param #number Speed Speed in km/h to drive to the depoly coordinate. Default is 50% of max possible speed the unit can go. +function AI_CARGO:onafterDeploy( APC, From, Event, To, Coordinate, Speed, DeployZone ) + + self.Relocating = false + self.Transporting = true + +end --- On before Load event. -- @param #AI_CARGO self @@ -322,6 +353,9 @@ function AI_CARGO:onafterPickedUp( Carrier, From, Event, To, PickupZone ) self:F( { Carrier, From, Event, To } ) Carrier:RouteResume() + + self.Relocating = false + self.Transporting = true end @@ -434,5 +468,7 @@ function AI_CARGO:onafterDeployed( Carrier, From, Event, To, DeployZone ) self:__Guard( 0.1 ) + self.Transporting = false + end diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index ce9aa85b0..f1e19b2d5 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -355,8 +355,6 @@ function AI_CARGO_APC._Pickup( APC, self, Coordinate, Speed, PickupZone ) if APC:IsAlive() then self:Load( PickupZone ) - self.Relocating = false - self.Transporting = true end end @@ -367,8 +365,6 @@ function AI_CARGO_APC._Deploy( APC, self, Coordinate, DeployZone ) if APC:IsAlive() then self:Unload( DeployZone ) - self.Transporting = false - self.Relocating = false end end @@ -405,8 +401,7 @@ function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed, Pi AI_CARGO_APC._Pickup( APC, self, Coordinate, Speed, PickupZone ) end - self.Relocating = true - self.Transporting = false + self:GetParent( self, AI_CARGO_APC ).onafterPickup( self, APC, From, Event, To, Coordinate, Speed, PickupZone ) end end @@ -438,13 +433,14 @@ function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed, De APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. - self.Relocating = false - self.Transporting = true + self:GetParent( self, AI_CARGO_APC ).onafterDeploy( self, APC, From, Event, To, Coordinate, Speed, DeployZone ) + end end + --- On after Home event. -- @param #AI_CARGO_APC self -- @param Wrapper.Group#GROUP APC From 08fffb90049f55748cca5eeafd0cbdbdd4ae1d2c Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 18 Sep 2018 13:49:37 +0200 Subject: [PATCH 377/420] Improvements to cargo AI handling. --- Moose Development/Moose/AI/AI_Cargo.lua | 14 +++- Moose Development/Moose/AI/AI_Cargo_APC.lua | 15 ++++ .../Moose/AI/AI_Cargo_Airplane.lua | 81 +++++++------------ .../Moose/AI/AI_Cargo_Dispatcher_Airplane.lua | 7 +- .../Moose/AI/AI_Cargo_Helicopter.lua | 47 ++--------- 5 files changed, 63 insertions(+), 101 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua index 9b743d555..bdc33396a 100644 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -354,8 +354,18 @@ function AI_CARGO:onafterPickedUp( Carrier, From, Event, To, PickupZone ) Carrier:RouteResume() + local HasCargo = false + if Carrier and Carrier :IsAlive() then + for Cargo, CarrierUnit in pairs( self.Carrier_Cargo ) do + HasCargo = true + break + end + end + self.Relocating = false - self.Transporting = true + if HasCargo then + self.Transporting = true + end end @@ -466,8 +476,6 @@ end function AI_CARGO:onafterDeployed( Carrier, From, Event, To, DeployZone ) self:F( { Carrier, From, Event, To, DeployZone = DeployZone } ) - self:__Guard( 0.1 ) - self.Transporting = false end diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index f1e19b2d5..0ee079672 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -439,6 +439,21 @@ function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed, De end +--- On after Deployed event. +-- @param #AI_CARGO_APC self +-- @param Wrapper.Group#GROUP Carrier +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +function AI_CARGO_APC:onafterDeployed( APC, From, Event, To, DeployZone ) + self:F( { APC, From, Event, To, DeployZone = DeployZone } ) + + self:__Guard( 0.1 ) + + self:GetParent( self, AI_CARGO_APC ).onafterDeployed( self, APC, From, Event, To, DeployZone ) + +end --- On after Home event. diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 8081a88de..408fa515c 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -145,18 +145,6 @@ function AI_CARGO_AIRPLANE:New( Airplane, CargoSet ) end -function AI_CARGO_AIRPLANE:IsTransporting() - - return self.Transporting == true -end - -function AI_CARGO_AIRPLANE:IsRelocating() - - return self.Relocating == true -end - - - --- Set the Carrier (controllable). Also initializes events for carrier and defines the coalition. -- @param #AI_CARGO_AIRPLANE self -- @param Wrapper.Group#GROUP Airplane Transport plane. @@ -253,16 +241,12 @@ function AI_CARGO_AIRPLANE:onafterLanded( Airplane, From, Event, To ) if self.RoutePickup == true then env.info("FF load airplane "..Airplane:GetName()) self:Load( self.PickupZone ) - self.RoutePickup = false - self.Relocating = true end -- Aircraft was send to this airbase to deploy troops. Initiate unloading. if self.RouteDeploy == true then self:Unload() self.RouteDeploy = false - self.Transporting = false - self.Relocating = false end end @@ -326,8 +310,7 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate, end - self.Transporting = false - self.Relocating = true + self:GetParent( self, AI_CARGO_AIRPLANE ).onafterPickup( self, Airplane, From, Event, To, Coordinate, Speed, PickupZone ) else env.info("FF onafterpick aircraft not alive") end @@ -364,30 +347,12 @@ function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Coordinate, -- Set destination airbase for next :Route() command. self.Airbase = Airbase - self.Transporting = true - self.Relocating = false + self:GetParent( self, AI_CARGO_AIRPLANE ).onafterDeploy( self, Airplane, From, Event, To, Coordinate, Speed, DeployZone ) end end - ---- On after PickedUp event. All cargo is inside the carrier and ready to be transported. --- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane Cargo transport plane. --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. -function AI_CARGO_AIRPLANE:onafterPickedUp( Airplane, From, Event, To, PickupZone ) - self:F( { AirplaneGroup, From, Event, To } ) - - if Airplane and Airplane:IsAlive() then - self.Transporting = true -- This will only be executed when there is no cargo boarded anymore. The dispatcher will then kick-off the deploy cycle! - end -end - - --- On after Unload event. Cargo is beeing unloaded, i.e. the unboarding process is started. -- @param #AI_CARGO_AIRPLANE self -- @param Wrapper.Group#GROUP Airplane Cargo transport plane. @@ -422,22 +387,6 @@ end ---- On after Deployed event. --- @param #AI_CARGO_AIRPLANE self --- @param Wrapper.Group#GROUP Airplane Cargo transport plane. --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Cargo.Cargo#CARGO Cargo -function AI_CARGO_AIRPLANE:onafterDeployed( Airplane, From, Event, To, DeployZone ) - - if Airplane and Airplane:IsAlive() then - self.Transporting = false -- This will only be executed when there is no cargo onboard anymore. The dispatcher will then kick-off the pickup cycle! - end -end - - - --- Route the airplane from one airport or it's current position to another airbase. -- @param #AI_CARGO_AIRPLANE self @@ -510,3 +459,29 @@ function AI_CARGO_AIRPLANE:Route( Airplane, Airbase, Speed, Uncontrolled ) end end end + +--- On after Home event. Aircraft will be routed to their home base. +-- @param #AI_CARGO_AIRPLANE self +-- @param Wrapper.Group#GROUP Airplane The cargo plane. +-- @param From From state. +-- @param Event Event. +-- @param To To State. +-- @param Core.Point#COORDINATE Coordinate Home place (not used). +-- @param #number Speed Speed in km/h to fly to the home airbase (zone). Default is 80% of max possible speed the unit can go. +-- @param Core.Zone#ZONE_AIRBASE HomeZone The home airbase (zone) where the plane should return to. +function AI_CARGO_AIRPLANE:onafterHome(Airplane, From, Event, To, Coordinate, Speed, HomeZone ) + if Airplane and Airplane:IsAlive() then + + -- We are going home! + self.RouteHome = true + + -- Home Base. + local HomeBase=HomeZone:GetAirbase() + self.Airbase=HomeBase + + -- Now route the airplane home + self:Route(Airplane, HomeBase, Speed) + + end + +end \ No newline at end of file diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua index 1df08d56f..6193c1bc1 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua @@ -13,7 +13,8 @@ -- -- @module AI.AI_Cargo_Dispatcher_Airplane -- @image AI_Cargo_Dispatching_For_Airplanes.JPG --- + + --- @type AI_CARGO_DISPATCHER_AIRPLANE -- @extends AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER @@ -62,8 +63,8 @@ function AI_CARGO_DISPATCHER_AIRPLANE:New( AirplaneSet, CargoSet, PickupZoneSet, local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithZones( AirplaneSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) -- #AI_CARGO_DISPATCHER_AIRPLANE - self:SetDeploySpeed( 200, 150 ) - self:SetPickupSpeed( 200, 150 ) + self:SetDeploySpeed( 1200, 600 ) + self:SetPickupSpeed( 1200, 600 ) self:SetPickupRadius( 0, 0 ) self:SetDeployRadius( 0, 0 ) diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 284000275..0a71e9250 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -176,16 +176,6 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) return self end -function AI_CARGO_HELICOPTER:IsTransporting() - - return self.Transporting == true -end - -function AI_CARGO_HELICOPTER:IsRelocating() - - return self.Relocating == true -end - --- Set the Carrier. -- @param #AI_CARGO_HELICOPTER self @@ -392,33 +382,6 @@ end ---- On after PickedUp event, raised when all cargo has been loaded into the CarrierGroup. --- @param #AI_CARGO_HELICOPTER self --- @param Wrapper.Group#GROUP Helicopter --- @param #string From From state. --- @param #string Event Event. --- @param #string To To state. --- @param Cargo.Cargo#CARGO Cargo Cargo object. --- @return #boolean Cargo is loaded. --- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. -function AI_CARGO_HELICOPTER:onafterPickedUp( Helicopter, From, Event, To, PickupZone ) - self:F( { Helicopter, From, Event, To } ) - - local HasCargo = false - if Helicopter and Helicopter:IsAlive() then - for Cargo, CarrierUnit in pairs( self.Carrier_Cargo ) do - HasCargo = true - break - end - self.Relocating = false - if HasCargo then - self.Transporting = true - end - end -end - - - --- On after Deployed event. -- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter @@ -440,7 +403,8 @@ function AI_CARGO_HELICOPTER:onafterDeployed( Helicopter, From, Event, To, Deplo end, Helicopter ) - self.Transporting = false + self:GetParent( self, AI_CARGO_HELICOPTER ).onafterDeployed( self, Helicopter, From, Event, To, DeployZone ) + end @@ -506,8 +470,8 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin self.PickupZone = PickupZone - self.Relocating = true - self.Transporting = false + self:GetParent( self, AI_CARGO_HELICOPTER ).onafterPickup( self, Helicopter, From, Event, To, Coordinate, Speed, PickupZone ) + end end @@ -584,8 +548,7 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin -- Now route the helicopter Helicopter:Route( Route, 0 ) - self.Relocating = false - self.Transporting = true + self:GetParent( self, AI_CARGO_HELICOPTER ).onafterDeploy( self, Helicopter, From, Event, To, Coordinate, Speed, DeployZone ) end end From bab1d21cc0fb6fa960d7b0a46f5cea4046b192f3 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Tue, 18 Sep 2018 16:18:40 +0200 Subject: [PATCH 378/420] Warehouse v0.5.1 Final polish. --- Moose Development/Moose/Core/Spawn.lua | 2 +- Moose Development/Moose/Core/SpawnStatic.lua | 6 ++-- Moose Development/Moose/Core/Zone.lua | 2 ++ Moose Development/Moose/Functional/RAT.lua | 2 +- .../Moose/Functional/Warehouse.lua | 32 +++++++++++++------ 5 files changed, 28 insertions(+), 16 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 614422720..9437c4509 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1615,7 +1615,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT else - self:E(string.format("Group %s spawning at airbase %s on parking spot id %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), parkingindex[UnitID])) + self:T(string.format("Group %s spawning at airbase %s on parking spot id %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), parkingindex[UnitID])) -- Get coordinates of parking spot. SpawnTemplate.units[UnitID].x = parkingspots[UnitID].x diff --git a/Moose Development/Moose/Core/SpawnStatic.lua b/Moose Development/Moose/Core/SpawnStatic.lua index c65f91ef2..742bf6eac 100644 --- a/Moose Development/Moose/Core/SpawnStatic.lua +++ b/Moose Development/Moose/Core/SpawnStatic.lua @@ -191,16 +191,14 @@ end --- Respawns the original @{Static}. -- @param #SPAWNSTATIC self --- @param DCS#country.id (Optional) The country ID of the static after respawning.. -- @return #SPAWNSTATIC -function SPAWNSTATIC:ReSpawn(countryid) +function SPAWNSTATIC:ReSpawn() local StaticTemplate, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate( self.SpawnTemplatePrefix ) if StaticTemplate then - --local CountryID = countryid or (self.CountryID or CountryID) - + local StaticUnitTemplate = StaticTemplate.units[1] StaticTemplate.route = nil StaticTemplate.groupId = nil diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 295adabb8..b434295f0 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -312,6 +312,7 @@ end -- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color. function ZONE_BASE:SmokeZone( SmokeColor ) self:F2( SmokeColor ) + end --- Set the randomization probability of a zone to be selected. @@ -330,6 +331,7 @@ end -- @return #number A value between 0 and 1. 0 = 0% and 1 = 100% probability. function ZONE_BASE:GetZoneProbability() self:F2() + return self.ZoneProbability end diff --git a/Moose Development/Moose/Functional/RAT.lua b/Moose Development/Moose/Functional/RAT.lua index 75bfe545d..7b9213bde 100644 --- a/Moose Development/Moose/Functional/RAT.lua +++ b/Moose Development/Moose/Functional/RAT.lua @@ -549,7 +549,7 @@ RAT.id="RAT | " --- RAT version. -- @list version RAT.version={ - version = "2.3.3", + version = "2.3.4", print = true, } diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index eed4d3424..ae96b68a0 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -640,7 +640,7 @@ -- -- A red BMP has made it through our defence lines and drives towards our unprotected airbase at Senaki. -- Once the BMP captures the airbase (DCS S\_EVENT_\BASE_\CAPTURED is evaluated) the warehouse at Senaki lost its air infrastructure and it is not --- possible any more to spawn airborne units. All requests for airborne units are rejected and not queued in this case. +-- possible any more to spawn airborne units. All requests for airborne units are rejected and cancelled in this case. -- -- The red BMP then drives further to the warehouse. Once it enters the warehouse zone (500 m radius around the warehouse building), the warehouse is -- considered to be under attack. This triggers the event **Attacked**. The @{#WAREHOUSE.OnAfterAttacked} function can be used to react to this situation. @@ -649,11 +649,10 @@ -- *all* ground assets are automatically spawned and assigned to defend the warehouse. Once/if the attack is defeated, these assets go automatically back -- into the warehouse stock. -- --- If the red coalition manages to capture our warehouse, all assets go into their possession. Here, even our airbase has been captured. Therefore, a (self) request --- to the warehouse will now spawn the F/A-18 fighters as red units. Note, that the request could also some from another red warehouse. In that case, --- the planes would take off and (if they make it) be added to the red warehouse. So you can steal valuable assets from your enemy if he is not careful. +-- If the red coalition manages to capture our warehouse, all assets go into their possession. Now red tries to steal three F/A-18 flights and send them to +-- Sukhumi. These aircraft will be spawned and begin to taxi. However, ... -- --- Here, we simply activate a blue external unit which drives to the warehouse, destroyes the red intruder and re-captures our warehouse. +-- A blue Bradley is in the area and will attemt to recapture the warehouse. It might also catch the red F/A-18s before they take off. -- -- -- Start warehouses. -- warehouse.Senaki:Start() @@ -685,6 +684,9 @@ -- -- Sukhumi tries to "steals" three F/A-18 from Senaki and brings them to Sukhumi. -- -- Well, actually the aircraft wont make it because blue1 will kill it on the taxi way leaving a blood bath. But that's life! -- warehouse.Senaki:AddRequest(warehouse.Sukhumi, WAREHOUSE.Descriptor.CATEGORY, Group.Category.AIRPLANE, 3) +-- warehouse.Senaki.warehouse:SmokeRed() +-- elseif Coalition==coalition.side.BLUE then +-- warehouse.Senaki.warehouse:SmokeBlue() -- end -- -- -- Activate a blue vehicle to re-capture the warehouse. It will drive to the warehouse zone and kill the red intruder. @@ -1306,7 +1308,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.5.0" +WAREHOUSE.version="0.5.1" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -3145,6 +3147,12 @@ end -- @param #WAREHOUSE.Queueitem Request Information table of the request. function WAREHOUSE:onafterRequest(From, Event, To, Request) + -- Info message. + local text=string.format("Warehouse %s: Processing request id=%d from warehouse %s.\n", self.alias, Request.uid, Request.warehouse.alias) + text=text..string.format("Requested %s assets of %s=%s.\n", tostring(Request.nasset), Request.assetdesc, Request.assetdescval) + text=text..string.format("Transports %s of type %s.", tostring(Request.ntransport), tostring(Request.transporttype)) + self:_InfoMessage(text, 5) + ------------------------------------------------------------------------------------------------------------------------------------ -- Cargo assets. ------------------------------------------------------------------------------------------------------------------------------------ @@ -3213,7 +3221,11 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Random place in the spawn zone of the requesting warehouse. local ToCoordinate=Request.warehouse.spawnzone:GetRandomCoordinate() - ToCoordinate:MarkToAll(string.format("Destination of group %s", group:GetName())) + + -- Debug marker. + if self.Debug then + ToCoordinate:MarkToAll(string.format("Destination of group %s", group:GetName())) + end -- Route ground. self:_RouteGround(group, Request) @@ -5402,8 +5414,8 @@ function WAREHOUSE:_CheckRequestNow(request) if not _enough then local text=string.format("Warehouse %s: Request denied! Not enough (cargo) assets currently available.", self.alias) self:_InfoMessage(text, 5) - text=string.format("Enough=%s, #_assets=%d, _nassets=%d, request.nasset=%s", tostring(_enough), #_assets,_nassets, tostring(request.nasset)) - env.info(text) + --text=string.format("Enough=%s, #_assets=%d, _nassets=%d, request.nasset=%s", tostring(_enough), #_assets,_nassets, tostring(request.nasset)) + --env.info(text) return false end @@ -6514,7 +6526,7 @@ function WAREHOUSE:_UpdateWarehouseMarkText() local _data=self:GetStockInfo(self.stock) -- Text. - local text=string.format("Warehouse state: %s\nStock - total assets %d:\n", self:GetState(), #self.stock) + local text=string.format("Warehouse state: %s\nTotal assets in stock %d:\n", self:GetState(), #self.stock) for _attribute,_count in pairs(_data) do if _count>0 then From 35e41570dfef18aef53ae48b10e325c31ddbaf83 Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Tue, 18 Sep 2018 16:41:17 +0200 Subject: [PATCH 379/420] Spawn and Group Removed debug markers. --- Moose Development/Moose/Core/Spawn.lua | 2 +- Moose Development/Moose/Wrapper/Group.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 9437c4509..8ba3908ca 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -1622,7 +1622,7 @@ function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude, TerminalT SpawnTemplate.units[UnitID].y = parkingspots[UnitID].z SpawnTemplate.units[UnitID].alt = parkingspots[UnitID].y - parkingspots[UnitID]:MarkToAll(string.format("Group %s spawning at airbase %s on parking spot id %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), parkingindex[UnitID])) + --parkingspots[UnitID]:MarkToAll(string.format("Group %s spawning at airbase %s on parking spot id %d", self.SpawnTemplatePrefix, SpawnAirbase:GetName(), parkingindex[UnitID])) end else diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 85894c489..d12f613d0 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1507,7 +1507,7 @@ function GROUP:RespawnAtCurrentAirbase(SpawnTemplate, Takeoff, Uncontrolled) -- -- Get unit coordinates for respawning position. local uc=unit:GetCoordinate() - uc:MarkToAll(string.format("re-spawnplace %s terminal %d", unit:GetName(), TermialID)) + --uc:MarkToAll(string.format("re-spawnplace %s terminal %d", unit:GetName(), TermialID)) SpawnTemplate.units[UnitID].x = uc.x --Parkingspot.x SpawnTemplate.units[UnitID].y = uc.z --Parkingspot.z From 337e7eab53d33823366c5f6f02b71df953ced6d7 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Wed, 19 Sep 2018 05:14:09 +0200 Subject: [PATCH 380/420] Fixed the remaining issues with APCs, Helicopter and Airplane cargo dispatchers. --- Moose Development/Moose/AI/AI_Cargo.lua | 25 ++++----- Moose Development/Moose/AI/AI_Cargo_APC.lua | 2 +- .../Moose/AI/AI_Cargo_Dispatcher.lua | 51 +++---------------- .../Moose/AI/AI_Cargo_Dispatcher_Airplane.lua | 2 + .../Moose/Wrapper/Controllable.lua | 2 +- 5 files changed, 24 insertions(+), 58 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua index bdc33396a..a0686b9c6 100644 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -230,13 +230,13 @@ function AI_CARGO:onbeforeLoad( Carrier, From, Event, To, PickupZone ) local Carrier_Count = #Carrier_List local Carrier_Index = 1 + local Loaded = false + for _, Cargo in UTILS.spairs( self.CargoSet:GetSet(), function( t, a, b ) return t[a]:GetWeight() > t[b]:GetWeight() end ) do local Cargo = Cargo -- Cargo.Cargo#CARGO self:F( { IsUnLoaded = Cargo:IsUnLoaded(), IsDeployed = Cargo:IsDeployed(), Cargo:GetName(), Carrier:GetName() } ) - local Loaded = false - -- Try all Carriers, but start from the one according the Carrier_Index for Carrier_Loop = 1, #Carrier_List do @@ -248,7 +248,7 @@ function AI_CARGO:onbeforeLoad( Carrier, From, Event, To, PickupZone ) Carrier_Index = 1 end - if Cargo:IsUnLoaded() then -- and not Cargo:IsDeployed() then + if Cargo:IsUnLoaded() and not Cargo:IsDeployed() then if Cargo:IsInLoadRadius( CarrierUnit:GetCoordinate() ) then self:F( { "In radius", CarrierUnit:GetName() } ) @@ -277,12 +277,13 @@ function AI_CARGO:onbeforeLoad( Carrier, From, Event, To, PickupZone ) end - if not Loaded then - -- No loading happened, so we need to pickup something else. - self.Relocating = false - end end + + if not Loaded == true then + -- No loading happened, so we need to pickup something else. + self.Relocating = false + end end return Boarding @@ -309,7 +310,7 @@ function AI_CARGO:onafterBoard( Carrier, From, Event, To, Cargo, CarrierUnit, Pi end end - self:__Loaded( 10, Cargo, CarrierUnit, PickupZone ) + self:__Loaded( 0.1, Cargo, CarrierUnit, PickupZone ) end @@ -337,7 +338,7 @@ function AI_CARGO:onafterLoaded( Carrier, From, Event, To, Cargo, PickupZone ) end if Loaded then - self:PickedUp( PickupZone ) + self:__PickedUp( 0.1, PickupZone ) end end @@ -363,9 +364,9 @@ function AI_CARGO:onafterPickedUp( Carrier, From, Event, To, PickupZone ) end self.Relocating = false - if HasCargo then - self.Transporting = true - end + if HasCargo then + self.Transporting = true + end end diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 0ee079672..76ebfc397 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -269,7 +269,7 @@ end -- @param #string Event Event. -- @param #string To To state. function AI_CARGO_APC:onafterMonitor( APC, From, Event, To ) - self:F( { APC, From, Event, To } ) + self:F( { APC, From, Event, To, IsTransporting = self:IsTransporting() } ) if self.CombatRadius > 0 then if APC and APC:IsAlive() then diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index be75508e5..edcf18846 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -438,7 +438,8 @@ function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo ) self:AddTransition( "Monitoring", "Home", "Monitoring" ) - self.MonitorTimeInterval = 30 + self:SetMonitorTimeInterval( 30 ) + self.DeployRadiusInner = 200 self.DeployRadiusOuter = 500 @@ -481,29 +482,13 @@ function AI_CARGO_DISPATCHER:NewWithZones( SetCarrier, SetCargo, PickupZoneSet, return self end - ---- Creates a new AI_CARGO_DISPATCHER object. +--- Set the monitor time interval. -- @param #AI_CARGO_DISPATCHER self --- @param Core.Set#SET_GROUP SetCarrier --- @param Core.Set#SET_CARGO SetCargo --- @param Core.Set#SET_AIRBASE PickupAirbasesSet --- @param Core.Set#SET_AIRBASE DeployAirbasesSet +-- @param #number MonitorTimeInterval -- @return #AI_CARGO_DISPATCHER --- @usage --- --- -- Create a new cargo dispatcher --- SetCarriers = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() --- SetCargos = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() --- PickupAirbasesSet = SET_AIRBASES:New() --- DeployAirbasesSet = SET_AIRBASES:New() --- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, PickupAirbasesSet, DeployAirbasesSet ) --- -function AI_CARGO_DISPATCHER:NewWithAirbases( SetCarriers, SetCargos, PickupAirbasesSet, DeployAirbasesSet ) +function AI_CARGO_DISPATCHER:SetMonitorTimeInterval( MonitorTimeInterval ) - local self = AI_CARGO_DISPATCHER:New( SetCarriers, SetCargos ) -- #AI_CARGO_DISPATCHER - - self.DeployAirbasesSet = DeployAirbasesSet - self.PickupAirbasesSet = PickupAirbasesSet + self.MonitorTimeInterval = MonitorTimeInterval return self end @@ -532,30 +517,8 @@ function AI_CARGO_DISPATCHER:SetHomeZone( HomeZone ) return self end ---- Set the home airbase. This is for air units, i.e. helicopters and airplanes. --- When there is nothing anymore to pickup, the carriers will go back to their home base. They will await here new orders. --- @param #AI_CARGO_DISPATCHER self --- @param Wrapper.Airbase#AIRBASE HomeBase Airbase where the carriers will go after all pickup assignments are done. --- @return #AI_CARGO_DISPATCHER self -function AI_CARGO_DISPATCHER:SetHomeBase( HomeBase ) - - self.HomeBase = HomeBase - - return self -end ---- Set the home base. --- When there is nothing anymore to pickup, the carriers will return to their home airbase. There they will await new orders. --- @param #AI_CARGO_DISPATCHER self --- @param Wrapper.Airbase#AIRBASE HomeBase The airbase where the carrier will go to, once they completed all pending assignments. --- @return #AI_CARGO_DISPATCHER self -function AI_CARGO_DISPATCHER:SetHomeBase( HomeBase ) - - self.HomeBase = HomeBase - - return self -end --- Sets or randomizes the pickup location for the carrier around the cargo coordinate in a radius defined an outer and optional inner radius. @@ -1022,7 +985,7 @@ function AI_CARGO_DISPATCHER:onafterTransport( From, Event, To, Carrier, Cargo ) local DeployZone = self.DeployZoneSet:GetRandomZone() local DeployCoordinate = DeployZone:GetCoordinate():GetRandomCoordinateInRadius( self.DeployOuterRadius, self.DeployInnerRadius ) - self.AI_Cargo[Carrier]:Deploy( DeployCoordinate, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ), DeployZone ) + self.AI_Cargo[Carrier]:__Deploy( 0.1, DeployCoordinate, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ), DeployZone ) end end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua index 6193c1bc1..24912784d 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua @@ -67,6 +67,8 @@ function AI_CARGO_DISPATCHER_AIRPLANE:New( AirplaneSet, CargoSet, PickupZoneSet, self:SetPickupSpeed( 1200, 600 ) self:SetPickupRadius( 0, 0 ) self:SetDeployRadius( 0, 0 ) + + self:SetMonitorTimeInterval( 600 ) return self end diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 9c9e89e37..4c725c4cb 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1974,7 +1974,7 @@ do -- Route methods -- @param #CONTROLLABLE self -- @return #CONTROLLABLE function CONTROLLABLE:RouteStop() - self:F(self:GetName() .. "RouteStop") + self:F(self:GetName() .. " RouteStop") local CommandStop = self:CommandStopRoute( true ) self:SetCommand( CommandStop ) From 1345bdcb1092585a1eba78ee7745cdba6eaf8825 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Thu, 20 Sep 2018 20:08:00 +0200 Subject: [PATCH 381/420] Refinement of the code. - ALL the Pickup event code has changed. A Height parameter has been added. - ALL the Deploy event code has changed. A Height parameter has been added. - ALL the Home event code has changed. A Height parameter has been added. - ZONE_POLYGON now also has a create zone event triggered. --- Moose Development/Moose/AI/AI_Cargo.lua | 7 +- Moose Development/Moose/AI/AI_Cargo_APC.lua | 14 ++-- .../Moose/AI/AI_Cargo_Airplane.lua | 26 +++--- .../Moose/AI/AI_Cargo_Dispatcher.lua | 82 ++++++++++++++++--- .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 3 + .../Moose/AI/AI_Cargo_Dispatcher_Airplane.lua | 8 +- .../AI/AI_Cargo_Dispatcher_Helicopter.lua | 9 +- .../Moose/AI/AI_Cargo_Helicopter.lua | 22 +++-- Moose Development/Moose/Core/Zone.lua | 7 +- 9 files changed, 135 insertions(+), 43 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua index a0686b9c6..eb51e29e0 100644 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -174,8 +174,9 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate of the pickup point. -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. +-- @param #number Height Height in meters to move to the home coordinate. -- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. -function AI_CARGO:onafterPickup( APC, From, Event, To, Coordinate, Speed, PickupZone ) +function AI_CARGO:onafterPickup( APC, From, Event, To, Coordinate, Speed, Height, PickupZone ) self.Transporting = false self.Relocating = true @@ -191,7 +192,9 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate Deploy place. -- @param #number Speed Speed in km/h to drive to the depoly coordinate. Default is 50% of max possible speed the unit can go. -function AI_CARGO:onafterDeploy( APC, From, Event, To, Coordinate, Speed, DeployZone ) +-- @param #number Height Height in meters to move to the deploy coordinate. +-- @param Core.Zone#ZONE DeployZone The zone where the cargo will be deployed. +function AI_CARGO:onafterDeploy( APC, From, Event, To, Coordinate, Speed, Height, DeployZone ) self.Relocating = false self.Transporting = true diff --git a/Moose Development/Moose/AI/AI_Cargo_APC.lua b/Moose Development/Moose/AI/AI_Cargo_APC.lua index 76ebfc397..ace90787d 100644 --- a/Moose Development/Moose/AI/AI_Cargo_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_APC.lua @@ -378,8 +378,9 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate of the pickup point. -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. +-- @param #number Height Height in meters to move to the pickup coordinate. This parameter is ignored for APCs. -- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. -function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed, PickupZone ) +function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed, Height, PickupZone ) if APC and APC:IsAlive() then @@ -401,7 +402,7 @@ function AI_CARGO_APC:onafterPickup( APC, From, Event, To, Coordinate, Speed, Pi AI_CARGO_APC._Pickup( APC, self, Coordinate, Speed, PickupZone ) end - self:GetParent( self, AI_CARGO_APC ).onafterPickup( self, APC, From, Event, To, Coordinate, Speed, PickupZone ) + self:GetParent( self, AI_CARGO_APC ).onafterPickup( self, APC, From, Event, To, Coordinate, Speed, Height, PickupZone ) end end @@ -415,7 +416,9 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate Deploy place. -- @param #number Speed Speed in km/h to drive to the depoly coordinate. Default is 50% of max possible speed the unit can go. -function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed, DeployZone ) +-- @param #number Height Height in meters to move to the deploy coordinate. This parameter is ignored for APCs. +-- @param Core.Zone#ZONE DeployZone The zone where the cargo will be deployed. +function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed, Height, DeployZone ) if APC and APC:IsAlive() then @@ -433,7 +436,7 @@ function AI_CARGO_APC:onafterDeploy( APC, From, Event, To, Coordinate, Speed, De APC:Route( Waypoints, 1 ) -- Move after a random seconds to the Route. See the Route method for details. - self:GetParent( self, AI_CARGO_APC ).onafterDeploy( self, APC, From, Event, To, Coordinate, Speed, DeployZone ) + self:GetParent( self, AI_CARGO_APC ).onafterDeploy( self, APC, From, Event, To, Coordinate, Speed, Height, DeployZone ) end @@ -464,7 +467,8 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate Home place. -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. -function AI_CARGO_APC:onafterHome( APC, From, Event, To, Coordinate, Speed, HomeZone ) +-- @param #number Height Height in meters to move to the home coordinate. This parameter is ignored for APCs. +function AI_CARGO_APC:onafterHome( APC, From, Event, To, Coordinate, Speed, Height, HomeZone ) if APC and APC:IsAlive() ~= nil then diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 186679d24..82334c8ab 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -261,8 +261,9 @@ end -- @param #string To To state. -- @param Core.Point#COORDINATE Coordinate -- @param #number Speed in km/h for travelling to pickup base. --- @param Core.Zone#ZONE_AIRBASE PickupZone -function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate, Speed, PickupZone ) +-- @param #number Height Height in meters to move to the pickup coordinate. +-- @param Core.Zone#ZONE_AIRBASE (optional) PickupZone The zone where the cargo will be picked up. +function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate, Speed, Height, PickupZone ) if Airplane and Airplane:IsAlive() then --env.info("FF onafterpick aircraft alive") @@ -293,7 +294,7 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate, --env.info("FF onafterpickup routing to airbase "..ClosestAirbase:GetName()) -- Route aircraft to pickup airbase. - self:Route( Airplane, Airbase, Speed ) + self:Route( Airplane, Airbase, Speed, Height ) -- Set airbase as starting point in the next Route() call. self.Airbase = Airbase @@ -310,7 +311,7 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate, end - self:GetParent( self, AI_CARGO_AIRPLANE ).onafterPickup( self, Airplane, From, Event, To, Coordinate, Speed, PickupZone ) + self:GetParent( self, AI_CARGO_AIRPLANE ).onafterPickup( self, Airplane, From, Event, To, Coordinate, Speed, Height, PickupZone ) else --env.info("FF onafterpick aircraft not alive") end @@ -326,8 +327,9 @@ end -- @param #string To To state. -- @param Core.Point#COORDINATE Coordinate -- @param #number Speed in km/h for travelling to pickup base. --- @param Core.Zone#ZONE_AIRBASE DeployZone -function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Coordinate, Speed, DeployZone ) +-- @param #number Height Height in meters to move to the home coordinate. +-- @param Core.Zone#ZONE_AIRBASE DeployZone The zone where the cargo will be deployed. +function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Coordinate, Speed, Height, DeployZone ) if Airplane and Airplane:IsAlive()~=nil then @@ -339,7 +341,7 @@ function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Coordinate, end -- Route to destination airbase. - self:Route( Airplane, Airbase, Speed ) + self:Route( Airplane, Airbase, Speed, Height ) -- Aircraft is on a depoly mission. self.RouteDeploy = true @@ -347,7 +349,7 @@ function AI_CARGO_AIRPLANE:onafterDeploy( Airplane, From, Event, To, Coordinate, -- Set destination airbase for next :Route() command. self.Airbase = Airbase - self:GetParent( self, AI_CARGO_AIRPLANE ).onafterDeploy( self, Airplane, From, Event, To, Coordinate, Speed, DeployZone ) + self:GetParent( self, AI_CARGO_AIRPLANE ).onafterDeploy( self, Airplane, From, Event, To, Coordinate, Speed, Height, DeployZone ) end end @@ -393,8 +395,9 @@ end -- @param Wrapper.Group#GROUP Airplane Airplane group to be routed. -- @param Wrapper.Airbase#AIRBASE Airbase Destination airbase. -- @param #number Speed Speed in km/h. Default is 80% of max possible speed the group can do. +-- @param #number Height Height in meters to move to the Airbase. -- @param #boolean Uncontrolled If true, spawn group in uncontrolled state. -function AI_CARGO_AIRPLANE:Route( Airplane, Airbase, Speed, Uncontrolled ) +function AI_CARGO_AIRPLANE:Route( Airplane, Airbase, Speed, Height, Uncontrolled ) if Airplane and Airplane:IsAlive() then @@ -468,8 +471,9 @@ end -- @param To To State. -- @param Core.Point#COORDINATE Coordinate Home place (not used). -- @param #number Speed Speed in km/h to fly to the home airbase (zone). Default is 80% of max possible speed the unit can go. +-- @param #number Height Height in meters to move to the home coordinate. -- @param Core.Zone#ZONE_AIRBASE HomeZone The home airbase (zone) where the plane should return to. -function AI_CARGO_AIRPLANE:onafterHome(Airplane, From, Event, To, Coordinate, Speed, HomeZone ) +function AI_CARGO_AIRPLANE:onafterHome(Airplane, From, Event, To, Coordinate, Speed, Height, HomeZone ) if Airplane and Airplane:IsAlive() then -- We are going home! @@ -480,7 +484,7 @@ function AI_CARGO_AIRPLANE:onafterHome(Airplane, From, Event, To, Coordinate, Sp self.Airbase=HomeBase -- Now route the airplane home - self:Route(Airplane, HomeBase, Speed) + self:Route( Airplane, HomeBase, Speed, Height ) end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index edcf18846..3e012d491 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -109,8 +109,9 @@ -- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- -- @param Core.Point#COORDINATE Coordinate The coordinate of the pickup location. -- -- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the pickup Coordinate. +-- -- @param #number Height Height in meters to move to the pickup coordinate. -- -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. --- function CLASS:OnAfterPickup( From, Event, To, CarrierGroup, Coordinate, Speed, PickupZone ) +-- function CLASS:OnAfterPickup( From, Event, To, CarrierGroup, Coordinate, Speed, Height, PickupZone ) -- -- -- Write here your own code. -- @@ -232,8 +233,9 @@ -- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- -- @param Core.Point#COORDINATE Coordinate The deploy coordinate. -- -- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the deploy Coordinate. +-- -- @param #number Height Height in meters to move to the deploy coordinate. -- -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. --- function CLASS:OnAfterDeploy( From, Event, To, CarrierGroup, Coordinate, Speed, DeployZone ) +-- function CLASS:OnAfterDeploy( From, Event, To, CarrierGroup, Coordinate, Speed, Height, DeployZone ) -- -- -- Write here your own code. -- @@ -347,8 +349,9 @@ -- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- -- @param Core.Point#COORDINATE Coordinate The home coordinate the Carrier will arrive and stop it's activities. -- -- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the home Coordinate. +-- -- @param #number Height Height in meters to move to the home coordinate. -- -- @param Core.Zone#ZONE HomeZone The zone wherein the carrier will return when all cargo has been transported. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. --- function CLASS:OnAfterHome( From, Event, To, CarrierGroup, Coordinate, Speed, HomeZone ) +-- function CLASS:OnAfterHome( From, Event, To, CarrierGroup, Coordinate, Speed, Height, HomeZone ) -- -- -- Write here your own code. -- @@ -361,6 +364,7 @@ -- -- * @{#AI_CARGO_DISPATCHER.SetPickupRadius}(): Sets or randomizes the pickup location for the carrier around the cargo coordinate in a radius defined an outer and optional inner radius. -- * @{#AI_CARGO_DISPATCHER.SetPickupSpeed}(): Set the speed or randomizes the speed in km/h to pickup the cargo. +-- * @{#AI_CARGO_DISPATCHER.SetPickupHeight}(): Set the height or randomizes the height in meters to pickup the cargo. -- -- # 4) Set the deploy parameters. -- @@ -368,6 +372,7 @@ -- -- * @{#AI_CARGO_DISPATCHER.SetDeployRadius}(): Sets or randomizes the deploy location for the carrier around the cargo coordinate in a radius defined an outer and an optional inner radius. -- * @{#AI_CARGO_DISPATCHER.SetDeploySpeed}(): Set the speed or randomizes the speed in km/h to deploy the cargo. +-- * @{#AI_CARGO_DISPATCHER.SetDeployHeight}(): Set the height or randomizes the height in meters to deploy the cargo. -- -- # 5) Set the home zone when there isn't any more cargo to pickup. -- @@ -640,6 +645,55 @@ function AI_CARGO_DISPATCHER:SetDeploySpeed( MaxSpeed, MinSpeed ) end +--- Set the height or randomizes the height in meters to fly and pickup the cargo. The default height is 200 meters. +-- @param #AI_CARGO_DISPATCHER self +-- @param #number MaxHeight (optional) The maximum height to fly to the cargo pickup location. +-- @param #number MinHeight (optional) The minimum height to fly to the cargo pickup location. +-- @return #AI_CARGO_DISPATCHER +-- @usage +-- +-- -- Create a new cargo dispatcher +-- AICargoDispatcherHelicopter = AI_CARGO_DISPATCHER_HELICOPTER:New( SetCarrier, SetCargo, SetDeployZone ) +-- +-- -- Set the minimum pickup fly height to be 50 meters and the maximum height to be 200 meters. +-- AICargoDispatcherHelicopter:SetPickupHeight( 200, 50 ) +-- +function AI_CARGO_DISPATCHER:SetPickupHeight( MaxHeight, MinHeight ) + + MaxHeight = MaxHeight or 200 + MinHeight = MinHeight or MaxHeight + + self.PickupMinHeight = MinHeight + self.PickupMaxHeight = MaxHeight + + return self +end + + +--- Set the height or randomizes the height in meters to fly and deploy the cargo. The default height is 200 meters. +-- @param #AI_CARGO_DISPATCHER self +-- @param #number MaxHeight (optional) The maximum height to fly to the cargo deploy location. +-- @param #number MinHeight (optional) The minimum height to fly to the cargo deploy location. +-- @return #AI_CARGO_DISPATCHER +-- @usage +-- +-- -- Create a new cargo dispatcher +-- AICargoDispatcherHelicopter = AI_CARGO_DISPATCHER_HELICOPTER:New( SetCarrier, SetCargo, SetDeployZone ) +-- +-- -- Set the minimum deploy fly height to be 50 meters and the maximum height to be 200 meters. +-- AICargoDispatcherHelicopter:SetDeployHeight( 200, 50 ) +-- +function AI_CARGO_DISPATCHER:SetDeployHeight( MaxHeight, MinHeight ) + + MaxHeight = MaxHeight or 200 + MinHeight = MinHeight or MaxHeight + + self.DeployMinHeight = MinHeight + self.DeployMaxHeight = MaxHeight + + return self +end + --- The Start trigger event, which actually takes action at the specified time interval. -- @param #AI_CARGO_DISPATCHER self @@ -666,9 +720,10 @@ function AI_CARGO_DISPATCHER:onafterMonitor() -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- @param Core.Point#COORDINATE Coordinate The coordinate of the pickup location. -- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the pickup Coordinate. + -- @param #number Height Height in meters to move to the pickup coordinate. -- @param Core.Zone#ZONE_AIRBASE PickupZone (optional) The zone from where the cargo is picked up. Note that the zone is optional and may not be provided, but for AI_CARGO_DISPATCHER_AIRBASE there will always be a PickupZone, as the pickup location is an airbase zone. - function AI_Cargo.OnAfterPickup( AI_Cargo, CarrierGroup, From, Event, To, Coordinate, Speed, PickupZone ) - self:Pickup( CarrierGroup, Coordinate, Speed, PickupZone ) + function AI_Cargo.OnAfterPickup( AI_Cargo, CarrierGroup, From, Event, To, Coordinate, Speed, Height, PickupZone ) + self:Pickup( CarrierGroup, Coordinate, Speed, Height, PickupZone ) end --- Load event handler OnAfter for AI_CARGO_DISPATCHER. @@ -751,10 +806,11 @@ function AI_CARGO_DISPATCHER:onafterMonitor() -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- @param Core.Point#COORDINATE Coordinate The deploy coordinate. -- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the deploy Coordinate. + -- @param #number Height Height in meters to move to the deploy coordinate. -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. - function AI_Cargo.OnAfterDeploy( AI_Cargo, CarrierGroup, From, Event, To, Coordinate, Speed, DeployZone ) - self:Deploy( CarrierGroup, Coordinate, Speed, DeployZone ) + function AI_Cargo.OnAfterDeploy( AI_Cargo, CarrierGroup, From, Event, To, Coordinate, Speed, Height, DeployZone ) + self:Deploy( CarrierGroup, Coordinate, Speed, Height, DeployZone ) end @@ -838,10 +894,11 @@ function AI_CARGO_DISPATCHER:onafterMonitor() -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. -- @param Core.Point#COORDINATE Coordinate The home coordinate the Carrier will arrive and stop it's activities. -- @param #number Speed The velocity in meters per second on which the CarrierGroup is routed towards the home Coordinate. + -- @param #number Height Height in meters to move to the home coordinate. -- @param Core.Zone#ZONE HomeZone The zone wherein the carrier will return when all cargo has been transported. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. - function AI_Cargo.OnAfterHome( AI_Cargo, Carrier, From, Event, To, Coordinate, Speed, HomeZone ) - self:Home( Carrier, Coordinate, Speed, HomeZone ) + function AI_Cargo.OnAfterHome( AI_Cargo, Carrier, From, Event, To, Coordinate, Speed, Height, HomeZone ) + self:Home( Carrier, Coordinate, Speed, Height, HomeZone ) end end @@ -862,6 +919,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() if Cargo:IsUnLoaded() == true and Cargo:IsDeployed() == false then local CargoCoordinate = Cargo:GetCoordinate() local CoordinateFree = true + self.PickupZoneSet:Flush() PickupZone = self.PickupZoneSet and self.PickupZoneSet:IsCoordinateInZone( CargoCoordinate ) if not self.PickupZoneSet or PickupZone then for CarrierPickup, Coordinate in pairs( self.PickupCargo ) do @@ -901,13 +959,13 @@ function AI_CARGO_DISPATCHER:onafterMonitor() if PickupCargo then self.CarrierHome[Carrier] = nil local PickupCoordinate = PickupCargo:GetCoordinate():GetRandomCoordinateInRadius( self.PickupOuterRadius, self.PickupInnerRadius ) - AI_Cargo:Pickup( PickupCoordinate, math.random( self.PickupMinSpeed, self.PickupMaxSpeed ), PickupZone ) + AI_Cargo:Pickup( PickupCoordinate, math.random( self.PickupMinSpeed, self.PickupMaxSpeed ), math.random( self.PickupMinHeight, self.PickupMaxHeight ), PickupZone ) break else if self.HomeZone then if not self.CarrierHome[Carrier] then self.CarrierHome[Carrier] = true - AI_Cargo:Home( self.HomeZone:GetRandomPointVec2(), math.random( self.PickupMinSpeed, self.PickupMaxSpeed ), self.HomeZone ) + AI_Cargo:Home( self.HomeZone:GetRandomPointVec2(), math.random( self.PickupMinSpeed, self.PickupMaxSpeed ), math.random( self.PickupMinHeight, self.PickupMaxHeight ), self.HomeZone ) end end end @@ -985,7 +1043,7 @@ function AI_CARGO_DISPATCHER:onafterTransport( From, Event, To, Carrier, Cargo ) local DeployZone = self.DeployZoneSet:GetRandomZone() local DeployCoordinate = DeployZone:GetCoordinate():GetRandomCoordinateInRadius( self.DeployOuterRadius, self.DeployInnerRadius ) - self.AI_Cargo[Carrier]:__Deploy( 0.1, DeployCoordinate, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ), DeployZone ) + self.AI_Cargo[Carrier]:__Deploy( 0.1, DeployCoordinate, math.random( self.DeployMinSpeed, self.DeployMaxSpeed ), math.random( self.DeployMinHeight, self.DeployMaxHeight ), DeployZone ) end end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index da11cdcb2..4dbb87f41 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -120,6 +120,9 @@ function AI_CARGO_DISPATCHER_APC:New( APCSet, CargoSet, PickupZoneSet, DeployZon self:SetPickupRadius( 0, 0 ) self:SetDeployRadius( 0, 0 ) + self:SetPickupHeight() + self:SetDeployHeight() + self:SetCombatRadius( CombatRadius ) return self diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua index 24912784d..911728bf2 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua @@ -63,11 +63,15 @@ function AI_CARGO_DISPATCHER_AIRPLANE:New( AirplaneSet, CargoSet, PickupZoneSet, local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithZones( AirplaneSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) -- #AI_CARGO_DISPATCHER_AIRPLANE - self:SetDeploySpeed( 1200, 600 ) self:SetPickupSpeed( 1200, 600 ) + self:SetDeploySpeed( 1200, 600 ) + self:SetPickupRadius( 0, 0 ) self:SetDeployRadius( 0, 0 ) - + + self:SetPickupHeight( 8000, 6000 ) + self:SetDeployHeight( 8000, 6000 ) + self:SetMonitorTimeInterval( 600 ) return self diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index 69beb31f4..399a985dd 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -119,14 +119,19 @@ function AI_CARGO_DISPATCHER_HELICOPTER:New( HelicopterSet, CargoSet, PickupZone local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithZones( HelicopterSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) -- #AI_CARGO_DISPATCHER_HELICOPTER - self:SetDeploySpeed( 200, 150 ) - self:SetPickupSpeed( 200, 150 ) + self:SetPickupSpeed( 350, 150 ) + self:SetDeploySpeed( 350, 150 ) + self:SetPickupRadius( 0, 0 ) self:SetDeployRadius( 0, 0 ) + self:SetPickupHeight( 500, 200 ) + self:SetDeployHeight( 500, 200 ) + return self end + function AI_CARGO_DISPATCHER_HELICOPTER:AICargo( Helicopter, CargoSet ) return AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) diff --git a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua index 0a71e9250..4b838fde5 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Helicopter.lua @@ -177,6 +177,9 @@ function AI_CARGO_HELICOPTER:New( Helicopter, CargoSet ) end + + + --- Set the Carrier. -- @param #AI_CARGO_HELICOPTER self -- @param Wrapper.Group#GROUP Helicopter @@ -416,15 +419,16 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate Pickup place. -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. +-- @param #number Height Height in meters to move to the pickup coordinate. This parameter is ignored for APCs. -- @param Core.Zone#ZONE PickupZone (optional) The zone where the cargo will be picked up. The PickupZone can be nil, if there wasn't any PickupZoneSet provided. -function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordinate, Speed, PickupZone ) +function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordinate, Speed, Height, PickupZone ) if Helicopter and Helicopter:IsAlive() ~= nil then Helicopter:Activate() self.RoutePickup = true - Coordinate.y = math.random( 50, 500 ) + Coordinate.y = Height local _speed=Speed or Helicopter:GetSpeedMax()*0.5 @@ -470,7 +474,7 @@ function AI_CARGO_HELICOPTER:onafterPickup( Helicopter, From, Event, To, Coordin self.PickupZone = PickupZone - self:GetParent( self, AI_CARGO_HELICOPTER ).onafterPickup( self, Helicopter, From, Event, To, Coordinate, Speed, PickupZone ) + self:GetParent( self, AI_CARGO_HELICOPTER ).onafterPickup( self, Helicopter, From, Event, To, Coordinate, Speed, Height, PickupZone ) end @@ -492,7 +496,8 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate Place at which the cargo is deployed. -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. -function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordinate, Speed, DeployZone ) +-- @param #number Height Height in meters to move to the deploy coordinate. +function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordinate, Speed, Height, DeployZone ) if Helicopter and Helicopter:IsAlive() ~= nil then @@ -503,7 +508,7 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin --- Calculate the target route point. - Coordinate.y = math.random( 50, 500 ) + Coordinate.y = Height local _speed=Speed or Helicopter:GetSpeedMax()*0.5 @@ -548,7 +553,7 @@ function AI_CARGO_HELICOPTER:onafterDeploy( Helicopter, From, Event, To, Coordin -- Now route the helicopter Helicopter:Route( Route, 0 ) - self:GetParent( self, AI_CARGO_HELICOPTER ).onafterDeploy( self, Helicopter, From, Event, To, Coordinate, Speed, DeployZone ) + self:GetParent( self, AI_CARGO_HELICOPTER ).onafterDeploy( self, Helicopter, From, Event, To, Coordinate, Speed, Height, DeployZone ) end end @@ -562,8 +567,9 @@ end -- @param To -- @param Core.Point#COORDINATE Coordinate Home place. -- @param #number Speed Speed in km/h to drive to the pickup coordinate. Default is 50% of max possible speed the unit can go. +-- @param #number Height Height in meters to move to the home coordinate. -- @param Core.Zone#ZONE HomeZone The zone wherein the carrier will return when all cargo has been transported. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. -function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinate, Speed, HomeZone ) +function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinate, Speed, Height, HomeZone ) if Helicopter and Helicopter:IsAlive() ~= nil then @@ -573,7 +579,7 @@ function AI_CARGO_HELICOPTER:onafterHome( Helicopter, From, Event, To, Coordinat --- Calculate the target route point. - Coordinate.y = math.random( 50, 200 ) + Coordinate.y = Height Speed = Speed or Helicopter:GetSpeedMax()*0.5 diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index b434295f0..643477a86 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -1561,6 +1561,9 @@ function ZONE_POLYGON:New( ZoneName, ZoneGroup ) local self = BASE:Inherit( self, ZONE_POLYGON_BASE:New( ZoneName, GroupPoints ) ) self:F( { ZoneName, ZoneGroup, self._.Polygon } ) + -- Zone objects are added to the _DATABASE and SET_ZONE objects. + _EVENTDISPATCHER:CreateEventNewZone( self ) + return self end @@ -1568,7 +1571,6 @@ end --- Constructor to create a ZONE_POLYGON instance, taking the zone name and the **name** of the @{Wrapper.Group#GROUP} defined within the Mission Editor. -- The @{Wrapper.Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected by ZONE_POLYGON. -- @param #ZONE_POLYGON self --- @param #string ZoneName Name of the zone. -- @param #string GroupName The group name of the GROUP defining the waypoints within the Mission Editor to define the polygon shape. -- @return #ZONE_POLYGON self function ZONE_POLYGON:NewFromGroupName( GroupName ) @@ -1580,6 +1582,9 @@ function ZONE_POLYGON:NewFromGroupName( GroupName ) local self = BASE:Inherit( self, ZONE_POLYGON_BASE:New( GroupName, GroupPoints ) ) self:F( { GroupName, ZoneGroup, self._.Polygon } ) + -- Zone objects are added to the _DATABASE and SET_ZONE objects. + _EVENTDISPATCHER:CreateEventNewZone( self ) + return self end From 3625ad37b4eb4bdb6ffd9e631c21e66a3b7f9149 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Thu, 20 Sep 2018 21:43:05 +0200 Subject: [PATCH 382/420] Documentation --- .../Moose/AI/AI_Cargo_Dispatcher.lua | 49 ++++++----- .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 82 ++++++++++++++----- .../Moose/AI/AI_Cargo_Dispatcher_Airplane.lua | 74 ++++++++++++++++- .../AI/AI_Cargo_Dispatcher_Helicopter.lua | 72 ++++++++++++---- 4 files changed, 218 insertions(+), 59 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 3e012d491..acfd783b7 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -15,17 +15,27 @@ --- A dynamic cargo handling capability for AI groups. -- +-- AI_CARGO_DISPATCHER is the **base class** for: +-- +-- * @{AI.AI_Cargo_Dispatcher_APC#AI_CARGO_DISPATCHER_APC} +-- * @{AI.AI_Cargo_Dispatcher_Helicopter#AI_CARGO_DISPATCHER_HELICOPTER} +-- * @{AI.AI_Cargo_Dispatcher_Airplane#AI_CARGO_DISPATCHER_AIRPLANE} +-- +-- --- +-- -- Carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. -- The AI_CARGO_DISPATCHER module uses the @{Cargo.Cargo} capabilities within the MOOSE framework, to enable Carrier GROUP objects -- to transport @{Cargo.Cargo} towards several deploy zones. -- @{Cargo.Cargo} must be declared within the mission to make the AI_CARGO_DISPATCHER object recognize the cargo. -- Please consult the @{Cargo.Cargo} module for more information. -- --- # 1) AI_CARGO_DISPATCHER constructor +-- # 1) AI_CARGO_DISPATCHER constructor. -- -- * @{#AI_CARGO_DISPATCHER.New}(): Creates a new AI\_CARGO\_DISPATCHER object. -- --- # 2) AI_CARGO_DISPATCHER is a FSM +-- --- +-- +-- # 2) AI_CARGO_DISPATCHER is a Finite State Machine. -- -- This section must be read as follows. Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed. -- The first column is the **From** state, the second column the **Event**, and the third column the **To** state. @@ -40,28 +50,27 @@ -- -- These are the different possible state transitions of this state machine implementation: -- --- * Idle => Start => Monitoring --- * Monitoring => Monitor => Monitoring --- * Monitoring => Stop => Idle +-- * Idle => Start => Monitoring +-- * Monitoring => Monitor => Monitoring +-- * Monitoring => Stop => Idle -- --- * Monitoring => Pickup => Monitoring --- * Monitoring => Load => Monitoring --- * Monitoring => Loading => Monitoring --- * Monitoring => Loaded => Monitoring --- * Monitoring => PickedUp => Monitoring --- * Monitoring => Deploy => Monitoring --- * Monitoring => Unload => Monitoring --- * Monitoring => Unloaded => Monitoring --- * Monitoring => Deployed => Monitoring --- * Monitoring => Home => Monitoring --- --- --- ## 2.1) AI_CARGO_DISPATCHER States +-- * Monitoring => Pickup => Monitoring +-- * Monitoring => Load => Monitoring +-- * Monitoring => Loading => Monitoring +-- * Monitoring => Loaded => Monitoring +-- * Monitoring => PickedUp => Monitoring +-- * Monitoring => Deploy => Monitoring +-- * Monitoring => Unload => Monitoring +-- * Monitoring => Unloaded => Monitoring +-- * Monitoring => Deployed => Monitoring +-- * Monitoring => Home => Monitoring +-- +-- ## 2.1) AI_CARGO_DISPATCHER States. -- -- * **Monitoring**: The process is dispatching. -- * **Idle**: The process is idle. -- --- ## 2.2) AI_CARGO_DISPATCHER Events +-- ## 2.2) AI_CARGO_DISPATCHER Events. -- -- * **Start**: Start the transport process. -- * **Stop**: Stop the transport process. @@ -78,6 +87,8 @@ -- * **Deployed**: All cargo is unloaded from the carriers in the group. -- * **Home**: A Carrier is going home. -- +-- --- +-- -- # 3) Enhance your mission scripts with **Tailored** Event Handling! -- -- Use these methods to capture the events and tailor the events with your own code! diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index 4dbb87f41..0ee874d86 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -41,50 +41,92 @@ -- CARGO derived objects must be declared within the mission to make the AI_CARGO_DISPATCHER_HELICOPTER object recognize the cargo. -- -- --- ## 1. AI\_CARGO\_DISPATCHER\_APC constructor +-- # 1) AI_CARGO_DISPATCHER_APC constructor. -- --- * @{#AI_CARGO_DISPATCHER\_APC.New}(): Creates a new AI\_CARGO\_DISPATCHER\_APC object. +-- * @{#AI_CARGO_DISPATCHER_APC.New}(): Creates a new AI_CARGO_DISPATCHER_APC object. -- --- ## 2. AI\_CARGO\_DISPATCHER\_APC is a FSM +-- --- -- --- ![Process](..\Presentations\AI_CARGO_DISPATCHER_APC\Dia3.JPG) +-- # 2) AI_CARGO_DISPATCHER_APC is a Finite State Machine. -- --- ### 2.1. AI\_CARGO\_DISPATCHER\_APC States +-- This section must be read as follows. Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed. +-- The first column is the **From** state, the second column the **Event**, and the third column the **To** state. +-- +-- So, each of the rows have the following structure. +-- +-- * **From** => **Event** => **To** +-- +-- Important to know is that an event can only be executed if the **current state** is the **From** state. +-- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed, +-- and the resulting state will be the **To** state. +-- +-- These are the different possible state transitions of this state machine implementation: +-- +-- * Idle => Start => Monitoring +-- * Monitoring => Monitor => Monitoring +-- * Monitoring => Stop => Idle +-- +-- * Monitoring => Pickup => Monitoring +-- * Monitoring => Load => Monitoring +-- * Monitoring => Loading => Monitoring +-- * Monitoring => Loaded => Monitoring +-- * Monitoring => PickedUp => Monitoring +-- * Monitoring => Deploy => Monitoring +-- * Monitoring => Unload => Monitoring +-- * Monitoring => Unloaded => Monitoring +-- * Monitoring => Deployed => Monitoring +-- * Monitoring => Home => Monitoring +-- +-- +-- ## 2.1) AI_CARGO_DISPATCHER States. -- -- * **Monitoring**: The process is dispatching. -- * **Idle**: The process is idle. -- --- ### 2.2. AI\_CARGO\_DISPATCHER\_APC Events +-- ## 2.2) AI_CARGO_DISPATCHER Events. -- --- * **Monitor**: Monitor and take action. -- * **Start**: Start the transport process. -- * **Stop**: Stop the transport process. +-- * **Monitor**: Monitor and take action. +-- -- * **Pickup**: Pickup cargo. -- * **Load**: Load the cargo. +-- * **Loading**: The dispatcher is coordinating the loading of a cargo. -- * **Loaded**: Flag that the cargo is loaded. +-- * **PickedUp**: The dispatcher has loaded all requested cargo into the CarrierGroup. -- * **Deploy**: Deploy cargo to a location. -- * **Unload**: Unload the cargo. -- * **Unloaded**: Flag that the cargo is unloaded. --- * **Home**: A APC is going home. +-- * **Deployed**: All cargo is unloaded from the carriers in the group. +-- * **Home**: A Carrier is going home. -- --- ## 3. Set the pickup parameters. +-- ## 2.3) Enhance your mission scripts with **Tailored** Event Handling! +-- +-- Within your mission, you can capture these events when triggered, and tailor the events with your own code! +-- Check out the @{AI.AI_Cargo_Dispatcher_APC#AI_CARGO_DISPATCHER} class at chapter 3 for details on the different event handlers that are available and how to use them. +-- +-- **There are a lot of templates available that allows you to quickly setup an event handler for a specific event type!** +-- +-- --- +-- +-- # 3) Set the pickup parameters. -- -- Several parameters can be set to pickup cargo: -- --- * @{#AI_CARGO_DISPATCHER\_APC.SetPickupRadius}(): Sets or randomizes the pickup location for the APC around the cargo coordinate in a radius defined an outer and optional inner radius. --- * @{#AI_CARGO_DISPATCHER\_APC.SetPickupSpeed}(): Set the speed or randomizes the speed in km/h to pickup the cargo. +-- * @{#AI_CARGO_DISPATCHER_APC.SetPickupRadius}(): Sets or randomizes the pickup location for the APC around the cargo coordinate in a radius defined an outer and optional inner radius. +-- * @{#AI_CARGO_DISPATCHER_APC.SetPickupSpeed}(): Set the speed or randomizes the speed in km/h to pickup the cargo. -- --- ## 4. Set the deploy parameters. +-- # 4) Set the deploy parameters. -- -- Several parameters can be set to deploy cargo: -- --- * @{#AI_CARGO_DISPATCHER\_APC.SetDeployRadius}(): Sets or randomizes the deploy location for the APC around the cargo coordinate in a radius defined an outer and an optional inner radius. --- * @{#AI_CARGO_DISPATCHER\_APC.SetDeploySpeed}(): Set the speed or randomizes the speed in km/h to deploy the cargo. +-- * @{#AI_CARGO_DISPATCHER_APC.SetDeployRadius}(): Sets or randomizes the deploy location for the APC around the cargo coordinate in a radius defined an outer and an optional inner radius. +-- * @{#AI_CARGO_DISPATCHER_APC.SetDeploySpeed}(): Set the speed or randomizes the speed in km/h to deploy the cargo. -- --- ## 5. Set the home zone when there isn't any more cargo to pickup. +-- # 5) Set the home zone when there isn't any more cargo to pickup. -- -- A home zone can be specified to where the APCs will move when there isn't any cargo left for pickup. --- Use @{#AI_CARGO_DISPATCHER\_APC.SetHomeZone}() to specify the home zone. +-- Use @{#AI_CARGO_DISPATCHER_APC.SetHomeZone}() to specify the home zone. -- -- If no home zone is specified, the APCs will wait near the deploy zone for a new pickup command. -- @@ -97,10 +139,10 @@ AI_CARGO_DISPATCHER_APC = { --- Creates a new AI_CARGO_DISPATCHER_APC object. -- @param #AI_CARGO_DISPATCHER_APC self --- @param Core.Set#SET_GROUP APCSet The collection of APC @{Wrapper.Group}s. --- @param Core.Set#SET_CARGO CargoSet The collection of @{Cargo.Cargo} derived objects. --- @param Core.Set#SET_ZONE PickupZoneSet (optional) The collection of pickup @{Zone}s, which are used to where the cargo can be picked up by the APCs. If nil, then cargo can be picked up everywhere. --- @param Core.Set#SET_ZONE DeployZoneSet The collection of deploy @{Zone}s, which are used to where the cargo will be deployed by the APCs. +-- @param Core.Set#SET_GROUP APCSet The set of @{Wrapper.Group#GROUP} objects of vehicles, trucks, APCs that will transport the cargo. +-- @param Core.Set#SET_CARGO CargoSet The set of @{Cargo.Cargo#CARGO} objects, which can be CARGO_GROUP, CARGO_CRATE, CARGO_SLINGLOAD objects. +-- @param Core.Set#SET_ZONE PickupZoneSet (optional) The set of pickup zones, which are used to where the cargo can be picked up by the APCs. If nil, then cargo can be picked up everywhere. +-- @param Core.Set#SET_ZONE DeployZoneSet The set of deploy zones, which are used to where the cargo will be deployed by the APCs. -- @param DCS#Distance CombatRadius The cargo will be unloaded from the APC and engage the enemy if the enemy is within CombatRadius range. The radius is in meters, the default value is 500 meters. -- @return #AI_CARGO_DISPATCHER_APC -- @usage diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua index 911728bf2..c1cade906 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua @@ -34,6 +34,72 @@ -- Also ensure that you fully understand how to declare and setup Cargo objects within the MOOSE framework before using this class. -- CARGO derived objects must be declared within the mission to make the AI_CARGO_DISPATCHER_HELICOPTER object recognize the cargo. -- +-- # 1) AI_CARGO_DISPATCHER_AIRPLANE constructor. +-- +-- * @{#AI_CARGO_DISPATCHER_AIRPLANE.New}(): Creates a new AI_CARGO_DISPATCHER_AIRPLANE object. +-- +-- --- +-- +-- # 2) AI_CARGO_DISPATCHER_AIRPLANE is a Finite State Machine. +-- +-- This section must be read as follows. Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed. +-- The first column is the **From** state, the second column the **Event**, and the third column the **To** state. +-- +-- So, each of the rows have the following structure. +-- +-- * **From** => **Event** => **To** +-- +-- Important to know is that an event can only be executed if the **current state** is the **From** state. +-- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed, +-- and the resulting state will be the **To** state. +-- +-- These are the different possible state transitions of this state machine implementation: +-- +-- * Idle => Start => Monitoring +-- * Monitoring => Monitor => Monitoring +-- * Monitoring => Stop => Idle +-- +-- * Monitoring => Pickup => Monitoring +-- * Monitoring => Load => Monitoring +-- * Monitoring => Loading => Monitoring +-- * Monitoring => Loaded => Monitoring +-- * Monitoring => PickedUp => Monitoring +-- * Monitoring => Deploy => Monitoring +-- * Monitoring => Unload => Monitoring +-- * Monitoring => Unloaded => Monitoring +-- * Monitoring => Deployed => Monitoring +-- * Monitoring => Home => Monitoring +-- +-- +-- ## 2.1) AI_CARGO_DISPATCHER States. +-- +-- * **Monitoring**: The process is dispatching. +-- * **Idle**: The process is idle. +-- +-- ## 2.2) AI_CARGO_DISPATCHER Events. +-- +-- * **Start**: Start the transport process. +-- * **Stop**: Stop the transport process. +-- * **Monitor**: Monitor and take action. +-- +-- * **Pickup**: Pickup cargo. +-- * **Load**: Load the cargo. +-- * **Loading**: The dispatcher is coordinating the loading of a cargo. +-- * **Loaded**: Flag that the cargo is loaded. +-- * **PickedUp**: The dispatcher has loaded all requested cargo into the CarrierGroup. +-- * **Deploy**: Deploy cargo to a location. +-- * **Unload**: Unload the cargo. +-- * **Unloaded**: Flag that the cargo is unloaded. +-- * **Deployed**: All cargo is unloaded from the carriers in the group. +-- * **Home**: A Carrier is going home. +-- +-- ## 2.3) Enhance your mission scripts with **Tailored** Event Handling! +-- +-- Within your mission, you can capture these events when triggered, and tailor the events with your own code! +-- Check out the @{AI.AI_Cargo_Dispatcher_AIRPLANE#AI_CARGO_DISPATCHER} class at chapter 3 for details on the different event handlers that are available and how to use them. +-- +-- **There are a lot of templates available that allows you to quickly setup an event handler for a specific event type!** +-- -- -- -- @field #AI_CARGO_DISPATCHER_AIRPLANE @@ -43,10 +109,10 @@ AI_CARGO_DISPATCHER_AIRPLANE = { --- Creates a new AI_CARGO_DISPATCHER_AIRPLANE object. -- @param #AI_CARGO_DISPATCHER_AIRPLANE self --- @param Core.Set#SET_GROUP AirplaneSet Set of cargo transport airplanes. --- @param Core.Set#SET_CARGO CargoSet Set of cargo, which is supposed to be transported. --- @param Core.Zone#SET_ZONE PickupZoneSet Set of zone airbases where the cargo has to be picked up. --- @param Core.Zone#SET_ZONE DeployZoneSet Set of zone airbases where the cargo is deployed. Choice for each cargo is random. +-- @param Core.Set#SET_GROUP AirplaneSet The set of @{Wrapper.Group#GROUP} objects of airplanes that will transport the cargo. +-- @param Core.Set#SET_CARGO CargoSet The set of @{Cargo.Cargo#CARGO} objects, which can be CARGO_GROUP, CARGO_CRATE, CARGO_SLINGLOAD objects. +-- @param Core.Zone#SET_ZONE PickupZoneSet The set of zone airbases where the cargo has to be picked up. +-- @param Core.Zone#SET_ZONE DeployZoneSet The set of zone airbases where the cargo is deployed. Choice for each cargo is random. -- @return #AI_CARGO_DISPATCHER_AIRPLANE self -- @usage -- diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index 399a985dd..09054c74d 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -38,33 +38,71 @@ -- -- --- -- --- ## 1. AI\_CARGO\_DISPATCHER\_HELICOPTER constructor +-- # 1. AI\_CARGO\_DISPATCHER\_HELICOPTER constructor. -- -- * @{#AI_CARGO_DISPATCHER\_HELICOPTER.New}(): Creates a new AI\_CARGO\_DISPATCHER\_HELICOPTER object. -- -- --- -- --- ## 2. AI\_CARGO\_DISPATCHER\_HELICOPTER is a FSM +-- # 2. AI\_CARGO\_DISPATCHER\_HELICOPTER is a Finite State Machine. -- --- ![Process](..\Presentations\AI_CARGO_DISPATCHER_HELICOPTER\Dia3.JPG) +-- This section must be read as follows. Each of the rows indicate a state transition, triggered through an event, and with an ending state of the event was executed. +-- The first column is the **From** state, the second column the **Event**, and the third column the **To** state. -- --- ### 2.1. AI\_CARGO\_DISPATCHER\_HELICOPTER States +-- So, each of the rows have the following structure. +-- +-- * **From** => **Event** => **To** +-- +-- Important to know is that an event can only be executed if the **current state** is the **From** state. +-- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed, +-- and the resulting state will be the **To** state. +-- +-- These are the different possible state transitions of this state machine implementation: +-- +-- * Idle => Start => Monitoring +-- * Monitoring => Monitor => Monitoring +-- * Monitoring => Stop => Idle +-- +-- * Monitoring => Pickup => Monitoring +-- * Monitoring => Load => Monitoring +-- * Monitoring => Loading => Monitoring +-- * Monitoring => Loaded => Monitoring +-- * Monitoring => PickedUp => Monitoring +-- * Monitoring => Deploy => Monitoring +-- * Monitoring => Unload => Monitoring +-- * Monitoring => Unloaded => Monitoring +-- * Monitoring => Deployed => Monitoring +-- * Monitoring => Home => Monitoring +-- +-- +-- ## 2.1) AI_CARGO_DISPATCHER States. -- -- * **Monitoring**: The process is dispatching. -- * **Idle**: The process is idle. -- --- ### 2.2. AI\_CARGO\_DISPATCHER\_HELICOPTER Events +-- ## 2.2) AI_CARGO_DISPATCHER Events. -- --- * **Monitor**: Monitor and take action. -- * **Start**: Start the transport process. -- * **Stop**: Stop the transport process. +-- * **Monitor**: Monitor and take action. +-- -- * **Pickup**: Pickup cargo. -- * **Load**: Load the cargo. +-- * **Loading**: The dispatcher is coordinating the loading of a cargo. -- * **Loaded**: Flag that the cargo is loaded. +-- * **PickedUp**: The dispatcher has loaded all requested cargo into the CarrierGroup. -- * **Deploy**: Deploy cargo to a location. -- * **Unload**: Unload the cargo. -- * **Unloaded**: Flag that the cargo is unloaded. --- * **Home**: A Helicopter is going home. +-- * **Deployed**: All cargo is unloaded from the carriers in the group. +-- * **Home**: A Carrier is going home. +-- +-- ## 2.3) Enhance your mission scripts with **Tailored** Event Handling! +-- +-- Within your mission, you can capture these events when triggered, and tailor the events with your own code! +-- Check out the @{AI.AI_Cargo_Dispatcher_APC#AI_CARGO_DISPATCHER} class at chapter 3 for details on the different event handlers that are available and how to use them. +-- +-- **There are a lot of templates available that allows you to quickly setup an event handler for a specific event type!** -- -- --- -- @@ -72,8 +110,9 @@ -- -- Several parameters can be set to pickup cargo: -- --- * @{#AI_CARGO_DISPATCHER\_HELICOPTER.SetPickupRadius}(): Sets or randomizes the pickup location for the helicopter around the cargo coordinate in a radius defined an outer and optional inner radius. --- * @{#AI_CARGO_DISPATCHER\_HELICOPTER.SetPickupSpeed}(): Set the speed or randomizes the speed in km/h to pickup the cargo. +-- * @{#AI_CARGO_DISPATCHER_HELICOPTER.SetPickupRadius}(): Sets or randomizes the pickup location for the helicopter around the cargo coordinate in a radius defined an outer and optional inner radius. +-- * @{#AI_CARGO_DISPATCHER_HELICOPTER.SetPickupSpeed}(): Set the speed or randomizes the speed in km/h to pickup the cargo. +-- * @{#AI_CARGO_DISPATCHER_HELICOPTER.SetPickupHeight}(): Set the height or randomizes the height in meters to pickup the cargo. -- -- --- -- @@ -81,15 +120,16 @@ -- -- Several parameters can be set to deploy cargo: -- --- * @{#AI_CARGO_DISPATCHER\_HELICOPTER.SetDeployRadius}(): Sets or randomizes the deploy location for the helicopter around the cargo coordinate in a radius defined an outer and an optional inner radius. --- * @{#AI_CARGO_DISPATCHER\_HELICOPTER.SetDeploySpeed}(): Set the speed or randomizes the speed in km/h to deploy the cargo. +-- * @{#AI_CARGO_DISPATCHER_HELICOPTER.SetDeployRadius}(): Sets or randomizes the deploy location for the helicopter around the cargo coordinate in a radius defined an outer and an optional inner radius. +-- * @{#AI_CARGO_DISPATCHER_HELICOPTER.SetDeploySpeed}(): Set the speed or randomizes the speed in km/h to deploy the cargo. +-- * @{#AI_CARGO_DISPATCHER_HELICOPTER.SetDeployHeight}(): Set the height or randomizes the height in meters to deploy the cargo. -- -- --- -- -- ## 5. Set the home zone when there isn't any more cargo to pickup. -- -- A home zone can be specified to where the Helicopters will move when there isn't any cargo left for pickup. --- Use @{#AI_CARGO_DISPATCHER\_HELICOPTER.SetHomeZone}() to specify the home zone. +-- Use @{#AI_CARGO_DISPATCHER_HELICOPTER.SetHomeZone}() to specify the home zone. -- -- If no home zone is specified, the helicopters will wait near the deploy zone for a new pickup command. -- @@ -102,10 +142,10 @@ AI_CARGO_DISPATCHER_HELICOPTER = { --- Creates a new AI_CARGO_DISPATCHER_HELICOPTER object. -- @param #AI_CARGO_DISPATCHER_HELICOPTER self --- @param Core.Set#SET_GROUP HelicopterSet The collection of Helicopter @{Wrapper.Group}s. --- @param Core.Set#SET_CARGO CargoSet The collection of @{Cargo.Cargo} derived objects. --- @param Core.Set#SET_ZONE PickupZoneSet (optional) The collection of pickup @{Zone}s, which are used to where the cargo can be picked up by the APCs. If nil, then cargo can be picked up everywhere. --- @param Core.Set#SET_ZONE DeployZoneSet The collection of deploy @{Zone}s, which are used to where the cargo will be deployed by the Helicopters. +-- @param Core.Set#SET_GROUP HelicopterSet The set of @{Wrapper.Group#GROUP} objects of helicopters that will transport the cargo. +-- @param Core.Set#SET_CARGO CargoSet The set of @{Cargo.Cargo#CARGO} objects, which can be CARGO_GROUP, CARGO_CRATE, CARGO_SLINGLOAD objects. +-- @param Core.Set#SET_ZONE PickupZoneSet (optional) The set of pickup zones, which are used to where the cargo can be picked up by the APCs. If nil, then cargo can be picked up everywhere. +-- @param Core.Set#SET_ZONE DeployZoneSet The set of deploy zones, which are used to where the cargo will be deployed by the Helicopters. -- @return #AI_CARGO_DISPATCHER_HELICOPTER -- @usage -- From 99b153297495eddad470e8981bc25e8a3cc3d02c Mon Sep 17 00:00:00 2001 From: FlightControl Date: Thu, 20 Sep 2018 22:02:27 +0200 Subject: [PATCH 383/420] docs --- .../Moose/AI/AI_Cargo_Dispatcher.lua | 15 +++++++++------ .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 2 +- .../Moose/AI/AI_Cargo_Dispatcher_Airplane.lua | 2 +- .../Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua | 2 +- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index acfd783b7..545040494 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -1,5 +1,14 @@ --- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo. -- +-- AI_CARGO_DISPATCHER is the **base class** for: +-- +-- * @{AI.AI_Cargo_Dispatcher_APC#AI_CARGO_DISPATCHER_APC} +-- * @{AI.AI_Cargo_Dispatcher_Helicopter#AI_CARGO_DISPATCHER_HELICOPTER} +-- * @{AI.AI_Cargo_Dispatcher_Airplane#AI_CARGO_DISPATCHER_AIRPLANE} +-- +-- These classes use the functionality of AI_CARGO_DISPATCHER. +-- Also, the methods contained in AI_CARGO_DISPATCHER are available for use in the three classes above. +-- -- === -- -- ### Author: **FlightControl** @@ -15,12 +24,6 @@ --- A dynamic cargo handling capability for AI groups. -- --- AI_CARGO_DISPATCHER is the **base class** for: --- --- * @{AI.AI_Cargo_Dispatcher_APC#AI_CARGO_DISPATCHER_APC} --- * @{AI.AI_Cargo_Dispatcher_Helicopter#AI_CARGO_DISPATCHER_HELICOPTER} --- * @{AI.AI_Cargo_Dispatcher_Airplane#AI_CARGO_DISPATCHER_AIRPLANE} --- -- --- -- -- Carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index 0ee874d86..3232cb13d 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -103,7 +103,7 @@ -- ## 2.3) Enhance your mission scripts with **Tailored** Event Handling! -- -- Within your mission, you can capture these events when triggered, and tailor the events with your own code! --- Check out the @{AI.AI_Cargo_Dispatcher_APC#AI_CARGO_DISPATCHER} class at chapter 3 for details on the different event handlers that are available and how to use them. +-- Check out the @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} class at chapter 3 for details on the different event handlers that are available and how to use them. -- -- **There are a lot of templates available that allows you to quickly setup an event handler for a specific event type!** -- diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua index c1cade906..d1f14441b 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua @@ -96,7 +96,7 @@ -- ## 2.3) Enhance your mission scripts with **Tailored** Event Handling! -- -- Within your mission, you can capture these events when triggered, and tailor the events with your own code! --- Check out the @{AI.AI_Cargo_Dispatcher_AIRPLANE#AI_CARGO_DISPATCHER} class at chapter 3 for details on the different event handlers that are available and how to use them. +-- Check out the @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} class at chapter 3 for details on the different event handlers that are available and how to use them. -- -- **There are a lot of templates available that allows you to quickly setup an event handler for a specific event type!** -- diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index 09054c74d..902a9bffa 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -100,7 +100,7 @@ -- ## 2.3) Enhance your mission scripts with **Tailored** Event Handling! -- -- Within your mission, you can capture these events when triggered, and tailor the events with your own code! --- Check out the @{AI.AI_Cargo_Dispatcher_APC#AI_CARGO_DISPATCHER} class at chapter 3 for details on the different event handlers that are available and how to use them. +-- Check out the @{AI.AI_Cargo_Dispatcher#AI_CARGO_DISPATCHER} class at chapter 3 for details on the different event handlers that are available and how to use them. -- -- **There are a lot of templates available that allows you to quickly setup an event handler for a specific event type!** -- From fc5cbb7862276be29385cab007830fad990c0a1e Mon Sep 17 00:00:00 2001 From: FlightControl Date: Fri, 21 Sep 2018 13:21:04 +0200 Subject: [PATCH 384/420] Documentation --- .../Moose/AI/AI_Cargo_Dispatcher.lua | 237 ++++++++++++++---- 1 file changed, 189 insertions(+), 48 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 545040494..a73050d4b 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -1,14 +1,95 @@ --- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo. -- --- AI_CARGO_DISPATCHER is the **base class** for: +-- **Features:** -- --- * @{AI.AI_Cargo_Dispatcher_APC#AI_CARGO_DISPATCHER_APC} --- * @{AI.AI_Cargo_Dispatcher_Helicopter#AI_CARGO_DISPATCHER_HELICOPTER} --- * @{AI.AI_Cargo_Dispatcher_Airplane#AI_CARGO_DISPATCHER_AIRPLANE} +-- * AI_CARGO_DISPATCHER is the **base class** for: +-- +-- * @{AI.AI_Cargo_Dispatcher_APC#AI_CARGO_DISPATCHER_APC} +-- * @{AI.AI_Cargo_Dispatcher_Helicopter#AI_CARGO_DISPATCHER_HELICOPTER} +-- * @{AI.AI_Cargo_Dispatcher_Airplane#AI_CARGO_DISPATCHER_AIRPLANE} +-- +-- * Provides the facilities to transport cargo over the battle field for the above classes. +-- * Dispatches transport tasks to a common set of cargo transporting groups. +-- * Different options can be setup to tweak the cargo transporation behaviour. +-- +-- ## The dispatcher concept +-- +-- Carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. +-- The AI_CARGO_DISPATCHER module uses the @{Cargo.Cargo} capabilities within the MOOSE framework, to enable Carrier GROUP objects +-- to transport @{Cargo.Cargo} towards several deploy zones. +-- @{Cargo.Cargo} must be declared within the mission to make the AI_CARGO_DISPATCHER object recognize the cargo. +-- Please consult the @{Cargo.Cargo} module for more information. +-- +-- # Why cargo dispatching? +-- +-- It provides a realistic way of distributing your army forces around the battlefield, and to provide a quick means of cargo transportation. +-- Instead of having troops or cargo to "appear" suddenly at certain locations, the dispatchers will pickup the cargo and transport it. +-- It also allows to enforce or retreat your army from certain zones when needed, using helicopters or APCs. +-- Airplanes can transport cargo over larger distances between the airfields. +-- +-- ## What is a cargo object then? +-- +-- In order to make use of the MOOSE cargo system, you need to **declare** the DCS objects as MOOSE cargo objects! +-- This sounds complicated, but it is actually quite simple. +-- +-- See here an example: +-- +-- local EngineerCargoGroup = CARGO_GROUP:New( GROUP:FindByName( "Engineers" ), "Workmaterials", "Engineers", 250 ) +-- +-- The above code declares a MOOSE cargo object called `EngineerCargoGroup`. +-- It actually just refers to an infantry group created within the sim called `"Engineers"`. +-- The infantry group now becomes controlled by the MOOSE cargo object `EngineerCargoGroup`. +-- A MOOSE cargo object also has properties, like the type of cargo, the logical name, and the reporting range. +-- +-- For more information, please consult the @{Cargo.Cargo} module documentation. Please read through it, because it will explain how to setup the cargo objects for use +-- within your dispatchers. +-- +-- ## Do I need to do a lot of coding to setup a dispatcher? +-- +-- No! It requires a bit of studying to set it up, but once you understand the different components that use the cargo dispatcher, it becomes very easy. +-- Also, the dispatchers work in a true dynamic environment. The carriers and cargo, pickup and deploy zones can be created dynamically in your mission, +-- and will automatically be recognized by the dispatcher. +-- +-- ## Is the dispatcher causing a lot of CPU overhead? +-- +-- A little yes, but once the cargo is properly loaded into the carrier, the CPU consumption is very little. +-- When infantry or vehicles board into a carrier, or unboard from a carrier, you may perceive certain performance lags. +-- We are working to minimize the impact of those. +-- That being said, the DCS simulator is limited. It is just impossible to deploy hundreds of cargo over the battlefield, hundreds of helicopters transporting, +-- without any performance impact. The amount of helicopters that are active and flying in your simulation influences more the performance than the dispatchers. +-- It really comes down to trying it out and getting experienced with what is possible and what is not (or too much). +-- +-- ## Are the dispatchers a "black box" in terms of the logic? +-- +-- No. You can tailor the dispatcher mechanisms using event handlers, and create additional logic to enhance the behaviour and dynamism in your own mission. +-- The events are listed below, and so are the options, but here are a couple of examples of what is possible: +-- +-- * You could handle the **Deployed** event, when all the cargo is unloaded from a carrier in the dispatcher. +-- Adding your own code to the event handler, you could move the deployed cargo (infantry) to specific points to engage in the battlefield. +-- +-- * When a carrier is picking up cargo, the *Pickup** event is triggered, and you can inform the coalition of this event, +-- because it is an indication that troops are planned to join. +-- +-- ## Are there options that you can set to modify the behaviour of the carries? +-- +-- Yes, there are options to configure: +-- +-- * the location where carriers will park or land near the cargo for pickup. +-- * the location where carriers will park or land in the deploy zone for cargo deployment. +-- * the height for airborne carriers when they fly to and from pickup and deploy zones. +-- * the speed of the carriers. This is an important parameter, because depending on the tactication situation, speed will influence the detection by radars. +-- +-- ## Can the zones be of any zone type? +-- +-- Yes, please ensure that the zones are declared using the @{Core.Zone} classes. +-- Possible zones that function at the moment are ZONE, ZONE_GROUP, ZONE_UNIT, ZONE_POLYGON. +-- +-- === +-- +-- ### Test Missions +-- +-- [AI Cargo Dispatcher test missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/AID%20-%20AI%20Dispatching/AID-CGO%20-%20AI%20Cargo%20Dispatching) -- --- These classes use the functionality of AI_CARGO_DISPATCHER. --- Also, the methods contained in AI_CARGO_DISPATCHER are available for use in the three classes above. --- -- === -- -- ### Author: **FlightControl** @@ -18,6 +99,7 @@ -- @module AI.AI_Cargo_Dispatcher -- @image AI_Cargo_Dispatcher.JPG + --- @type AI_CARGO_DISPATCHER -- @extends Core.Fsm#FSM @@ -34,7 +116,44 @@ -- -- # 1) AI_CARGO_DISPATCHER constructor. -- --- * @{#AI_CARGO_DISPATCHER.New}(): Creates a new AI\_CARGO\_DISPATCHER object. +-- * @{#AI_CARGO_DISPATCHER.New}(): Creates a new AI_CARGO_DISPATCHER object. +-- +-- This are some example of AI cargo dispatcher objects created. +-- +-- * An AI dispatcher object for a helicopter squadron, moving infantry from pickup zones to deploy zones. +-- +-- local SetCargoInfantry = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- local SetHelicopter = SET_GROUP:New():FilterPrefixes( "Helicopter" ):FilterStart() +-- local SetPickupZones = SET_ZONE:New():FilterPrefixes( "Pickup" ):FilterStart() +-- local SetDeployZones = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() +-- +-- AICargoDispatcherHelicopter = AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargoInfantry, SetPickupZones, SetDeployZones ) +-- AICargoDispatcherHelicopter:SetHomeZone( ZONE:FindByName( "Home" ) ) +-- +-- * An AI dispatcher object for a vehicle squadron, moving infantry from pickup zones to deploy zones. +-- +-- local SetCargoInfantry = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- local SetAPC = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() +-- local SetDeployZones = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() +-- +-- AICargoDispatcherAPC = AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargoInfantry, nil, SetDeployZones ) +-- AICargoDispatcherAPC:Start() +-- +-- * An AI dispatcher object for an airplane squadron, moving infantry and vehicles from pickup airbases to deploy airbases. +-- +-- local CargoInfantrySet = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- local AirplanesSet = SET_GROUP:New():FilterPrefixes( "Airplane" ):FilterStart() +-- local PickupZoneSet = SET_ZONE:New() +-- local DeployZoneSet = SET_ZONE:New() +-- +-- PickupZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Gudauta ) ) +-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Sochi_Adler ) ) +-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Maykop_Khanskaya ) ) +-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Mineralnye_Vody ) ) +-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Vaziani ) ) +-- +-- AICargoDispatcherAirplanes = AI_CARGO_DISPATCHER_AIRPLANE:New( AirplanesSet, CargoInfantrySet, PickupZoneSet, DeployZoneSet ) +-- AICargoDispatcherAirplanes:SetHomeZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Kobuleti ) ) -- -- --- -- @@ -45,7 +164,7 @@ -- -- So, each of the rows have the following structure. -- --- * **From** => **Event** => **To** +-- * **From** => **Event** => **To** -- -- Important to know is that an event can only be executed if the **current state** is the **From** state. -- This, when an **Event** that is being triggered has a **From** state that is equal to the **Current** state of the state machine, the event will be executed, @@ -107,6 +226,27 @@ -- -- You can send messages or fire off any other events within the code section. The sky is the limit! -- +-- Mission AID-CGO-140, AID-CGO-240 and AID-CGO-340 contain examples how these events can be tailored. +-- +-- For those who don't have the time to check the test missions, find the underlying example of a Deployed event that is tailored. +-- +-- --- Deployed Handler OnAfter for AI_CARGO_DISPATCHER. +-- -- Use this event handler to tailor the event when a carrier has deployed all cargo objects from the CarrierGroup. +-- -- You can use this event handler to post messages to players, or provide status updates etc. +-- -- @function OnAfterDeployed +-- -- @param #AICargoDispatcherHelicopter self +-- -- @param #string From A string that contains the "*from state name*" when the event was fired. +-- -- @param #string Event A string that contains the "*event name*" when the event was fired. +-- -- @param #string To A string that contains the "*to state name*" when the event was fired. +-- -- @param Wrapper.Group#GROUP CarrierGroup The group object that contains the CarrierUnits. +-- -- @param Core.Zone#ZONE DeployZone The zone wherein the cargo is deployed. This can be any zone type, like a ZONE, ZONE_GROUP, ZONE_AIRBASE. +-- function AICargoDispatcherHelicopter:OnAfterDeployed( From, Event, To, CarrierGroup, DeployZone ) +-- +-- MESSAGE:NewType( "Group " .. CarrierGroup:GetName() .. " deployed all cargo in zone " .. DeployZone:GetName(), MESSAGE.Type.Information ):ToAll() +-- +-- end +-- +-- -- ## 3.1) Tailor the **Pickup** event -- -- Use this event handler to tailor the event when a CarrierGroup is routed towards a new pickup Coordinate and a specified Speed. @@ -420,11 +560,44 @@ AI_CARGO_DISPATCHER.PickupCargo = {} -- @return #AI_CARGO_DISPATCHER -- @usage -- --- -- Create a new cargo dispatcher --- SetCarriers = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() --- SetCargos = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() --- SetDeployZone = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() --- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo ) +-- -- An AI dispatcher object for a helicopter squadron, moving infantry from pickup zones to deploy zones. +-- +-- local SetCargoInfantry = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- local SetHelicopter = SET_GROUP:New():FilterPrefixes( "Helicopter" ):FilterStart() +-- local SetPickupZones = SET_ZONE:New():FilterPrefixes( "Pickup" ):FilterStart() +-- local SetDeployZones = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() +-- +-- AICargoDispatcherHelicopter = AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargoInfantry, SetPickupZones, SetDeployZones ) +-- AICargoDispatcherHelicopter:SetHomeZone( ZONE:FindByName( "Home" ) ) +-- +-- @usage +-- +-- -- An AI dispatcher object for a vehicle squadron, moving infantry from pickup zones to deploy zones. +-- +-- local SetCargoInfantry = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- local SetAPC = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() +-- local SetDeployZones = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() +-- +-- AICargoDispatcherAPC = AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargoInfantry, nil, SetDeployZones ) +-- AICargoDispatcherAPC:Start() +-- +-- @usage +-- +-- -- An AI dispatcher object for an airplane squadron, moving infantry and vehicles from pickup airbases to deploy airbases. +-- +-- local CargoInfantrySet = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- local AirplanesSet = SET_GROUP:New():FilterPrefixes( "Airplane" ):FilterStart() +-- local PickupZoneSet = SET_ZONE:New() +-- local DeployZoneSet = SET_ZONE:New() +-- +-- PickupZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Gudauta ) ) +-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Sochi_Adler ) ) +-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Maykop_Khanskaya ) ) +-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Mineralnye_Vody ) ) +-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Vaziani ) ) +-- +-- AICargoDispatcherAirplanes = AI_CARGO_DISPATCHER_AIRPLANE:New( AirplanesSet, CargoInfantrySet, PickupZoneSet, DeployZoneSet ) +-- AICargoDispatcherAirplanes:SetHomeZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Kobuleti ) ) -- function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo ) @@ -459,8 +632,7 @@ function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo ) self:SetMonitorTimeInterval( 30 ) - self.DeployRadiusInner = 200 - self.DeployRadiusOuter = 500 + self:SetDeployRadius( 500, 200 ) self.PickupCargo = {} self.CarrierHome = {} @@ -501,6 +673,7 @@ function AI_CARGO_DISPATCHER:NewWithZones( SetCarrier, SetCargo, PickupZoneSet, return self end + --- Set the monitor time interval. -- @param #AI_CARGO_DISPATCHER self -- @param #number MonitorTimeInterval @@ -513,7 +686,6 @@ function AI_CARGO_DISPATCHER:SetMonitorTimeInterval( MonitorTimeInterval ) end - --- Set the home zone. -- When there is nothing anymore to pickup, the carriers will go to a random coordinate in this zone. -- They will await here new orders. @@ -537,9 +709,6 @@ function AI_CARGO_DISPATCHER:SetHomeZone( HomeZone ) end - - - --- Sets or randomizes the pickup location for the carrier around the cargo coordinate in a radius defined an outer and optional inner radius. -- This radius is influencing the location where the carrier will land to pickup the cargo. -- There are two aspects that are very important to remember and take into account: @@ -989,20 +1158,6 @@ function AI_CARGO_DISPATCHER:onafterMonitor() self:__Monitor( self.MonitorTimeInterval ) end ---- Start event handler OnBefore for AI_CARGO_DISPATCHER --- @function [parent=#AI_CARGO_DISPATCHER] OnBeforeStart --- @param #AI_CARGO_DISPATCHER self --- @param #string From --- @param #string Event --- @param #string To --- @return #boolean - ---- Start event handler OnAfter for AI_CARGO_DISPATCHER --- @function [parent=#AI_CARGO_DISPATCHER] OnAfterStart --- @param #AI_CARGO_DISPATCHER self --- @param #string From --- @param #string Event --- @param #string To --- Start Trigger for AI_CARGO_DISPATCHER -- @function [parent=#AI_CARGO_DISPATCHER] Start @@ -1017,20 +1172,6 @@ function AI_CARGO_DISPATCHER:onafterStart( From, Event, To ) self:__Monitor( -1 ) end ---- Stop event handler OnBefore for AI_CARGO_DISPATCHER --- @function [parent=#AI_CARGO_DISPATCHER] OnBeforeStop --- @param #AI_CARGO_DISPATCHER self --- @param #string From --- @param #string Event --- @param #string To --- @return #boolean - ---- Stop event handler OnAfter for AI_CARGO_DISPATCHER --- @function [parent=#AI_CARGO_DISPATCHER] OnAfterStop --- @param #AI_CARGO_DISPATCHER self --- @param #string From --- @param #string Event --- @param #string To --- Stop Trigger for AI_CARGO_DISPATCHER -- @function [parent=#AI_CARGO_DISPATCHER] Stop From 8516cb349c820ca4bbc8408865f3d9c1f673633b Mon Sep 17 00:00:00 2001 From: FlightControl Date: Fri, 21 Sep 2018 15:24:40 +0200 Subject: [PATCH 385/420] Test --- Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index a73050d4b..cc0c5572c 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -2,6 +2,7 @@ -- -- **Features:** -- +-- -- * AI_CARGO_DISPATCHER is the **base class** for: -- -- * @{AI.AI_Cargo_Dispatcher_APC#AI_CARGO_DISPATCHER_APC} From c8f27866113acdcb909137c3321ac3c1716cb416 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Fri, 21 Sep 2018 16:29:53 +0200 Subject: [PATCH 386/420] Unboarding and Unboarding timing updates. --- Moose Development/Moose/AI/AI_Cargo.lua | 10 +++++----- Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua | 3 ++- Moose Development/Moose/Cargo/CargoUnit.lua | 11 ++++++++--- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua index eb51e29e0..6251f4e38 100644 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -213,7 +213,7 @@ function AI_CARGO:onbeforeLoad( Carrier, From, Event, To, PickupZone ) local Boarding = false - local LoadInterval = 2 + local LoadInterval = 5 local LoadDelay = 0 local Carrier_List = {} local Carrier_Weight = {} @@ -261,8 +261,8 @@ function AI_CARGO:onbeforeLoad( Carrier, From, Event, To, PickupZone ) if Carrier_Weight[CarrierUnit] > CargoWeight then --and CargoBayFreeVolume > CargoVolume then Carrier:RouteStop() --Cargo:Ungroup() - Cargo:__Board( LoadDelay, CarrierUnit, 25 ) - LoadDelay = LoadDelay + LoadInterval + Cargo:__Board( -LoadDelay, CarrierUnit, 25 ) + LoadDelay = LoadDelay + Cargo:GetCount() * LoadInterval self:__Board( LoadDelay, Cargo, CarrierUnit, PickupZone ) -- So now this CarrierUnit has Cargo that is being loaded. @@ -308,7 +308,7 @@ function AI_CARGO:onafterBoard( Carrier, From, Event, To, Cargo, CarrierUnit, Pi if Carrier and Carrier:IsAlive() then self:F({ IsLoaded = Cargo:IsLoaded(), Cargo:GetName(), Carrier:GetName() } ) if not Cargo:IsLoaded() then - self:__Board( 10, Cargo, CarrierUnit, PickupZone ) + self:__Board( -10, Cargo, CarrierUnit, PickupZone ) return end end @@ -397,7 +397,7 @@ function AI_CARGO:onafterUnload( Carrier, From, Event, To, DeployZone ) self:F( { Cargo = Cargo:GetName(), Isloaded = Cargo:IsLoaded() } ) if Cargo:IsLoaded() then Cargo:__UnBoard( UnboardDelay ) - UnboardDelay = UnboardDelay + UnboardInterval + UnboardDelay = UnboardDelay + Cargo:GetCount() * UnboardInterval Cargo:SetDeployed( true ) self:__Unboard( UnboardDelay, Cargo, CarrierUnit, DeployZone ) end diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index cc0c5572c..780f7c39b 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -1097,13 +1097,14 @@ function AI_CARGO_DISPATCHER:onafterMonitor() local PickupCargo = nil local PickupZone = nil + --self.SetCargo:Flush() for CargoName, Cargo in UTILS.spairs( self.SetCargo:GetSet(), function( t, a, b ) return t[a]:GetWeight() < t[b]:GetWeight() end ) do local Cargo = Cargo -- Cargo.Cargo#CARGO self:F( { Cargo = Cargo:GetName(), UnLoaded = Cargo:IsUnLoaded(), Deployed = Cargo:IsDeployed(), PickupCargo = self.PickupCargo[Carrier] ~= nil } ) if Cargo:IsUnLoaded() == true and Cargo:IsDeployed() == false then local CargoCoordinate = Cargo:GetCoordinate() local CoordinateFree = true - self.PickupZoneSet:Flush() + --self.PickupZoneSet:Flush() PickupZone = self.PickupZoneSet and self.PickupZoneSet:IsCoordinateInZone( CargoCoordinate ) if not self.PickupZoneSet or PickupZone then for CarrierPickup, Coordinate in pairs( self.PickupCargo ) do diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index 4429fc279..f499fb7a8 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -271,7 +271,7 @@ do -- CARGO_UNIT local TaskRoute = self.CargoObject:TaskRoute( Points ) self.CargoObject:SetTask( TaskRoute, 2 ) - self:__Boarding( -1, CargoCarrier, NearRadius ) + self:__Boarding( -5, CargoCarrier, NearRadius ) self.RunCount = 0 end end @@ -297,8 +297,13 @@ do -- CARGO_UNIT if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then self:__Load( 1, CargoCarrier, ... ) else - self:__Boarding( -1, CargoCarrier, NearRadius, ... ) - self.RunCount = self.RunCount + 1 + if self:IsNear( CargoCarrier:GetPointVec2(), 20 ) then + self:__Boarding( -2, CargoCarrier, NearRadius, ... ) + self.RunCount = self.RunCount + 2 + else + self:__Boarding( -10, CargoCarrier, NearRadius, ... ) + self.RunCount = self.RunCount + 10 + end if self.RunCount >= 40 then self.RunCount = 0 local Speed = 90 From e274ac3151388f168db8ccfdf67fe3cfabd58e98 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Fri, 21 Sep 2018 21:18:40 +0200 Subject: [PATCH 387/420] Documentation --- .../Moose/AI/AI_Cargo_Dispatcher.lua | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 780f7c39b..a87f18988 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -123,38 +123,38 @@ -- -- * An AI dispatcher object for a helicopter squadron, moving infantry from pickup zones to deploy zones. -- --- local SetCargoInfantry = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() --- local SetHelicopter = SET_GROUP:New():FilterPrefixes( "Helicopter" ):FilterStart() --- local SetPickupZones = SET_ZONE:New():FilterPrefixes( "Pickup" ):FilterStart() --- local SetDeployZones = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() +-- local SetCargoInfantry = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- local SetHelicopter = SET_GROUP:New():FilterPrefixes( "Helicopter" ):FilterStart() +-- local SetPickupZones = SET_ZONE:New():FilterPrefixes( "Pickup" ):FilterStart() +-- local SetDeployZones = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() -- --- AICargoDispatcherHelicopter = AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargoInfantry, SetPickupZones, SetDeployZones ) --- AICargoDispatcherHelicopter:SetHomeZone( ZONE:FindByName( "Home" ) ) +-- AICargoDispatcherHelicopter = AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargoInfantry, SetPickupZones, SetDeployZones ) +-- AICargoDispatcherHelicopter:SetHomeZone( ZONE:FindByName( "Home" ) ) -- -- * An AI dispatcher object for a vehicle squadron, moving infantry from pickup zones to deploy zones. -- --- local SetCargoInfantry = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() --- local SetAPC = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() --- local SetDeployZones = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() +-- local SetCargoInfantry = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- local SetAPC = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() +-- local SetDeployZones = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() -- --- AICargoDispatcherAPC = AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargoInfantry, nil, SetDeployZones ) --- AICargoDispatcherAPC:Start() +-- AICargoDispatcherAPC = AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargoInfantry, nil, SetDeployZones ) +-- AICargoDispatcherAPC:Start() -- -- * An AI dispatcher object for an airplane squadron, moving infantry and vehicles from pickup airbases to deploy airbases. -- --- local CargoInfantrySet = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() --- local AirplanesSet = SET_GROUP:New():FilterPrefixes( "Airplane" ):FilterStart() --- local PickupZoneSet = SET_ZONE:New() --- local DeployZoneSet = SET_ZONE:New() +-- local CargoInfantrySet = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- local AirplanesSet = SET_GROUP:New():FilterPrefixes( "Airplane" ):FilterStart() +-- local PickupZoneSet = SET_ZONE:New() +-- local DeployZoneSet = SET_ZONE:New() -- --- PickupZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Gudauta ) ) --- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Sochi_Adler ) ) --- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Maykop_Khanskaya ) ) --- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Mineralnye_Vody ) ) --- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Vaziani ) ) +-- PickupZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Gudauta ) ) +-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Sochi_Adler ) ) +-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Maykop_Khanskaya ) ) +-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Mineralnye_Vody ) ) +-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Vaziani ) ) -- --- AICargoDispatcherAirplanes = AI_CARGO_DISPATCHER_AIRPLANE:New( AirplanesSet, CargoInfantrySet, PickupZoneSet, DeployZoneSet ) --- AICargoDispatcherAirplanes:SetHomeZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Kobuleti ) ) +-- AICargoDispatcherAirplanes = AI_CARGO_DISPATCHER_AIRPLANE:New( AirplanesSet, CargoInfantrySet, PickupZoneSet, DeployZoneSet ) +-- AICargoDispatcherAirplanes:SetHomeZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Kobuleti ) ) -- -- --- -- From 136a2ce5f2e8a05e2793b0d5663efc22716ad348 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sat, 22 Sep 2018 09:28:08 +0200 Subject: [PATCH 388/420] Documentation test --- Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index a87f18988..8d25c5775 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -2,7 +2,6 @@ -- -- **Features:** -- --- -- * AI_CARGO_DISPATCHER is the **base class** for: -- -- * @{AI.AI_Cargo_Dispatcher_APC#AI_CARGO_DISPATCHER_APC} @@ -119,9 +118,9 @@ -- -- * @{#AI_CARGO_DISPATCHER.New}(): Creates a new AI_CARGO_DISPATCHER object. -- --- This are some example of AI cargo dispatcher objects created. +-- Find below some examples of AI cargo dispatcher objects created. -- --- * An AI dispatcher object for a helicopter squadron, moving infantry from pickup zones to deploy zones. +-- ### An AI dispatcher object for a helicopter squadron, moving infantry from pickup zones to deploy zones. -- -- local SetCargoInfantry = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() -- local SetHelicopter = SET_GROUP:New():FilterPrefixes( "Helicopter" ):FilterStart() @@ -131,7 +130,7 @@ -- AICargoDispatcherHelicopter = AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargoInfantry, SetPickupZones, SetDeployZones ) -- AICargoDispatcherHelicopter:SetHomeZone( ZONE:FindByName( "Home" ) ) -- --- * An AI dispatcher object for a vehicle squadron, moving infantry from pickup zones to deploy zones. +-- ### An AI dispatcher object for a vehicle squadron, moving infantry from pickup zones to deploy zones. -- -- local SetCargoInfantry = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() -- local SetAPC = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() @@ -140,7 +139,7 @@ -- AICargoDispatcherAPC = AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargoInfantry, nil, SetDeployZones ) -- AICargoDispatcherAPC:Start() -- --- * An AI dispatcher object for an airplane squadron, moving infantry and vehicles from pickup airbases to deploy airbases. +-- ### An AI dispatcher object for an airplane squadron, moving infantry and vehicles from pickup airbases to deploy airbases. -- -- local CargoInfantrySet = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() -- local AirplanesSet = SET_GROUP:New():FilterPrefixes( "Airplane" ):FilterStart() From 6b2e9fcb5ffb0c47fbb93d55a534d1ab37b86fbc Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sat, 22 Sep 2018 09:34:36 +0200 Subject: [PATCH 389/420] Documentation --- Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua | 3 +-- Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua | 3 +-- Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index 3232cb13d..726239fa9 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -31,8 +31,7 @@ -- -- The AI_CARGO_DISPATCHER_APC module is derived from the AI_CARGO_DISPATCHER module. -- --- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_APC class, it is recommended that you --- **first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!!** +-- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_APC class, it is recommended that you first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!! -- -- Especially to learn how to **Tailor the different cargo handling events**, this will be very useful! -- diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua index d1f14441b..65a1371b9 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua @@ -25,8 +25,7 @@ -- -- The AI_CARGO_DISPATCHER_AIRPLANE module is derived from the AI_CARGO_DISPATCHER module. -- --- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_AIRPLANE class, it is recommended that you --- **first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!!** +-- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_AIRPLANE class, it is recommended that you first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!!** -- -- Especially to learn how to **Tailor the different cargo handling events**, this will be very useful! -- diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index 902a9bffa..dc92a74a3 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -27,8 +27,7 @@ -- -- The AI_CARGO_DISPATCHER_HELICOPTER module is derived from the AI_CARGO_DISPATCHER module. -- --- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_HELICOPTER class, it is recommended that you --- **first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!!** +-- ## Note! In order to fully understand the mechanisms of the AI_CARGO_DISPATCHER_HELICOPTER class, it is recommended that you first consult and READ the documentation of the @{AI.AI_Cargo_Dispatcher} module!!!** -- -- Especially to learn how to **Tailor the different cargo handling events**, this will be very useful! -- From 6e67da16720a84b6ce73bb263eb5262b86425708 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sat, 22 Sep 2018 09:45:57 +0200 Subject: [PATCH 390/420] Dispatcher documentation. --- .../Moose/AI/AI_Cargo_Dispatcher.lua | 54 +++++-------------- .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 15 +++--- .../Moose/AI/AI_Cargo_Dispatcher_Airplane.lua | 25 +++++---- .../AI/AI_Cargo_Dispatcher_Helicopter.lua | 16 +++--- 4 files changed, 49 insertions(+), 61 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 8d25c5775..6adddce8d 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -568,7 +568,7 @@ AI_CARGO_DISPATCHER.PickupCargo = {} -- local SetDeployZones = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() -- -- AICargoDispatcherHelicopter = AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargoInfantry, SetPickupZones, SetDeployZones ) --- AICargoDispatcherHelicopter:SetHomeZone( ZONE:FindByName( "Home" ) ) +-- AICargoDispatcherHelicopter:Start() -- -- @usage -- @@ -597,7 +597,7 @@ AI_CARGO_DISPATCHER.PickupCargo = {} -- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Vaziani ) ) -- -- AICargoDispatcherAirplanes = AI_CARGO_DISPATCHER_AIRPLANE:New( AirplanesSet, CargoInfantrySet, PickupZoneSet, DeployZoneSet ) --- AICargoDispatcherAirplanes:SetHomeZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Kobuleti ) ) +-- AICargoDispatcherAirplanes:Start() -- function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo ) @@ -648,32 +648,6 @@ function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo ) end ---- Creates a new AI_CARGO_DISPATCHER object. --- @param #AI_CARGO_DISPATCHER self --- @param Core.Set#SET_GROUP SetCarrier --- @param Core.Set#SET_CARGO SetCargo --- @param Core.Set#SET_ZONE PickupZoneSet --- @param Core.Set#SET_ZONE DeployZoneSet --- @return #AI_CARGO_DISPATCHER --- @usage --- --- -- Create a new cargo dispatcher --- SetCarriers = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() --- SetCargos = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() --- DeployZoneSet = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() --- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZone ) --- -function AI_CARGO_DISPATCHER:NewWithZones( SetCarrier, SetCargo, PickupZoneSet, DeployZoneSet ) - - local self = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo ) -- #AI_CARGO_DISPATCHER - - self.PickupZoneSet = PickupZoneSet - self.DeployZoneSet = DeployZoneSet - - return self -end - - --- Set the monitor time interval. -- @param #AI_CARGO_DISPATCHER self -- @param #number MonitorTimeInterval @@ -695,11 +669,11 @@ end -- @usage -- -- -- Create a new cargo dispatcher --- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZone ) +-- AICargoDispatcherHelicopter = AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargoInfantry, SetPickupZones, SetDeployZones ) -- -- -- Set the home coordinate -- local HomeZone = ZONE:New( "Home" ) --- AICargoDispatcher:SetHomeZone( HomeZone ) +-- AICargoDispatcherHelicopter:SetHomeZone( HomeZone ) -- function AI_CARGO_DISPATCHER:SetHomeZone( HomeZone ) @@ -728,10 +702,10 @@ end -- @usage -- -- -- Create a new cargo dispatcher --- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZone ) +-- AICargoDispatcherHelicopter = AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargoInfantry, SetPickupZones, SetDeployZones ) -- -- -- Set the carrier to land within a band around the cargo coordinate between 500 and 300 meters! --- AICargoDispatcher:SetPickupRadius( 500, 300 ) +-- AICargoDispatcherHelicopter:SetPickupRadius( 500, 300 ) -- function AI_CARGO_DISPATCHER:SetPickupRadius( OuterRadius, InnerRadius ) @@ -753,10 +727,10 @@ end -- @usage -- -- -- Create a new cargo dispatcher --- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZone ) +-- AICargoDispatcherHelicopter = AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargoInfantry, SetPickupZones, SetDeployZones ) -- -- -- Set the minimum pickup speed to be 100 km/h and the maximum speed to be 200 km/h. --- AICargoDispatcher:SetPickupSpeed( 200, 100 ) +-- AICargoDispatcherHelicopter:SetPickupSpeed( 200, 100 ) -- function AI_CARGO_DISPATCHER:SetPickupSpeed( MaxSpeed, MinSpeed ) @@ -786,10 +760,10 @@ end -- @usage -- -- -- Create a new cargo dispatcher --- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZone ) +-- AICargoDispatcherHelicopter = AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargoInfantry, SetPickupZones, SetDeployZones ) -- -- -- Set the carrier to land within a band around the cargo coordinate between 500 and 300 meters! --- AICargoDispatcher:SetDeployRadius( 500, 300 ) +-- AICargoDispatcherHelicopter:SetDeployRadius( 500, 300 ) -- function AI_CARGO_DISPATCHER:SetDeployRadius( OuterRadius, InnerRadius ) @@ -811,10 +785,10 @@ end -- @usage -- -- -- Create a new cargo dispatcher --- AICargoDispatcher = AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, SetDeployZone ) +-- AICargoDispatcherHelicopter = AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargoInfantry, SetPickupZones, SetDeployZones ) -- -- -- Set the minimum deploy speed to be 100 km/h and the maximum speed to be 200 km/h. --- AICargoDispatcher:SetDeploySpeed( 200, 100 ) +-- AICargoDispatcherHelicopter:SetDeploySpeed( 200, 100 ) -- function AI_CARGO_DISPATCHER:SetDeploySpeed( MaxSpeed, MinSpeed ) @@ -836,7 +810,7 @@ end -- @usage -- -- -- Create a new cargo dispatcher --- AICargoDispatcherHelicopter = AI_CARGO_DISPATCHER_HELICOPTER:New( SetCarrier, SetCargo, SetDeployZone ) +-- AICargoDispatcherHelicopter = AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargoInfantry, SetPickupZones, SetDeployZones ) -- -- -- Set the minimum pickup fly height to be 50 meters and the maximum height to be 200 meters. -- AICargoDispatcherHelicopter:SetPickupHeight( 200, 50 ) @@ -861,7 +835,7 @@ end -- @usage -- -- -- Create a new cargo dispatcher --- AICargoDispatcherHelicopter = AI_CARGO_DISPATCHER_HELICOPTER:New( SetCarrier, SetCargo, SetDeployZone ) +-- AICargoDispatcherHelicopter = AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargoInfantry, SetPickupZones, SetDeployZones ) -- -- -- Set the minimum deploy fly height to be 50 meters and the maximum height to be 200 meters. -- AICargoDispatcherHelicopter:SetDeployHeight( 200, 50 ) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index 726239fa9..a4e9854ce 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -146,15 +146,18 @@ AI_CARGO_DISPATCHER_APC = { -- @return #AI_CARGO_DISPATCHER_APC -- @usage -- --- -- Create a new cargo dispatcher for the set of APCs, with a combatradius of 500. --- APCSet = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() --- CargoSet = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() --- DeployZoneSet = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() --- AICargoDispatcher = AI_CARGO_DISPATCHER_APC:New( APCSet, CargoSet, nil, DeployZoneSet, 500 ) +-- -- An AI dispatcher object for a vehicle squadron, moving infantry from pickup zones to deploy zones. +-- +-- local SetCargoInfantry = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- local SetAPC = SET_GROUP:New():FilterPrefixes( "APC" ):FilterStart() +-- local SetDeployZones = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() +-- +-- AICargoDispatcherAPC = AI_CARGO_DISPATCHER_APC:New( SetAPC, SetCargoInfantry, nil, SetDeployZones ) +-- AICargoDispatcherAPC:Start() -- function AI_CARGO_DISPATCHER_APC:New( APCSet, CargoSet, PickupZoneSet, DeployZoneSet, CombatRadius ) - local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithZones( APCSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) -- #AI_CARGO_DISPATCHER_APC + local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( APCSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) -- #AI_CARGO_DISPATCHER_APC self:SetDeploySpeed( 120, 70 ) self:SetPickupSpeed( 120, 70 ) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua index 65a1371b9..bfcd74b15 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua @@ -115,18 +115,25 @@ AI_CARGO_DISPATCHER_AIRPLANE = { -- @return #AI_CARGO_DISPATCHER_AIRPLANE self -- @usage -- --- -- Create a new cargo dispatcher --- AirplaneSet = SET_GROUP:New():FilterPrefixes( "Airplane" ):FilterStart() --- CargoSet = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() --- PickupZoneSet = SET_AIRBASE:New() --- DeployZoneSet = SET_AIRBASE:New() --- PickupZoneSet:AddZone( ZONE_AIRBASE:New( "Gudauta", AIRBASE:FindByName( AIRBASE.Caucasus.Gudauta ), 3000 ) ) --- DeployZoneSet:AddZone( ZONE_AIRBASE:New( "Sochi", AIRBASE:FindByName( AIRBASE.Caucasus.Sochi_Adler ), 3000 ) ) --- AICargoDispatcher = AI_CARGO_DISPATCHER_AIRPLANE:New( AirplaneSet, CargoSet, PickupZoneSet, DeployZoneSet ) +-- -- An AI dispatcher object for an airplane squadron, moving infantry and vehicles from pickup airbases to deploy airbases. +-- +-- local CargoInfantrySet = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- local AirplanesSet = SET_GROUP:New():FilterPrefixes( "Airplane" ):FilterStart() +-- local PickupZoneSet = SET_ZONE:New() +-- local DeployZoneSet = SET_ZONE:New() +-- +-- PickupZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Gudauta ) ) +-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Sochi_Adler ) ) +-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Maykop_Khanskaya ) ) +-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Mineralnye_Vody ) ) +-- DeployZoneSet:AddZone( ZONE_AIRBASE:New( AIRBASE.Caucasus.Vaziani ) ) +-- +-- AICargoDispatcherAirplanes = AI_CARGO_DISPATCHER_AIRPLANE:New( AirplanesSet, CargoInfantrySet, PickupZoneSet, DeployZoneSet ) +-- AICargoDispatcherAirplanes:Start() -- function AI_CARGO_DISPATCHER_AIRPLANE:New( AirplaneSet, CargoSet, PickupZoneSet, DeployZoneSet ) - local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithZones( AirplaneSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) -- #AI_CARGO_DISPATCHER_AIRPLANE + local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( AirplaneSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) -- #AI_CARGO_DISPATCHER_AIRPLANE self:SetPickupSpeed( 1200, 600 ) self:SetDeploySpeed( 1200, 600 ) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index dc92a74a3..1f546d37f 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -148,15 +148,19 @@ AI_CARGO_DISPATCHER_HELICOPTER = { -- @return #AI_CARGO_DISPATCHER_HELICOPTER -- @usage -- --- -- Create a new cargo dispatcher --- HelicopterSet = SET_GROUP:New():FilterPrefixes( "Helicopter" ):FilterStart() --- CargoSet = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() --- DeployZoneSet = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() --- AICargoDispatcher = AI_CARGO_DISPATCHER_HELICOPTER:New( HelicopterSet, SetCargo, nil, DeployZoneSet ) +-- -- An AI dispatcher object for a helicopter squadron, moving infantry from pickup zones to deploy zones. +-- +-- local SetCargoInfantry = SET_CARGO:New():FilterTypes( "Infantry" ):FilterStart() +-- local SetHelicopter = SET_GROUP:New():FilterPrefixes( "Helicopter" ):FilterStart() +-- local SetPickupZones = SET_ZONE:New():FilterPrefixes( "Pickup" ):FilterStart() +-- local SetDeployZones = SET_ZONE:New():FilterPrefixes( "Deploy" ):FilterStart() +-- +-- AICargoDispatcherHelicopter = AI_CARGO_DISPATCHER_HELICOPTER:New( SetHelicopter, SetCargoInfantry, SetPickupZones, SetDeployZones ) +-- AICargoDispatcherHelicopter:Start() -- function AI_CARGO_DISPATCHER_HELICOPTER:New( HelicopterSet, CargoSet, PickupZoneSet, DeployZoneSet ) - local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:NewWithZones( HelicopterSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) -- #AI_CARGO_DISPATCHER_HELICOPTER + local self = BASE:Inherit( self, AI_CARGO_DISPATCHER:New( HelicopterSet, CargoSet, PickupZoneSet, DeployZoneSet ) ) -- #AI_CARGO_DISPATCHER_HELICOPTER self:SetPickupSpeed( 350, 150 ) self:SetDeploySpeed( 350, 150 ) From d86419f89306d156e257278403db2ae13a22d6fa Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sat, 22 Sep 2018 09:55:40 +0200 Subject: [PATCH 391/420] Documentation --- .../Moose/AI/AI_Cargo_Dispatcher.lua | 28 +++++++++++++------ .../Moose/AI/AI_Cargo_Dispatcher_APC.lua | 11 +++++++- .../Moose/AI/AI_Cargo_Dispatcher_Airplane.lua | 11 +++++++- .../AI/AI_Cargo_Dispatcher_Helicopter.lua | 11 +++++++- 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 6adddce8d..02f852bc0 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -1,6 +1,6 @@ --- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo. -- --- **Features:** +-- ## Features: -- -- * AI_CARGO_DISPATCHER is the **base class** for: -- @@ -12,6 +12,17 @@ -- * Dispatches transport tasks to a common set of cargo transporting groups. -- * Different options can be setup to tweak the cargo transporation behaviour. -- +-- === +-- +-- ## Test Missions: +-- +-- Test missions can be located on the main GITHUB site. +-- +-- [FlightControl-Master/MOOSE_MISSIONS/AID - AI Dispatching/AID-CGO - AI Cargo Dispatching/] +-- (https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/AID%20-%20AI%20Dispatching/AID-CGO%20-%20AI%20Cargo%20Dispatching) +-- +-- === +-- -- ## The dispatcher concept -- -- Carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. @@ -20,13 +31,15 @@ -- @{Cargo.Cargo} must be declared within the mission to make the AI_CARGO_DISPATCHER object recognize the cargo. -- Please consult the @{Cargo.Cargo} module for more information. -- --- # Why cargo dispatching? +-- +-- ## Why cargo dispatching? -- -- It provides a realistic way of distributing your army forces around the battlefield, and to provide a quick means of cargo transportation. -- Instead of having troops or cargo to "appear" suddenly at certain locations, the dispatchers will pickup the cargo and transport it. -- It also allows to enforce or retreat your army from certain zones when needed, using helicopters or APCs. -- Airplanes can transport cargo over larger distances between the airfields. -- +-- -- ## What is a cargo object then? -- -- In order to make use of the MOOSE cargo system, you need to **declare** the DCS objects as MOOSE cargo objects! @@ -44,12 +57,14 @@ -- For more information, please consult the @{Cargo.Cargo} module documentation. Please read through it, because it will explain how to setup the cargo objects for use -- within your dispatchers. -- +-- -- ## Do I need to do a lot of coding to setup a dispatcher? -- -- No! It requires a bit of studying to set it up, but once you understand the different components that use the cargo dispatcher, it becomes very easy. -- Also, the dispatchers work in a true dynamic environment. The carriers and cargo, pickup and deploy zones can be created dynamically in your mission, -- and will automatically be recognized by the dispatcher. -- +-- -- ## Is the dispatcher causing a lot of CPU overhead? -- -- A little yes, but once the cargo is properly loaded into the carrier, the CPU consumption is very little. @@ -59,6 +74,7 @@ -- without any performance impact. The amount of helicopters that are active and flying in your simulation influences more the performance than the dispatchers. -- It really comes down to trying it out and getting experienced with what is possible and what is not (or too much). -- +-- -- ## Are the dispatchers a "black box" in terms of the logic? -- -- No. You can tailor the dispatcher mechanisms using event handlers, and create additional logic to enhance the behaviour and dynamism in your own mission. @@ -69,6 +85,7 @@ -- -- * When a carrier is picking up cargo, the *Pickup** event is triggered, and you can inform the coalition of this event, -- because it is an indication that troops are planned to join. +-- -- -- ## Are there options that you can set to modify the behaviour of the carries? -- @@ -79,6 +96,7 @@ -- * the height for airborne carriers when they fly to and from pickup and deploy zones. -- * the speed of the carriers. This is an important parameter, because depending on the tactication situation, speed will influence the detection by radars. -- +-- -- ## Can the zones be of any zone type? -- -- Yes, please ensure that the zones are declared using the @{Core.Zone} classes. @@ -86,12 +104,6 @@ -- -- === -- --- ### Test Missions --- --- [AI Cargo Dispatcher test missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/AID%20-%20AI%20Dispatching/AID-CGO%20-%20AI%20Cargo%20Dispatching) --- --- === --- -- ### Author: **FlightControl** -- -- === diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua index a4e9854ce..b4bef21bc 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_APC.lua @@ -1,6 +1,6 @@ --- **AI** -- (2.4) - Models the intelligent transportation of infantry and other cargo using APCs. -- --- **Features:** +-- ## Features: -- -- * Quickly transport cargo to various deploy zones using ground vehicles (APCs, trucks ...). -- * Various @{Cargo.Cargo#CARGO} types can be transported. These are infantry groups and crates. @@ -14,6 +14,15 @@ -- -- === -- +-- ## Test Missions: +-- +-- Test missions can be located on the main GITHUB site. +-- +-- [FlightControl-Master/MOOSE_MISSIONS/AID - AI Dispatching/AID-CGO - AI Cargo Dispatching/] +-- (https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/AID%20-%20AI%20Dispatching/AID-CGO%20-%20AI%20Cargo%20Dispatching) +-- +-- === +-- -- ### Author: **FlightControl** -- -- === diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua index bfcd74b15..11df8c188 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Airplane.lua @@ -1,12 +1,21 @@ --- **AI** -- (R2.4) - Models the intelligent transportation of infantry and other cargo using Planes. -- --- **Features:** +-- ## Features: -- -- * The airplanes will fly towards the pickup airbases to pickup the cargo. -- * The airplanes will fly towards the deploy airbases to deploy the cargo. -- -- === -- +-- ## Test Missions: +-- +-- Test missions can be located on the main GITHUB site. +-- +-- [FlightControl-Master/MOOSE_MISSIONS/AID - AI Dispatching/AID-CGO - AI Cargo Dispatching/] +-- (https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/AID%20-%20AI%20Dispatching/AID-CGO%20-%20AI%20Cargo%20Dispatching) +-- +-- === +-- -- ### Author: **FlightControl** -- -- === diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua index 1f546d37f..127c67dbb 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher_Helicopter.lua @@ -1,6 +1,6 @@ --- **AI** -- (2.4) - Models the intelligent transportation of infantry and other cargo using Helicopters. -- --- **Features:** +-- ## Features: -- -- * The helicopters will fly towards the pickup locations to pickup the cargo. -- * The helicopters will fly towards the deploy zones to deploy the cargo. @@ -9,6 +9,15 @@ -- -- === -- +-- ## Test Missions: +-- +-- Test missions can be located on the main GITHUB site. +-- +-- [FlightControl-Master/MOOSE_MISSIONS/AID - AI Dispatching/AID-CGO - AI Cargo Dispatching/] +-- (https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/AID%20-%20AI%20Dispatching/AID-CGO%20-%20AI%20Cargo%20Dispatching) +-- +-- === +-- -- ### Author: **FlightControl** -- -- === From 9ca61c680ed5c12a6408023cfabdd85a70292c4d Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sat, 22 Sep 2018 09:57:46 +0200 Subject: [PATCH 392/420] Documentation --- Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 02f852bc0..4197423f8 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -23,7 +23,7 @@ -- -- === -- --- ## The dispatcher concept +-- ## The dispatcher concept. -- -- Carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. -- The AI_CARGO_DISPATCHER module uses the @{Cargo.Cargo} capabilities within the MOOSE framework, to enable Carrier GROUP objects @@ -523,8 +523,9 @@ -- -- end -- +-- --- -- --- # 3) Set the pickup parameters. +-- # 4) Set the pickup parameters. -- -- Several parameters can be set to pickup cargo: -- @@ -532,7 +533,9 @@ -- * @{#AI_CARGO_DISPATCHER.SetPickupSpeed}(): Set the speed or randomizes the speed in km/h to pickup the cargo. -- * @{#AI_CARGO_DISPATCHER.SetPickupHeight}(): Set the height or randomizes the height in meters to pickup the cargo. -- --- # 4) Set the deploy parameters. +-- --- +-- +-- # 5) Set the deploy parameters. -- -- Several parameters can be set to deploy cargo: -- @@ -540,7 +543,9 @@ -- * @{#AI_CARGO_DISPATCHER.SetDeploySpeed}(): Set the speed or randomizes the speed in km/h to deploy the cargo. -- * @{#AI_CARGO_DISPATCHER.SetDeployHeight}(): Set the height or randomizes the height in meters to deploy the cargo. -- --- # 5) Set the home zone when there isn't any more cargo to pickup. +-- --- +-- +-- # 6) Set the home zone when there isn't any more cargo to pickup. -- -- A home zone can be specified to where the Carriers will move when there isn't any cargo left for pickup. -- Use @{#AI_CARGO_DISPATCHER.SetHomeZone}() to specify the home zone. From 163e42ee6eba097872459aba45972f4d6deda873 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sat, 22 Sep 2018 10:06:19 +0200 Subject: [PATCH 393/420] Documentation --- .../Moose/AI/AI_Cargo_Dispatcher.lua | 3 +- .../Moose/Tasking/Task_CARGO.lua | 50 ++++++++++++------- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 4197423f8..41cc93113 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -18,8 +18,7 @@ -- -- Test missions can be located on the main GITHUB site. -- --- [FlightControl-Master/MOOSE_MISSIONS/AID - AI Dispatching/AID-CGO - AI Cargo Dispatching/] --- (https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/AID%20-%20AI%20Dispatching/AID-CGO%20-%20AI%20Cargo%20Dispatching) +-- [FlightControl-Master/MOOSE_MISSIONS/AID - AI Dispatching/AID-CGO - AI Cargo Dispatching/](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/AID%20-%20AI%20Dispatching/AID-CGO%20-%20AI%20Cargo%20Dispatching) -- -- === -- diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index d3f830c65..235f69fe2 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -1,19 +1,35 @@ --- **Tasking** -- Base class to model tasks for players to transport cargo. -- +-- ## Features: +-- +-- * TASK_CARGO is the **base class** for: +-- +-- * @{Tasking.Task_Cargo_Transport#TASK_CARGO_TRANSPORT} +-- * @{Tasking.Task_Cargo_CSAR#TASK_CARGO_CSAR} +-- +-- +-- === +-- +-- ## Test Missions: +-- +-- Test missions can be located on the main GITHUB site. +-- +-- [FlightControl-Master/MOOSE_MISSIONS/TAD - Task Dispatching/CGO - Cargo Dispatching/](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/TAD%20-%20Task%20Dispatching/CGO%20-%20Cargo%20Dispatching) +-- -- === -- --- # 1) Tasking system. +-- ## Tasking system. -- --- #### If you are not yet aware what the MOOSE tasking system is about, read FIRST the explanation on tasking **@{Tasking.Task}**. +-- #### If you are not yet aware what the MOOSE tasking system is about, read FIRST the explanation on the @{Tasking.Task} module. -- -- === -- --- # 2) Context of cargo tasking. +-- ## Context of cargo tasking. -- -- The Moose framework provides various CARGO classes that allow DCS physical or logical objects to be transported or sling loaded by Carriers. -- The CARGO_ classes, as part of the MOOSE core, are able to Board, Load, UnBoard and UnLoad cargo between Carrier units. -- --- The TASK\_CARGO class is not meant to use within your missions as a mission designer. It is a base class, and other classes are derived from it. +-- The TASK_CARGO class is not meant to use within your missions as a mission designer. It is a base class, and other classes are derived from it. -- -- The following TASK_CARGO_ classes are important, as they implement the CONCRETE tasks: -- @@ -25,7 +41,7 @@ -- -- === -- --- # 3) Cargo tasking from a player perspective. +-- ## Cargo tasking from a player perspective. -- -- A human player can join the battle field in a client airborne slot or a ground vehicle within the CA module (ALT-J). -- The player needs to accept the task from the task overview list within the mission, using the menus. @@ -46,19 +62,19 @@ -- In the menu, you'll find for each CARGO, that is part of the scope of the task, various actions that can be completed. -- Depending on the location of your Carrier unit, the menu options will vary. -- --- ## 3.1) Joining a Cargo Transport Task +-- ### Joining a Cargo Transport Task -- -- Once you've joined a task, using the **Join Planned Task Menu**, -- you can Pickup cargo from a pickup location and Deploy cargo in deployment zones, using the **Task Action Menu**. -- --- ## 3.2) Task Action Menu. +-- ### Task Action Menu. -- -- When a player has joined a **`CARGO`** task (type), for that player only, -- it's **Task Action Menu** will show an additional menu options. -- -- From within this menu, you will be able to route to a cargo location, deploy zone, and load/unload cargo. -- --- ## 3.3) Pickup cargo by Boarding, Loading and Sling Loading. +-- ### Pickup cargo by Boarding, Loading and Sling Loading. -- -- There are three different ways how cargo can be picked up: -- @@ -140,7 +156,7 @@ -- These routing options will only be shown, when your carrier bays have cargo loaded. -- So, only when there is something to be deployed from your carrier, the deploy options will be shown. -- --- ### 3.3.1) Pickup Cargo. +-- #### Pickup Cargo. -- -- In order to pickup cargo, use the **task action menu** to **route to a specific cargo**. -- When a cargo route is selected, the HQ will send you routing messages indicating the location of the cargo. @@ -155,7 +171,7 @@ -- It takes a bit of skill to land a helicopter near a cargo to be loaded, but that is part of the game, isn't it? -- Expecially when you are landing in a "hot" zone, so when cargo is under immediate threat of fire. -- --- ### 3.3.2) Board Cargo (infantry). +-- #### Board Cargo (infantry). -- -- ![](../Tasking/Boarding_Ready.png) -- @@ -200,7 +216,7 @@ -- If during boarding the Carrier gets airborne, the boarding process will be cancelled. -- * The carrier must remain stationary when the boarding sequence has started until further notified. -- --- ### 3.3.3) Load Cargo. +-- #### Load Cargo. -- -- Cargo can be loaded into vehicles or helicopters or airplanes, as long as the carrier is sufficiently near to the cargo object. -- @@ -226,7 +242,7 @@ -- * For airborne Carriers, it is required to **land first right near the cargo**, before the loading process can be initiated. -- As stated, this requires some pilot skills :-) -- --- ### 3.3.4) Sling Load Cargo (helicopters only). +-- #### Sling Load Cargo (helicopters only). -- -- If your Carrier is within the **Loading Range of the cargo**, and the cargo is **stationary**, the **cargo can also be sling loaded**! -- Note that this is only possible for helicopters. @@ -238,7 +254,7 @@ -- As stated, this requires some pilot skills :-) -- -- --- ## 3.4) Deploy cargo by Unboarding, Unloading and Sling Deploying. +-- ### Deploy cargo by Unboarding, Unloading and Sling Deploying. -- -- #### **Deploying the relevant cargo within deploy zones, will make you achieve cargo transportation tasks!!!** -- @@ -286,7 +302,7 @@ -- ![Task_Types](../Tasking/Task_Cargo_Settings.JPG) -- Use the **Settings Menu** to select the coordinate format that you would like to use for location determination. -- --- ### 3.4.1) Unboard Cargo. +-- #### Unboard Cargo. -- -- If your carrier contains cargo, and the cargo is **moveable**, the **cargo can be unboarded**! -- You can only unload cargo if there is cargo within your cargo bays within the carrier. @@ -326,7 +342,7 @@ -- -- **Deploying a cargo within a deployment zone, may complete a deployment task! So ensure that you deploy the right cargo at the right deployment zone!** -- --- ### 3.4.2) Unload Cargo. +-- #### Unload Cargo. -- -- If your carrier contains cargo, and the cargo is **stationary**, the **cargo can be unloaded**, but not unboarded! -- You can only unload cargo if there is cargo within your cargo bays within the carrier. @@ -358,7 +374,7 @@ -- **Deploying a cargo within a deployment zone, may complete a deployment task! So ensure that you deploy the right cargo at the right deployment zone!** -- -- --- ### 3.4.3) Sling Deploy Cargo (helicopters only). +-- #### Sling Deploy Cargo (helicopters only). -- -- If your Carrier is within the **deploy zone**, and the cargo is **stationary**, the **cargo can also be sling deploying**! -- Note that this is only possible for helicopters. @@ -367,7 +383,7 @@ -- -- **Deploying a cargo within a deployment zone, may complete a deployment task! So ensure that you deploy the right cargo at the right deployment zone!** -- --- ## 4) Cargo tasking from a mission designer perspective. +-- ## Cargo tasking from a mission designer perspective. -- -- Please consult the documentation how to implement the derived classes of SET_CARGO in: -- From bef91cea353d431cc0e1c29a792d7676558ecbd7 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sat, 22 Sep 2018 10:24:47 +0200 Subject: [PATCH 394/420] Documentation --- Moose Development/Moose/Cargo/Cargo.lua | 48 ++++++++++++++++++++----- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 433836ba5..27a40b200 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -122,7 +122,7 @@ -- -- The cargo sets are extremely important for the AI cargo transportation dispatchers and the cargo transporation tasking. -- --- # 5) Declare MOOSE cargo within the mission editor!!! +-- # 5) Declare cargo directly in the mission editor! -- -- But I am not finished! There is something more, that is even more great! -- Imagine the mission designers having to code all these lines every time it wants to embed cargo within a mission. @@ -137,10 +137,11 @@ -- This would be extremely tiring and a huge overload. -- However, the MOOSE framework allows to declare MOOSE cargo objects within the mission editor!!! -- --- So, at mission startup, MOOSE will search for objects following a special naming convention, and will create for you dynamically --- cargo objects at mission start!!! --- These cargo objects can then be automatically incorporated within cargo set(s)!!! --- In other words, your mission would be reduced to about a few lines of code, providing you with a full dynamic cargo handling mission! +-- So, at mission startup, MOOSE will search for objects following a special naming convention, and will **create** for you **dynamically +-- cargo objects** at **mission start**!!! -- These cargo objects can then be automatically incorporated within cargo set(s)!!! +-- In other words, your mission will be reduced to about a few lines of code, providing you with a full dynamic cargo handling mission! +-- +-- ## 5.1) Use \#CARGO tags in the mission editor: -- -- MOOSE can create automatically cargo objects, if the name of the cargo contains the **\#CARGO** tag. -- When a mission starts, MOOSE will scan all group and static objects it found for the presence of the \#CARGO tag. @@ -178,11 +179,16 @@ -- And there is NO cargo object actually declared within the script! However, if you would open the mission, there would be hundreds of cargo objects... -- -- The \#CARGO tag even allows for several options to be specified, which are important to learn. --- For example, the following #CARGO naming in the group name of the object, will create a group cargo object for MOOSE. -- --- `Infantry #CARGO(T=Workmaterials,RR=500,NR=25)´ +-- ## 5.2) The \#CARGO tag to create CARGO_GROUP objects: -- --- This will create a cargo object: +-- You can also use the \#CARGO tag on **group** objects of the mission editor. +-- +-- For example, the following #CARGO naming in the **group name** of the object, will create a CARGO_GROUP object when the mission starts. +-- +-- `Infantry #CARGO(T=Workmaterials,RR=500,NR=25)` +-- +-- This will create a CARGO_GROUP object: -- -- * with the group name `Infantry #CARGO` -- * is of type `Workmaterials` @@ -200,6 +206,32 @@ -- * **NR=** Provide the maximum range in meters when the cargo units will be boarded within the carrier during boarding. -- Note that this option is optional, so can be omitted. The default value of the RR is 10 meters. -- +-- ## 5.2) The \#CARGO tag to create CARGO_CRATE objects: +-- +-- You can also use the \#CARGO tag on **static** objects, including **static cargo** objects of the mission editor. +-- +-- For example, the following #CARGO naming in the **static name** of the object, will create a CARGO_CRATE object when the mission starts. +-- +-- `Static #CARGO(T=Workmaterials,RR=500,NR=25)` +-- +-- This will create a CARGO_CRATE object: +-- +-- * with the group name `Static #CARGO` +-- * is of type `Workmaterials` +-- * will report when a carrier is within 500 meters +-- * will board to carriers when the carrier is within 500 meters from the cargo object +-- * will dissapear when the cargo is within 25 meters from the carrier during boarding +-- +-- So the overall syntax of the #CARGO naming tag and arguments are: +-- +-- `StaticName #CARGO(T=CargoTypeName,RR=Range,NR=Range)` +-- +-- * **T=** Provide a text that contains the type name of the cargo object. This type name can be used to filter cargo within a SET_CARGO object. +-- * **RR=** Provide the minimal range in meters when the report to the carrier, and board to the carrier. +-- Note that this option is optional, so can be omitted. The default value of the RR is 250 meters. +-- * **NR=** Provide the maximum range in meters when the cargo units will be boarded within the carrier during boarding. +-- Note that this option is optional, so can be omitted. The default value of the RR is 10 meters. +-- -- === -- -- ### Author: **FlightControl** From ddeb832f12cd73637b294a9ef186dee32a0f1a38 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sat, 22 Sep 2018 10:29:52 +0200 Subject: [PATCH 395/420] Documentation --- Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 41cc93113..afe2e3223 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -22,7 +22,7 @@ -- -- === -- --- ## The dispatcher concept. +-- # The dispatcher concept. -- -- Carrier equipment can be mobilized to intelligently transport infantry and other cargo within the simulation. -- The AI_CARGO_DISPATCHER module uses the @{Cargo.Cargo} capabilities within the MOOSE framework, to enable Carrier GROUP objects From fd70ffc83677643a19ed12ad91b74e63dd06f2e3 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 23 Sep 2018 11:02:36 +0200 Subject: [PATCH 396/420] Warehouse v0.5.2 Added NewAsset event. --- Moose Development/Moose/Cargo/CargoUnit.lua | 4 +- Moose Development/Moose/Core/Point.lua | 15 ++++ .../Moose/Functional/Detection.lua | 4 +- .../Moose/Functional/Warehouse.lua | 73 ++++++++++++++----- .../Moose/Wrapper/Positionable.lua | 14 ++-- 5 files changed, 84 insertions(+), 26 deletions(-) diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index 4429fc279..2c5fd65e8 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -242,7 +242,9 @@ do -- CARGO_UNIT if not self.CargoInAir then -- If NearRadius is given, then use the given NearRadius, otherwise calculate the NearRadius -- based upon the Carrier bounding radius, which is calculated from the bounding rectangle on the Y axis. + env.info(string.format("FF nearradius = %d (before)", NearRadius)) local NearRadius = CargoCarrier:GetBoundingRadius( NearRadius ) + 5 + env.info(string.format("FF nearradius = %d isnear=%s", NearRadius, tostring(self:IsNear( CargoCarrier:GetPointVec2(), NearRadius )) )) if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then self:Load( CargoCarrier, NearRadius, ... ) else @@ -288,7 +290,7 @@ do -- CARGO_UNIT -- @param Wrapper.Client#CLIENT CargoCarrier -- @param #number NearRadius Default 25 m. function CARGO_UNIT:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { From, Event, To, CargoCarrier:GetName() } ) + self:F( { From, Event, To, CargoCarrier:GetName(), NearRadius } ) if CargoCarrier and CargoCarrier:IsAlive() and self.CargoObject and self.CargoObject:IsAlive() then diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index b5d173644..72c98fa7c 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -938,6 +938,21 @@ do -- COORDINATE end + --- Set altitude. + -- @param #COORDINATE self + -- @param #number altitude New altitude in meters. + -- @param #boolean asl Altitude above sea level. Default is above ground level. + -- @return #COORDINATE The COORDINATE with adjusted altitude. + function COORDINATE:SetAltitude(altitude, asl) + local alt=altitude + if asl then + alt=altitude + else + alt=self:GetLandHeight()+altitude + end + self.y=alt + return self + end --- Add a Distance in meters from the COORDINATE horizontal plane, with the given angle, and calculate the new COORDINATE. -- @param #COORDINATE self diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 42f25311a..684ca0e9f 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -1897,7 +1897,7 @@ do -- DETECTION_UNITS --- Make text documenting the changes of the detected zone. -- @param #DETECTION_UNITS self - -- @param #DETECTION_UNITS.DetectedItem DetectedItem + -- @param #DETECTION_BASE.DetectedItem DetectedItem -- @return #string The Changes text function DETECTION_UNITS:GetChangeText( DetectedItem ) self:F( DetectedItem ) @@ -2149,7 +2149,7 @@ do -- DETECTION_TYPES --- Make text documenting the changes of the detected zone. -- @param #DETECTION_TYPES self - -- @param #DETECTION_TYPES.DetectedItem DetectedItem + -- @param Functional.Detection#DETECTION_BASE.DetectedItem DetectedItem -- @return #string The Changes text function DETECTION_TYPES:GetChangeText( DetectedItem ) self:F( DetectedItem ) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index ae96b68a0..871ae1742 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -11,6 +11,7 @@ -- * Realistic transportation of assets between warehouses. -- * Different means of automatic transportation (planes, helicopters, APCs, self propelled). -- * Strategic components such as capturing, defending and destroying warehouses and their associated infrastructure. +-- * Intelligent spawning of aircraft on airports (only if enough parking spots are available). -- * Can be easily interfaced to other MOOSE classes. -- -- Please not that his class is work in progress and in an **alpha** stage. @@ -121,8 +122,8 @@ -- Once the static warehouse object is placed in the mission editor it can be used as a MOOSE warehouse by the @{#WAREHOUSE.New}(*warehousestatic*, *alias*) constructor, -- like for example: -- --- warehouse=WAREHOUSE:New(STATIC:FindByName("Warehouse Static Batumi"), "My Warehouse Alias") --- warehouse:Start() +-- warehouseBatumi=WAREHOUSE:New(STATIC:FindByName("Warehouse Batumi"), "My Warehouse Alias") +-- warehouseBatumi:Start() -- -- The first parameter *warehousestatic* is the static MOOSE object. By default, the name of the warehouse will be the same as the name given to the static object. -- The second parameter *alias* can be used to choose a more convenient name if desired. This will be the name the warehouse calls itself when reporting messages. @@ -137,7 +138,7 @@ -- -- -- infrantry=GROUP:FindByName("Some Infantry Group") --- warehouse:AddAsset(infantry, 5) +-- warehouseBatumi:AddAsset(infantry, 5) -- -- This will add five infantry groups to the warehouse stock. Note that the group will normally be a late activated template group, -- which was defined in the mission editor. But you can also add other groups which are already spawned and present in the mission. @@ -158,19 +159,19 @@ -- For example, a UH-1H Huey has in DCS the attibute of an attack helicopter. But of course, it can also transport cargo. If you want to use it for transportation, you can specify this -- manually when the asset is added -- --- warehouse.Batumi:AddAsset("Huey", 5, WAREHOUSE.Attribute.AIR_TRANSPORTHELO) +-- warehouseBatumi:AddAsset("Huey", 5, WAREHOUSE.Attribute.AIR_TRANSPORTHELO) -- -- ### Setting the Cargo Bay Weight Limit -- You can also ajust the cargo bay weight limit, in case it is not calculated correctly automatically. For example, the cargo bay of a C-17A is much smaller in DCS than that of a C-130, which is -- unrealistic. This can be corrected by the *forcecargobay* parmeter which is here set to 77,000 kg -- --- warehouse.Batumi:AddAsset("C-17A", nil, 77000) +-- warehouseBatumi:AddAsset("C-17A", nil, nil, 77000) -- -- ### Setting the Weight -- In the current version of DCS a mortar unit has a weight of 5 tons. This confuses the transporter logic, because it appears to be too have for, e.g. all APCs. You can manually adjust the weight -- by the *forceweight* parameter and set it to 210 kg for each unit in the group -- --- warehouse.Batumi:AddAsset("Mortar Alpha", nil, nil, nil, 210) +-- warehouseBatumi:AddAsset("Mortar Alpha", nil, nil, nil, 210) -- -- === -- @@ -195,7 +196,7 @@ -- -- For example: -- --- warehouse.Batumi:AddRequest(warehouse.Kobuleti, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 5, WAREHOUSE.TransportType.APC, 2) +-- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 5, WAREHOUSE.TransportType.APC, 2) -- -- Here, warehouse Kobuleti requests 5 infantry groups from warehouse Batumi. These "cargo" assets should be transported from Batumi to Kobuleti by 2 APCS. -- Note that the warehouse at Batumi needs to have at least five infantry groups and two APC groups in their stock if the request can be processed. @@ -209,7 +210,7 @@ -- -- A more specific request could look like: -- --- warehouse.Batumi:AddRequest(warehouse.Kobuleti, WAREHOUSE.Descriptor.UNITTYPE, "A-10C", 2) +-- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.UNITTYPE, "A-10C", 2) -- -- Here, Kobuleti requests a specific unit type, in particular two groups of A-10Cs. Note that the spelling is important as it must exacly be the same as -- what one get's when using the DCS unit type. @@ -218,7 +219,7 @@ -- -- An even more specific request would be: -- --- warehouse.Batumi:AddRequest(warehouse.Kobuleti, WAREHOUSE.Descriptor.TEMPLATENAME, "Group Name as in ME", 3) +-- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.TEMPLATENAME, "Group Name as in ME", 3) -- -- In this case three groups named "Group Name as in ME" are requested. This explicitly request the groups named like that in the Mission Editor. -- @@ -226,7 +227,7 @@ -- -- On the other hand, very general unspecifc requests can be made as -- --- warehouse.Batumi:AddRequest(warehouse.Kobuleti, WAREHOUSE.Descriptor.CATEGORY, Group.Category.Ground, 10) +-- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.CATEGORY, Group.Category.Ground, 10) -- -- Here, Kubuleti requests 10 ground groups and does not care which ones. This could be a mix of infantry, APCs, trucks etc. -- @@ -237,7 +238,7 @@ -- Assets in the warehouse' stock can used for user defined tasks realtively easily. They can be spawned into the game by a "*self request*", i.e. the warehouse -- requests the assets from itself: -- --- warehouse.Batumi:AddRequest(warehouse.Batumi, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 5) +-- warehouseBatumi:AddRequest(warehouseBatumi, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 5) -- -- This would simply spawn five infantry groups in the spawn zone of the Batumi warehouse if/when they are available. -- @@ -365,7 +366,9 @@ -- # Why is my request not processed? -- -- For each request, the warehouse class logic does a lot of consistancy and validation checks under the hood. --- This means that sometimes a request is deemed to be *invalid* in which case they are deleted from the queue or considered to be valid but cannot be executed at this very moment. +-- This helps to circumvent a lot of DCS issues and shortcomings. For example, it is checked that enough free +-- parking spots at an airport are available *before* the assets are spawned. +-- However, this also means that sometimes a request is deemed to be *invalid* in which case they are deleted from the queue or considered to be valid but cannot be executed at this very moment. -- -- ## Invalid Requests -- @@ -1308,7 +1311,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.5.1" +WAREHOUSE.version="0.5.2" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -1427,6 +1430,7 @@ function WAREHOUSE:New(warehouse, alias) self:AddTransition("Loaded", "Start", "Running") -- TODO Start the warehouse when loaded from disk. self:AddTransition("*", "Status", "*") -- Status update. self:AddTransition("*", "AddAsset", "*") -- Add asset to warehouse stock. + self:AddTransition("*", "NewAsset", "*") -- New asset was added to warehouse stock. self:AddTransition("*", "AddRequest", "*") -- New request from other warehouse. self:AddTransition("Running", "Request", "*") -- Process a request. Only in running mode. self:AddTransition("Attacked", "Request", "*") -- Process a request. Only in running mode. @@ -1517,6 +1521,26 @@ function WAREHOUSE:New(warehouse, alias) -- @param #number forceweight (Optional) Explicitly force weight in kg of each unit in the group. + --- Triggers the FSM event "NewAsset" when a new asset has been added to the warehouse stock. + -- @function [parent=#WAREHOUSE] NewAsset + -- @param #WAREHOUSE self + -- @param #number delay Delay in seconds. + -- @param #WAREHOUSE.Assetitem asset The new asset. + + --- Triggers the FSM delayed event "NewAsset" when a new asset has been added to the warehouse stock. + -- @function [parent=#WAREHOUSE] NewAsset + -- @param #WAREHOUSE self + -- @param #WAREHOUSE.Assetitem asset The new asset. + + --- On after "NewAsset" event user function. A new asset has been added to the warehouse stock. + -- @function [parent=#WAREHOUSE] OnAfterNewAsset + -- @param #WAREHOUSE self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #WAREHOUSE.Assetitem asset The asset that has just been added + + --- Triggers the FSM event "AddRequest". Add a request to the warehouse queue, which is processed when possible. -- @function [parent=#WAREHOUSE] AddRequest -- @param #WAREHOUSE self @@ -2715,6 +2739,7 @@ end -- @param #number forcecargobay (Optional) Explicitly force cargobay weight limit in kg for cargo carriers. This is for each *unit* of the group. -- @param #number forceweight (Optional) Explicitly force weight in kg of each unit in the group. function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribute, forcecargobay, forceweight) + self:T({group=group, ngroups=ngroups, forceattribute=forceattribute, forcecargobay=forcecargobay, forceweight=forceweight}) -- Set default. local n=ngroups or 1 @@ -2766,6 +2791,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu if asset~=nil then self:_DebugMessage(string.format("Warehouse %s: Adding KNOWN asset uid=%d with attribute=%s to stock.", self.alias, asset.uid, asset.attribute), 5) table.insert(self.stock, asset) + self:NewAsset(asset) else self:_ErrorMessage(string.format("ERROR: Known asset could not be found in global warehouse db!"), 0) end @@ -2784,6 +2810,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu -- Add created assets to stock of this warehouse. for _,asset in pairs(assets) do table.insert(self.stock, asset) + self:NewAsset(asset) end end @@ -2812,7 +2839,7 @@ end -- @param #number forceweight Weight of units in kg. -- @return #table A table containing all registered assets. function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay, forceweight) - self:F({groupname=group:GetName(), ngroups=ngroups, forceattribute=forceattribute}) + self:F({groupname=group:GetName(), ngroups=ngroups, forceattribute=forceattribute, forcecargobay=forcecargobay, forceweight=forceweight}) -- Set default. local n=ngroups or 1 @@ -2864,6 +2891,7 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay, -- Cargo bay size. local bay=forcecargobay or unit:GetCargoBayFreeWeight() + env.info(string.format("FF unit %s bay=%d", unit:GetName(), bay)) -- Add bay size to table. table.insert(cargobay, bay) @@ -2944,6 +2972,15 @@ function WAREHOUSE:_AssetItemInfo(asset) self:T3({Template=asset.template}) end +--- On after "NewAsset" event. A new asset has been added to the warehouse stock. +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #WAREHOUSE.Assetitem asset The asset that has just been added +function WAREHOUSE:onafterNewAsset(From, Event, To, asset) +end + ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --- On before "AddRequest" event. Checks some basic properties of the given parameters. @@ -3278,6 +3315,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then _loadradius=10000 + _nearradius=500 elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then --_loadradius=1000 _loadradius=nil @@ -5412,10 +5450,10 @@ function WAREHOUSE:_CheckRequestNow(request) -- Check if enough assets are in stock. if not _enough then - local text=string.format("Warehouse %s: Request denied! Not enough (cargo) assets currently available.", self.alias) + local text=string.format("Warehouse %s: Request ID=%d denied! Not enough (cargo) assets currently available.", self.alias, request.uid) self:_InfoMessage(text, 5) - --text=string.format("Enough=%s, #_assets=%d, _nassets=%d, request.nasset=%s", tostring(_enough), #_assets,_nassets, tostring(request.nasset)) - --env.info(text) + text=string.format("Enough=%s, #_assets=%d, _nassets=%d, request.nasset=%s", tostring(_enough), #_assets,_nassets, tostring(request.nasset)) + self:T(self.wid..text) return false end @@ -5600,6 +5638,7 @@ function WAREHOUSE:_GetTransportsForAssets(request) -- How many times does the cargo fit into the carrier? local delta=cargobay-asset.weight + env.info(string.format("k=%d, j=%d delta=%d cargobay=%d weight=%d", k, j, delta, cargobay, asset.weight)) --self:E(self.wid..string.format("%s unit %d loads cargo uid=%d: bayempty=%02d, bayloaded = %02d - weight=%02d", transport.templatename, k, asset.uid, transport.cargobay[k], cargobay, asset.weight)) diff --git a/Moose Development/Moose/Wrapper/Positionable.lua b/Moose Development/Moose/Wrapper/Positionable.lua index 97becb610..ac01535fe 100644 --- a/Moose Development/Moose/Wrapper/Positionable.lua +++ b/Moose Development/Moose/Wrapper/Positionable.lua @@ -318,20 +318,20 @@ end --- Get the bounding radius of the underlying POSITIONABLE DCS Object. -- @param #POSITIONABLE self --- @return DCS#Distance The bounding radius of the POSITIONABLE. --- @return #nil The POSITIONABLE is not existing or alive. -function POSITIONABLE:GetBoundingRadius() +-- @param #number mindist (Optional) If bounding box is smaller than this value, mindist is returned. +-- @return DCS#Distance The bounding radius of the POSITIONABLE or #nil if the POSITIONABLE is not existing or alive. +function POSITIONABLE:GetBoundingRadius(mindist) self:F2() local Box = self:GetBoundingBox() - + local boxmin=mindist or 0 if Box then local X = Box.max.x - Box.min.x local Z = Box.max.z - Box.min.z local CX = X / 2 local CZ = Z / 2 - return math.max( CX, CZ ) + return math.max( math.max( CX, CZ ), boxmin ) end BASE:E( { "Cannot GetBoundingRadius", Positionable = self, Alive = self:IsAlive() } ) @@ -1025,7 +1025,9 @@ do -- Cargo function POSITIONABLE:SetCargoBayWeightLimit( WeightLimit ) if WeightLimit then - self.__.CargoBayWeightLimit = self.__.CargoBayWeightLimit or WeightLimit + self.__.CargoBayWeightLimit = WeightLimit + elseif self.__.CargoBayWeightLimit~=nil then + -- Value already set ==> Do nothing! else -- If weightlimit is not provided, we will calculate it depending on the type of unit. From 954ca31c79083f6b48245e44b0308a37d54a858c Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 23 Sep 2018 21:45:19 +0200 Subject: [PATCH 397/420] Strange Things! --- Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index afe2e3223..74fe9d9cb 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -615,13 +615,16 @@ AI_CARGO_DISPATCHER.PickupCargo = {} -- AICargoDispatcherAirplanes = AI_CARGO_DISPATCHER_AIRPLANE:New( AirplanesSet, CargoInfantrySet, PickupZoneSet, DeployZoneSet ) -- AICargoDispatcherAirplanes:Start() -- -function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo ) +function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo, PickupZoneSet, DeployZoneSet ) local self = BASE:Inherit( self, FSM:New() ) -- #AI_CARGO_DISPATCHER self.SetCarrier = SetCarrier -- Core.Set#SET_GROUP self.SetCargo = SetCargo -- Core.Set#SET_CARGO + self.PickupZoneSet=PickupZoneSet + self.DeployZoneSet=DeployZoneSet + self:SetStartState( "Idle" ) self:AddTransition( "Monitoring", "Monitor", "Monitoring" ) @@ -1094,7 +1097,10 @@ function AI_CARGO_DISPATCHER:onafterMonitor() local CargoCoordinate = Cargo:GetCoordinate() local CoordinateFree = true --self.PickupZoneSet:Flush() - PickupZone = self.PickupZoneSet and self.PickupZoneSet:IsCoordinateInZone( CargoCoordinate ) + env.info("FF pickupzoneset") + self:E(self.PickupZoneSet) + PickupZone = self.PickupZoneSet and self.PickupZoneSet:GetRandomZone():IsCoordinateInZone( CargoCoordinate ) + self:E(PickupZone) if not self.PickupZoneSet or PickupZone then for CarrierPickup, Coordinate in pairs( self.PickupCargo ) do if CarrierPickup:IsAlive() == true then @@ -1133,6 +1139,7 @@ function AI_CARGO_DISPATCHER:onafterMonitor() if PickupCargo then self.CarrierHome[Carrier] = nil local PickupCoordinate = PickupCargo:GetCoordinate():GetRandomCoordinateInRadius( self.PickupOuterRadius, self.PickupInnerRadius ) + env.info("FF pickup zone = "..tostring(PickupZone:GetName())) AI_Cargo:Pickup( PickupCoordinate, math.random( self.PickupMinSpeed, self.PickupMaxSpeed ), math.random( self.PickupMinHeight, self.PickupMaxHeight ), PickupZone ) break else From def7a1513ed72f6f179fb82dc12b4e74e0ee51e4 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Mon, 24 Sep 2018 05:40:40 +0200 Subject: [PATCH 398/420] fixes stupid error I did on AI_CARGO_DISPATCHER. --- .../Moose/AI/AI_Cargo_Dispatcher.lua | 46 ++++++++++++++----- Moose Development/Moose/Core/Database.lua | 6 +-- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index afe2e3223..d9f162032 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -112,6 +112,24 @@ --- @type AI_CARGO_DISPATCHER +-- @field Core.Set#SET_GROUP CarrierSet The set of @{Wrapper.Group#GROUP} objects of carriers that will transport the cargo. +-- @field Core.Set#SET_CARGO CargoSet The set of @{Cargo.Cargo#CARGO} objects, which can be CARGO_GROUP, CARGO_CRATE, CARGO_SLINGLOAD objects. +-- @field Core.Zone#SET_ZONE PickupZoneSet The set of pickup zones, which are used to where the cargo can be picked up by the carriers. If nil, then cargo can be picked up everywhere. +-- @field Core.Zone#SET_ZONE DeployZoneSet The set of deploy zones, which are used to where the cargo will be deployed by the carriers. +-- @field #number PickupMaxSpeed The maximum speed to move to the cargo pickup location. +-- @field #number PickupMinSpeed The minimum speed to move to the cargo pickup location. +-- @field #number DeployMaxSpeed The maximum speed to move to the cargo deploy location. +-- @field #number DeployMinSpeed The minimum speed to move to the cargo deploy location. +-- @field #number PickupMaxHeight The maximum height to fly to the cargo pickup location. +-- @field #number PickupMinHeight The minimum height to fly to the cargo pickup location. +-- @field #number DeployMaxHeight The maximum height to fly to the cargo deploy location. +-- @field #number DeployMinHeight The minimum height to fly to the cargo deploy location. +-- @field #number PickupOuterRadius The outer radius in meters around the cargo coordinate to pickup the cargo. +-- @field #number PickupInnerRadius The inner radius in meters around the cargo coordinate to pickup the cargo. +-- @field #number DeployOuterRadius The outer radius in meters around the cargo coordinate to deploy the cargo. +-- @field #number DeployInnerRadius The inner radius in meters around the cargo coordinate to deploy the cargo. +-- @field Core.Zone#ZONE_BASE HomeZone The home zone where the carriers will return when there is no more cargo to pickup. +-- @field #number MonitorTimeInterval The interval in seconds when the cargo dispatcher will search for new cargo to be picked up. -- @extends Core.Fsm#FSM @@ -556,23 +574,23 @@ -- @field #AI_CARGO_DISPATCHER AI_CARGO_DISPATCHER = { ClassName = "AI_CARGO_DISPATCHER", - SetCarrier = nil, - DeployZoneSet = nil, AI_Cargo = {}, PickupCargo = {} } ---- @field #AI_CARGO_DISPATCHER.AI_Cargo +--- @field #list AI_CARGO_DISPATCHER.AI_Cargo = {} ---- @field #AI_CARGO_DISPATCHER.PickupCargo +--- @field #list AI_CARGO_DISPATCHER.PickupCargo = {} --- Creates a new AI_CARGO_DISPATCHER object. -- @param #AI_CARGO_DISPATCHER self --- @param Core.Set#SET_GROUP SetCarrier --- @param Core.Set#SET_CARGO SetCargo +-- @param Core.Set#SET_GROUP CarrierSet The set of @{Wrapper.Group#GROUP} objects of carriers that will transport the cargo. +-- @param Core.Set#SET_CARGO CargoSet The set of @{Cargo.Cargo#CARGO} objects, which can be CARGO_GROUP, CARGO_CRATE, CARGO_SLINGLOAD objects. +-- @param Core.Set#SET_ZONE PickupZoneSet (optional) The set of pickup zones, which are used to where the cargo can be picked up by the carriers. If nil, then cargo can be picked up everywhere. +-- @param Core.Set#SET_ZONE DeployZoneSet The set of deploy zones, which are used to where the cargo will be deployed by the carriers. -- @return #AI_CARGO_DISPATCHER -- @usage -- @@ -615,12 +633,16 @@ AI_CARGO_DISPATCHER.PickupCargo = {} -- AICargoDispatcherAirplanes = AI_CARGO_DISPATCHER_AIRPLANE:New( AirplanesSet, CargoInfantrySet, PickupZoneSet, DeployZoneSet ) -- AICargoDispatcherAirplanes:Start() -- -function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo ) +function AI_CARGO_DISPATCHER:New( CarrierSet, CargoSet, PickupZoneSet, DeployZoneSet ) local self = BASE:Inherit( self, FSM:New() ) -- #AI_CARGO_DISPATCHER - self.SetCarrier = SetCarrier -- Core.Set#SET_GROUP - self.SetCargo = SetCargo -- Core.Set#SET_CARGO + self.SetCarrier = CarrierSet -- Core.Set#SET_GROUP + self.SetCargo = CargoSet -- Core.Set#SET_CARGO + + self.PickupZoneSet = PickupZoneSet + self.DeployZoneSet = DeployZoneSet + self:SetStartState( "Idle" ) @@ -654,7 +676,7 @@ function AI_CARGO_DISPATCHER:New( SetCarrier, SetCargo ) self.CarrierHome = {} -- Put a Dead event handler on SetCarrier, to ensure that when a carrier is destroyed, that all internal parameters are reset. - function SetCarrier.OnAfterRemoved( SetCarrier, From, Event, To, CarrierName, Carrier ) + function self.SetCarrier.OnAfterRemoved( SetCarrier, From, Event, To, CarrierName, Carrier ) self:F( { Carrier = Carrier:GetName() } ) self.PickupCargo[Carrier] = nil self.CarrierHome[Carrier] = nil @@ -666,7 +688,7 @@ end --- Set the monitor time interval. -- @param #AI_CARGO_DISPATCHER self --- @param #number MonitorTimeInterval +-- @param #number MonitorTimeInterval The interval in seconds when the cargo dispatcher will search for new cargo to be picked up. -- @return #AI_CARGO_DISPATCHER function AI_CARGO_DISPATCHER:SetMonitorTimeInterval( MonitorTimeInterval ) @@ -680,7 +702,7 @@ end -- When there is nothing anymore to pickup, the carriers will go to a random coordinate in this zone. -- They will await here new orders. -- @param #AI_CARGO_DISPATCHER self --- @param Core.Zone#ZONE_BASE HomeZone +-- @param Core.Zone#ZONE_BASE HomeZone The home zone where the carriers will return when there is no more cargo to pickup. -- @return #AI_CARGO_DISPATCHER -- @usage -- diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 56b3faa46..6163bac25 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -310,9 +310,9 @@ do -- Zones end for ZoneGroupName, ZoneGroup in pairs( self.GROUPS ) do - if ZoneGroupName:match("~ZONE_POLYGON") then - local ZoneName1 = ZoneGroupName:match("(.*)~ZONE_POLYGON") - local ZoneName2 = ZoneGroupName:match(".*~ZONE_POLYGON(.*)") + if ZoneGroupName:match("#ZONE_POLYGON") then + local ZoneName1 = ZoneGroupName:match("(.*)#ZONE_POLYGON") + local ZoneName2 = ZoneGroupName:match(".*#ZONE_POLYGON(.*)") local ZoneName = ZoneName1 .. ( ZoneName2 or "" ) self:I( { "Register ZONE_POLYGON:", Name = ZoneName } ) From 483c5deddd88330f893067fb22df8545a778323b Mon Sep 17 00:00:00 2001 From: Frank Date: Mon, 24 Sep 2018 10:00:43 +0200 Subject: [PATCH 399/420] Wrong --- Moose Development/Moose/AI/AI_Cargo_Airplane.lua | 2 +- Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 82334c8ab..5fb72f990 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -272,7 +272,7 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate, -- Get closest airbase of current position. local ClosestAirbase, DistToAirbase=Airplane:GetCoordinate():GetClosestAirbase() - + --env.info("FF onafterpickup closest airbase "..ClosestAirbase:GetName()) -- Two cases. Aircraft spawned in air or at an airbase. diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index 74fe9d9cb..50985f02e 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -112,6 +112,8 @@ --- @type AI_CARGO_DISPATCHER +-- @field Core.Set#SET_ZONE PickupZoneSet +-- @field Core.Set#SET_ZONE DeployZoneSet -- @extends Core.Fsm#FSM @@ -557,6 +559,7 @@ AI_CARGO_DISPATCHER = { ClassName = "AI_CARGO_DISPATCHER", SetCarrier = nil, + PickupZoneSet = nil, DeployZoneSet = nil, AI_Cargo = {}, PickupCargo = {} @@ -1097,10 +1100,8 @@ function AI_CARGO_DISPATCHER:onafterMonitor() local CargoCoordinate = Cargo:GetCoordinate() local CoordinateFree = true --self.PickupZoneSet:Flush() - env.info("FF pickupzoneset") - self:E(self.PickupZoneSet) - PickupZone = self.PickupZoneSet and self.PickupZoneSet:GetRandomZone():IsCoordinateInZone( CargoCoordinate ) - self:E(PickupZone) + PickupZone = self.PickupZoneSet:GetRandomZone() + --PickupZone = self.PickupZoneSet and self.PickupZoneSet:IsCoordinateInZone( CargoCoordinate ) if not self.PickupZoneSet or PickupZone then for CarrierPickup, Coordinate in pairs( self.PickupCargo ) do if CarrierPickup:IsAlive() == true then @@ -1139,7 +1140,6 @@ function AI_CARGO_DISPATCHER:onafterMonitor() if PickupCargo then self.CarrierHome[Carrier] = nil local PickupCoordinate = PickupCargo:GetCoordinate():GetRandomCoordinateInRadius( self.PickupOuterRadius, self.PickupInnerRadius ) - env.info("FF pickup zone = "..tostring(PickupZone:GetName())) AI_Cargo:Pickup( PickupCoordinate, math.random( self.PickupMinSpeed, self.PickupMaxSpeed ), math.random( self.PickupMinHeight, self.PickupMaxHeight ), PickupZone ) break else From 49d2f5eef174de0dbb6f036600a81c918de6eddc Mon Sep 17 00:00:00 2001 From: Frank Date: Mon, 24 Sep 2018 10:44:23 +0200 Subject: [PATCH 400/420] Back --- Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua index d9dd9dc8a..533a83e15 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Dispatcher.lua @@ -640,9 +640,6 @@ function AI_CARGO_DISPATCHER:New( CarrierSet, CargoSet, PickupZoneSet, DeployZon self.SetCarrier = CarrierSet -- Core.Set#SET_GROUP self.SetCargo = CargoSet -- Core.Set#SET_CARGO - self.PickupZoneSet = PickupZoneSet - self.DeployZoneSet = DeployZoneSet - self.PickupZoneSet=PickupZoneSet self.DeployZoneSet=DeployZoneSet @@ -1119,8 +1116,8 @@ function AI_CARGO_DISPATCHER:onafterMonitor() local CargoCoordinate = Cargo:GetCoordinate() local CoordinateFree = true --self.PickupZoneSet:Flush() - PickupZone = self.PickupZoneSet:GetRandomZone() - --PickupZone = self.PickupZoneSet and self.PickupZoneSet:IsCoordinateInZone( CargoCoordinate ) + --PickupZone = self.PickupZoneSet:GetRandomZone() + PickupZone = self.PickupZoneSet and self.PickupZoneSet:IsCoordinateInZone( CargoCoordinate ) if not self.PickupZoneSet or PickupZone then for CarrierPickup, Coordinate in pairs( self.PickupCargo ) do if CarrierPickup:IsAlive() == true then From 125dc09e4db8bd3a37a4903718c942bd37d645fa Mon Sep 17 00:00:00 2001 From: Frank Date: Mon, 24 Sep 2018 22:58:23 +0200 Subject: [PATCH 401/420] Warehouse v0.5.3 Added optional loadradius. Improved documentation. Changes in deploy radius. --- .../Moose/Functional/Warehouse.lua | 147 +++++++++++------- 1 file changed, 94 insertions(+), 53 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 871ae1742..f7d7a4242 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -6,12 +6,14 @@ -- -- Features: -- --- * Holds (virtual) assests in stock. +-- * Holds (virtual) assests in stock and spawns them upon request. -- * Manages requests of assets from other warehouses. +-- * Queueing system with optional priorization of requests. -- * Realistic transportation of assets between warehouses. -- * Different means of automatic transportation (planes, helicopters, APCs, self propelled). -- * Strategic components such as capturing, defending and destroying warehouses and their associated infrastructure. -- * Intelligent spawning of aircraft on airports (only if enough parking spots are available). +-- * Possibility to hook into events and customize actions. -- * Can be easily interfaced to other MOOSE classes. -- -- Please not that his class is work in progress and in an **alpha** stage. @@ -86,6 +88,7 @@ -- It also alliviates the problem of limited parking spots at smaller air bases -- -- ## What means of transportation are available? +-- -- Firstly, all mobile assets can be send from warehouse to another on their own. -- -- * Ground vehicles will use the road infrastructure. So a good road connection for both warehouses is important but also off road connections can be added if necessary. @@ -133,9 +136,8 @@ -- -- # Adding Assets -- --- Assets can be added to the warehouse stock by using the @{#WAREHOUSE.AddAsset}(*group*, *ngroups*, *forceattribute*, *forcecargobay*, *forceweight*) function. The parameter *group* has to be a MOOSE @{Wrapper.Group#GROUP}. --- The parameter *ngroups* specifies how many clones of this group are added to the stock. --- +-- Assets can be added to the warehouse stock by using the @{#WAREHOUSE.AddAsset}(*group*, *ngroups*, *forceattribute*, *forcecargobay*, *forceweight*, *loadradius*) function. +-- The parameter *group* has to be a MOOSE @{Wrapper.Group#GROUP}. The parameter *ngroups* specifies how many clones of this group are added to the stock. -- -- infrantry=GROUP:FindByName("Some Infantry Group") -- warehouseBatumi:AddAsset(infantry, 5) @@ -143,7 +145,7 @@ -- This will add five infantry groups to the warehouse stock. Note that the group will normally be a late activated template group, -- which was defined in the mission editor. But you can also add other groups which are already spawned and present in the mission. -- --- You can add assets with a delay by using the @{#WAREHOUSE.__AddAsset}(*delay*, *group*, *ngroups*, *foceattribute*, *forcecargobay*, *forceweight*), where *delay* +-- You can add assets with a delay by using the @{#WAREHOUSE.__AddAsset}(*delay*, *group*, *ngroups*, *foceattribute*, *forcecargobay*, *forceweight*, *loadradius*), where *delay* -- is the delay in seconds before the asset is added. -- -- In game, the warehouse will get a mark which is regularly updated and showing the currently available assets in stock. @@ -235,7 +237,7 @@ -- -- # Employing Assets -- --- Assets in the warehouse' stock can used for user defined tasks realtively easily. They can be spawned into the game by a "*self request*", i.e. the warehouse +-- Assets in the warehouses stock can used for user defined tasks realtively easily. They can be spawned into the game by a "*self request*", i.e. the warehouse -- requests the assets from itself: -- -- warehouseBatumi:AddRequest(warehouseBatumi, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 5) @@ -255,6 +257,8 @@ -- -- @param Core.Set#SET_GROUP groupset The set of cargo groups that was delivered to the warehouse itself. -- -- @param #WAREHOUSE.Pendingitem request Pending self request. -- function WAREHOUSE:OnAfterSelfRequest(From, Event, To, groupset, request) +-- local groupset=groupset --Core.Set#SET_GROUP +-- local request=request --Functional.Warehouse#WAREHOUSE.Pendingitem -- -- for _,group in pairs(groupset:GetSetObjects()) do -- local group=group --Wrapper.Group#GROUP @@ -266,7 +270,7 @@ -- The variable *groupset* is a @{Core.Set#SET_GROUP} object and holds all asset groups from the request. The code above shows, how the mission designer can access the groups -- for further tasking. Here, the groups are only smoked but, of course, you can use them for whatever task you fancy. -- --- Note that airborne groups are spawned in uncontrolled state and need to be activated first before they can start their assigned mission. +-- Note that airborne groups are spawned in **uncontrolled state** and need to be activated first before they can start their assigned mission. -- This can be done with the @{Wrapper.Controllable#CONTROLLABLE.StartUncontrolled} function. -- -- === @@ -368,38 +372,69 @@ -- For each request, the warehouse class logic does a lot of consistancy and validation checks under the hood. -- This helps to circumvent a lot of DCS issues and shortcomings. For example, it is checked that enough free -- parking spots at an airport are available *before* the assets are spawned. --- However, this also means that sometimes a request is deemed to be *invalid* in which case they are deleted from the queue or considered to be valid but cannot be executed at this very moment. +-- However, this also means that sometimes a request is deemed to be *invalid* in which case they are deleted +-- from the queue or considered to be valid but cannot be executed at this very moment. -- -- ## Invalid Requests -- --- Invalid request are requests which can **never** be processes because there is some logical or physical argument against it. Or simply because that feature was not implemented (yet). +-- Invalid request are requests which can **never** be processes because there is some logical or physical argument against it. +-- (Or simply because that feature was not implemented (yet).) -- -- * All airborne assets need an associated airbase of any kind on the sending *and* receiving warhouse. -- * Airplanes need an airdrome at the sending and receiving warehouses. --- * Not enough parking spots of the right terminal type at the sending warehouse. --- * No parking spots of the right terminal type at the receiving warehouse. --- * Ground assets need a road connection between both warehouses. +-- * Not enough parking spots of the right terminal type at the sending warehouse. This avoids planes spawning on runways or on top of each other. +-- * No parking spots of the right terminal type at the receiving warehouse. This avoids DCS despawning planes on landing if they have no valid parking spot. +-- * Ground assets need a road connection between both warehouses or an off-road path needs to be added manually. -- * Ground assets cannot be send directly to ships, i.e. warehouses on ships. -- * Naval units need a user defined shipping lane between both warehouses. -- * Warehouses need a user defined port zone to spawn naval assets. +-- * The receiving warehouse is destroyed or stopped. -- * If transport by airplane, both warehouses must have and airdrome. -- * If transport by APC, both warehouses must have a road connection. --- * If transport by helicopter, the sending airbase must have an associated airbase. +-- * If transport by helicopter, the sending airbase must have an associated airbase (airdrome or FARP). -- --- All invalid requests are removed from the warehouse queue! +-- All invalid requests are cancelled and **removed** from the warehouse queue! -- -- ## Temporarily Unprocessable Requests -- -- Temporarily unprocessable requests are possible in priciple, but cannot be processed at the given time the warehouse checks its queue. -- --- * No enough parking spaces are available for the requests assets but the airbase has enough parking spots in total so that this request is possible once other aircraft have taken off. --- * Requesting warehouse is not in state "Running" (could be stopped, not yet started or under attack). +-- * No enough parking spaces are available for all requested assets but the airbase has enough parking spots in total so that this request is possible once other aircraft have taken off. +-- * The requesting warehouse is not in state "Running" (could be paused, not yet started or under attack). -- * Not enough cargo assets available at this moment. -- * Not enough free parking spots for all cargo or transport airborne assets at the moment. -- * Not enough transport assets to carry all cargo assets. -- -- Temporarily unprocessable requests are held in the queue. If at some point in time, the situation changes so that these requests can be processed, they are executed. -- +-- ## Cargo Bay and Weight Limitations +-- +-- The transporation of cargo is handled by the AI\_Dispatcher classes. These take the cargo bay of a carrier and the weight of +-- the cargo into account so that a carrier can only load a realistic amount of cargo. +-- +-- However, if troops are supposed to be transported between warehouses, there is one important limitations one has to keep in mind. +-- This is that **cargo asset groups cannot be split** and devided into separate carrier units! +-- +-- For example, a TPz Fuchs has a cargo bay large enough to carry up to 10 soldiers at once, which is a realistic number. +-- If a group consisting of more than ten soldiers needs to be transported, it cannot be loaded into the APC. +-- Even if two APCs are available, which could in principle carry up to 20 soldiers, a group of, let's say 12 soldiers will not +-- be split into a group of ten soldiers using the first APC and a group two soldiers using the second APC. +-- +-- In other words, **there must be at least one carrier unit available that has a cargo bay large enough to load the heaviest cargo group!** +-- The warehouse logic will automatically search all available transport assets for a large enough carrier. +-- But if none is available, the request will be queued until a suitable carrier becomes available. +-- +-- The only realistic solution in this case is to either provide a transport carrier with a larger cargo bay or to reduce the number of soldiers +-- in the group. +-- +-- A better way would be to have two groups of max. 10 soldiers each and one TPz Fuchs for transport. In this case, the first group is +-- loaded and transported to the receiving warehouse. Once this is done, the carrier will drive back and pick up the remaining +-- group. +-- +-- As an artificial workaround one can manually set the cargo bay size to a larger value or alternatively reduce the weight of the cargo +-- when adding the assets via the @{#WAREHOUSE.AddAsset} function. This might even be unavoidable if, for example, a SAM group +-- should be transported since SAM sites only work when all units are in the same group. +-- -- ## Processing Speed -- -- A warehouse has a limited speed to process requests. Each time the status of the warehouse is updated only one requests is processed. @@ -1160,10 +1195,10 @@ WAREHOUSE = { -- @field #number weight The weight of the whole asset group in kilo gramms. -- @field DCS#Object.Desc DCSdesc All DCS descriptors. -- @field #WAREHOUSE.Attribute attribute Generalized attribute of the group. --- @field #boolean transporter If true, the asset is able to transport troops. -- @field #table cargobay Array of cargo bays of all units in an asset group. -- @field #number cargobaytot Total weight in kg that fits in the cargo bay of all asset group units. -- @field #number cargobaymax Largest cargo bay of all units in the group. +-- @field #number loadradius Distance when cargo is loaded into the carrier. --- Item of the warehouse queue table. -- @type WAREHOUSE.Queueitem @@ -1311,7 +1346,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.5.2" +WAREHOUSE.version="0.5.3" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -1505,20 +1540,22 @@ function WAREHOUSE:New(warehouse, alias) -- @function [parent=#WAREHOUSE] AddAsset -- @param #WAREHOUSE self -- @param Wrapper.Group#GROUP group Group to be added as new asset. - -- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1. + -- @param #number ngroups (Optional) Number of groups to add to the warehouse stock. Default is 1. -- @param #WAREHOUSE.Attribute forceattribute (Optional) Explicitly force a generalized attribute for the asset. This has to be an @{#WAREHOUSE.Attribute}. -- @param #number forcecargobay (Optional) Explicitly force cargobay weight limit in kg for cargo carriers. This is for each *unit* of the group. -- @param #number forceweight (Optional) Explicitly force weight in kg of each unit in the group. + -- @param #number loadradius (Optional) The distance in meters when the cargo is loaded into the carrier. Default is the bounding box size of the carrier. --- Trigger the FSM event "AddAsset" with a delay. Add a group to the warehouse stock. -- @function [parent=#WAREHOUSE] __AddAsset -- @param #WAREHOUSE self -- @param #number delay Delay in seconds. -- @param Wrapper.Group#GROUP group Group to be added as new asset. - -- @param #number ngroups Number of groups to add to the warehouse stock. Default is 1. + -- @param #number ngroups (Optional) Number of groups to add to the warehouse stock. Default is 1. -- @param #WAREHOUSE.Attribute forceattribute (Optional) Explicitly force a generalized attribute for the asset. This has to be an @{#WAREHOUSE.Attribute}. -- @param #number forcecargobay (Optional) Explicitly force cargobay weight limit in kg for cargo carriers. This is for each *unit* of the group. -- @param #number forceweight (Optional) Explicitly force weight in kg of each unit in the group. + -- @param #number loadradius (Optional) The distance in meters when the cargo is loaded into the carrier. Default is the bounding box size of the carrier. --- Triggers the FSM event "NewAsset" when a new asset has been added to the warehouse stock. @@ -2738,7 +2775,8 @@ end -- @param #WAREHOUSE.Attribute forceattribute (Optional) Explicitly force a generalized attribute for the asset. This has to be an @{#WAREHOUSE.Attribute}. -- @param #number forcecargobay (Optional) Explicitly force cargobay weight limit in kg for cargo carriers. This is for each *unit* of the group. -- @param #number forceweight (Optional) Explicitly force weight in kg of each unit in the group. -function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribute, forcecargobay, forceweight) +-- @param #number loadradius (Optional) Radius in meters when the cargo is loaded into the carrier. +function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribute, forcecargobay, forceweight, loadradius) self:T({group=group, ngroups=ngroups, forceattribute=forceattribute, forcecargobay=forcecargobay, forceweight=forceweight}) -- Set default. @@ -2805,7 +2843,7 @@ function WAREHOUSE:onafterAddAsset(From, Event, To, group, ngroups, forceattribu self:_DebugMessage(string.format("Warehouse %s: Adding %d NEW assets of group %s to stock.", self.alias, n, tostring(group:GetName())), 5) -- This is a group that is not in the db yet. Add it n times. - local assets=self:_RegisterAsset(group, n, forceattribute, forcecargobay, forceweight) + local assets=self:_RegisterAsset(group, n, forceattribute, forcecargobay, forceweight, loadradius) -- Add created assets to stock of this warehouse. for _,asset in pairs(assets) do @@ -2837,8 +2875,9 @@ end -- @param #string forceattribute Forced generalized attribute. -- @param #number forcecargobay Cargo bay weight limit in kg. -- @param #number forceweight Weight of units in kg. +-- @param #number loadradius Radius in meters when cargo is loaded into the carrier. -- @return #table A table containing all registered assets. -function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay, forceweight) +function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay, forceweight, loadradius) self:F({groupname=group:GetName(), ngroups=ngroups, forceattribute=forceattribute, forcecargobay=forcecargobay, forceweight=forceweight}) -- Set default. @@ -2931,10 +2970,10 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay, asset.weight=weight asset.DCSdesc=Descriptors asset.attribute=attribute - asset.transporter=false -- not used yet asset.cargobay=cargobay asset.cargobaytot=cargobaytot asset.cargobaymax=cargobaymax + asset.loadradius=loadradius if i==1 then self:_AssetItemInfo(asset) @@ -2967,6 +3006,7 @@ function WAREHOUSE:_AssetItemInfo(asset) text=text..string.format("Weight total = %5.2f kg\n", asset.weight) text=text..string.format("Cargo bay tot = %5.2f kg\n", asset.cargobaytot) text=text..string.format("Cargo bay max = %5.2f kg\n", asset.cargobaymax) + text=text..string.format("Load radius = %s m", tostring(asset.loadradius)) self:T(self.wid..text) self:T({DCSdesc=asset.DCSdesc}) self:T3({Template=asset.template}) @@ -3309,23 +3349,18 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Add groups to cargo if they don't go by themselfs. local CargoGroups --Core.Set#SET_CARGO - -- Load radius and near radius. - local _loadradius=5000 - local _nearradius=nil + -- Board radius, i.e. when the cargo will begin to board the carrier + local _boardradius=5000 if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then - _loadradius=10000 - _nearradius=500 + _boardradius=5000 elseif Request.transporttype==WAREHOUSE.TransportType.HELICOPTER then --_loadradius=1000 - _loadradius=nil + --_boardradius=nil elseif Request.transporttype==WAREHOUSE.TransportType.APC then - _loadradius=nil + --_boardradius=nil end - --_loadradius=nil - --_nearradius=nil - -- Empty cargo group set. CargoGroups = SET_CARGO:New() @@ -3337,15 +3372,14 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Add cargo groups to set. for _i,_group in pairs(_spawngroups:GetSetObjects()) do - -- New cargo group object. - local cargogroup=CARGO_GROUP:New(_group, _cargotype,_group:GetName(),_loadradius,_nearradius) - -- Find asset belonging to this group. - local asset=self:FindAssetInDB(_group) - if asset then - -- Set weight for this group. - cargogroup:SetWeight(asset.weight) - end + local asset=self:FindAssetInDB(_group) + + -- New cargo group object. + local cargogroup=CARGO_GROUP:New(_group, _cargotype,_group:GetName(),_boardradius, 500) --asset.loadradius) + + -- Set weight for this group. + cargogroup:SetWeight(asset.weight) -- Add group to group set. CargoGroups:AddCargo(cargogroup) @@ -3473,7 +3507,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) carrierunit:SetCargoBayWeightLimit(cargobay) -- Debug info. - self:T2(self.wid..string.format("Cargo bay weight limit ofcarrier unit %s: %.1f kg.", carrierunit:GetName(), carrierunit:GetCargoBayFreeWeight())) + self:T2(self.wid..string.format("Cargo bay weight limit of carrier unit %s: %.1f kg.", carrierunit:GetName(), carrierunit:GetCargoBayFreeWeight())) end end @@ -3508,11 +3542,6 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Home zone. CargoTransport:SetHomeZone(self.spawnzone) - --TODO: Need to check/optimize if/how this works with polygon zones! - -- The 20 m inner radius are to ensure that the helo does not land on the warehouse itself in the middle of the default spawn zone. - CargoTransport:SetPickupRadius(self.spawnzone:GetRadius(), 20) - CargoTransport:SetDeployRadius(Request.warehouse.spawnzone:GetRadius(), 20) - elseif Request.transporttype==WAREHOUSE.TransportType.APC then -- Pickup and deploy zones. @@ -3524,15 +3553,27 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Set home zone. CargoTransport:SetHomeZone(self.spawnzone) - - --TODO: Need to check/optimize if/how this works with polygon zones! - -- The 20 m inner radius are to ensure that the helo does not land on the warehouse itself in the middle of the default spawn zone. - CargoTransport:SetPickupRadius(self.spawnzone:GetRadius(), 20) - CargoTransport:SetDeployRadius(Request.warehouse.spawnzone:GetRadius(), 20) else self:E(self.wid.."ERROR: Unknown transporttype!") end + + -- Set pickup and deploy radii. + -- The 20 m inner radius are to ensure that the helo does not land on the warehouse itself in the middle of the default spawn zone. + local pickupouter=200 + local pickupinner=0 + if self.spawnzone.Radius~=nil then + pickupouter=self.spawnzone.Radius + pickupinner=20 + end + local deployouter=200 + local deployinner=0 + if self.spawnzone.Radius~=nil then + deployouter=Request.warehouse.spawnzone.Radius + deployinner=20 + end + CargoTransport:SetPickupRadius(pickupouter, pickupinner) + CargoTransport:SetDeployRadius(deployouter, deployinner) -------------------------------- -- Dispatcher Event Functions -- From 51b6703b58071c41650cb46d7fb6fe166cd1d618 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Tue, 25 Sep 2018 17:05:56 +0200 Subject: [PATCH 402/420] Documentation --- Moose Development/Moose/Core/Point.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index b5d173644..a1bfba1ce 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -2018,7 +2018,7 @@ do -- POINT_VEC3 -- @field #POINT_VEC3.RoutePointAltType RoutePointAltType -- @field #POINT_VEC3.RoutePointType RoutePointType -- @field #POINT_VEC3.RoutePointAction RoutePointAction - -- @extends Core.Point#COORDINATE + -- @extends #COORDINATE --- Defines a 3D point in the simulator and with its methods, you can use or manipulate the point in 3D space. From 98f51116bb6e096d930e0ffbe1c2303f7844ad7c Mon Sep 17 00:00:00 2001 From: FlightControl Date: Wed, 26 Sep 2018 18:01:09 +0200 Subject: [PATCH 403/420] Improved documentation of detection. --- .../Moose/Functional/Detection.lua | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 42f25311a..cfed9c709 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -1864,14 +1864,16 @@ end do -- DETECTION_UNITS + --- @type DETECTION_UNITS + -- @field DCS#Distance DetectionRange The range till which targets are detected. + -- @extends Functional.Detection#DETECTION_BASE + --- Will detect units within the battle zone. -- -- It will build a DetectedItems list filled with DetectedItems. Each DetectedItem will contain a field Set, which contains a @{Core.Set#SET_UNIT} containing ONE @{UNIT} object reference. -- Beware that when the amount of units detected is large, the DetectedItems list will be large also. -- - -- @type DETECTION_UNITS - -- @field DCS#Distance DetectionRange The range till which targets are detected. - -- @extends #DETECTION_BASE + -- @field #DETECTION_UNITS DETECTION_UNITS = { ClassName = "DETECTION_UNITS", DetectionRange = nil, @@ -2117,13 +2119,15 @@ end do -- DETECTION_TYPES + --- @type DETECTION_TYPES + -- @extends Functional.Detection#DETECTION_BASE + --- Will detect units within the battle zone. -- It will build a DetectedItems[] list filled with DetectedItems, grouped by the type of units detected. -- Each DetectedItem will contain a field Set, which contains a @{Core.Set#SET_UNIT} containing ONE @{UNIT} object reference. -- Beware that when the amount of different types detected is large, the DetectedItems[] list will be large also. -- - -- @type DETECTION_TYPES - -- @extends #DETECTION_BASE + -- @field #DETECTION_TYPES DETECTION_TYPES = { ClassName = "DETECTION_TYPES", DetectionRange = nil, @@ -2324,6 +2328,11 @@ end do -- DETECTION_AREAS + --- @type DETECTION_AREAS + -- @field DCS#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. + -- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Wrapper.Unit}s, @{Zone}s, the center @{Wrapper.Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. + -- @extends Functional.Detection#DETECTION_BASE + --- Detect units within the battle zone for a list of @{Wrapper.Group}s detecting targets following (a) detection method(s), -- and will build a list (table) of @{Core.Set#SET_UNIT}s containing the @{Wrapper.Unit#UNIT}s detected. -- The class is group the detected units within zones given a DetectedZoneRange parameter. @@ -2354,10 +2363,7 @@ do -- DETECTION_AREAS -- -- the detected zones when a new detection has taken place. -- - -- @type DETECTION_AREAS - -- @field DCS#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target. - -- @field #DETECTION_BASE.DetectedItems DetectedItems A list of areas containing the set of @{Wrapper.Unit}s, @{Zone}s, the center @{Wrapper.Unit} within the zone, and ID of each area that was detected within a DetectionZoneRange. - -- @extends #DETECTION_BASE + -- @field #DETECTION_AREAS DETECTION_AREAS = { ClassName = "DETECTION_AREAS", DetectionZoneRange = nil, From 1d5ddb3928c3e25fb960d6625ef93d0d73aee19f Mon Sep 17 00:00:00 2001 From: FlightControl Date: Wed, 26 Sep 2018 20:59:21 +0200 Subject: [PATCH 404/420] Fixes documentation --- Moose Development/Moose/Core/Fsm.lua | 2 +- Moose Development/Moose/Tasking/DetectionManager.lua | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index 7469a3726..9ff0b5d16 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -325,7 +325,7 @@ do -- FSM -- -- === -- - -- @field #FSM FSM + -- @field #FSM -- FSM = { ClassName = "FSM", diff --git a/Moose Development/Moose/Tasking/DetectionManager.lua b/Moose Development/Moose/Tasking/DetectionManager.lua index c27511cde..9e4532e39 100644 --- a/Moose Development/Moose/Tasking/DetectionManager.lua +++ b/Moose Development/Moose/Tasking/DetectionManager.lua @@ -43,11 +43,13 @@ do -- DETECTION MANAGER - --- DETECTION_MANAGER class. - -- @type DETECTION_MANAGER + --- @type DETECTION_MANAGER -- @field Core.Set#SET_GROUP SetGroup The groups to which the FAC will report to. -- @field Functional.Detection#DETECTION_BASE Detection The DETECTION_BASE object that is used to report the detected objects. -- @extends Core.Fsm#FSM + + --- DETECTION_MANAGER class. + -- @field #DETECTION_MANAGER DETECTION_MANAGER = { ClassName = "DETECTION_MANAGER", SetGroup = nil, From 3275fd2e5bb81be770eb950927a4c6f7d969f583 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Wed, 26 Sep 2018 21:16:47 +0200 Subject: [PATCH 405/420] Fixed issue with NearRadius. --- Moose Development/Moose/Cargo/CargoUnit.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index f499fb7a8..bb3df8657 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -242,7 +242,7 @@ do -- CARGO_UNIT if not self.CargoInAir then -- If NearRadius is given, then use the given NearRadius, otherwise calculate the NearRadius -- based upon the Carrier bounding radius, which is calculated from the bounding rectangle on the Y axis. - local NearRadius = CargoCarrier:GetBoundingRadius( NearRadius ) + 5 + local NearRadius = NearRadius or CargoCarrier:GetBoundingRadius( NearRadius ) + 5 if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then self:Load( CargoCarrier, NearRadius, ... ) else @@ -293,7 +293,7 @@ do -- CARGO_UNIT if CargoCarrier and CargoCarrier:IsAlive() and self.CargoObject and self.CargoObject:IsAlive() then if (CargoCarrier:IsAir() and not CargoCarrier:InAir()) or true then - local NearRadius = CargoCarrier:GetBoundingRadius( NearRadius ) + 5 + local NearRadius = NearRadius or CargoCarrier:GetBoundingRadius( NearRadius ) + 5 if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then self:__Load( 1, CargoCarrier, ... ) else From a76ea8f393431ba61e592713849f8bddf7657b5d Mon Sep 17 00:00:00 2001 From: Frank Date: Wed, 26 Sep 2018 21:40:18 +0200 Subject: [PATCH 406/420] Stuff --- Moose Development/Moose/AI/AI_Cargo.lua | 2 +- Moose Development/Moose/Cargo/Cargo.lua | 1 + Moose Development/Moose/Cargo/CargoUnit.lua | 2 ++ Moose Development/Moose/Functional/Warehouse.lua | 2 +- 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua index 6251f4e38..b22006944 100644 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -261,7 +261,7 @@ function AI_CARGO:onbeforeLoad( Carrier, From, Event, To, PickupZone ) if Carrier_Weight[CarrierUnit] > CargoWeight then --and CargoBayFreeVolume > CargoVolume then Carrier:RouteStop() --Cargo:Ungroup() - Cargo:__Board( -LoadDelay, CarrierUnit, 25 ) + Cargo:__Board( -LoadDelay, CarrierUnit, 25) LoadDelay = LoadDelay + Cargo:GetCount() * LoadInterval self:__Board( LoadDelay, Cargo, CarrierUnit, PickupZone ) diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 27a40b200..d1a602aed 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -472,6 +472,7 @@ do -- CARGO self.LoadRadius = LoadRadius or 500 self.NearRadius = NearRadius or 25 + env.info("FF nearradius = ".. self.NearRadius) self:SetDeployed( false ) diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index 474d3f90d..26b1a3b4d 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -226,6 +226,8 @@ do -- CARGO_UNIT -- @param #string Event -- @param #string From -- @param #string To + -- @param Wrapper.Group#GROUP CargoCarrier + -- @param #number NearRadius function CARGO_UNIT:onafterBoard( From, Event, To, CargoCarrier, NearRadius, ... ) self:F( { From, Event, To, CargoCarrier, NearRadius } ) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index f7d7a4242..a7c15e897 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -3350,7 +3350,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) local CargoGroups --Core.Set#SET_CARGO -- Board radius, i.e. when the cargo will begin to board the carrier - local _boardradius=5000 + local _boardradius=500 if Request.transporttype==WAREHOUSE.TransportType.AIRPLANE then _boardradius=5000 From 2dc7ca0cb26a4980ca71894e2944da38d83a7219 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Wed, 26 Sep 2018 22:14:59 +0200 Subject: [PATCH 407/420] Fixing nearrange bug and command center tasking auto assignment optimization. Moved auto assignment task menu to the command center group menu structure. I hope it will work ... --- Moose Development/Moose/AI/AI_Cargo.lua | 2 +- .../Moose/Tasking/CommandCenter.lua | 80 +++++++++++++------ 2 files changed, 56 insertions(+), 26 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo.lua b/Moose Development/Moose/AI/AI_Cargo.lua index 6251f4e38..a1649d7a8 100644 --- a/Moose Development/Moose/AI/AI_Cargo.lua +++ b/Moose Development/Moose/AI/AI_Cargo.lua @@ -261,7 +261,7 @@ function AI_CARGO:onbeforeLoad( Carrier, From, Event, To, PickupZone ) if Carrier_Weight[CarrierUnit] > CargoWeight then --and CargoBayFreeVolume > CargoVolume then Carrier:RouteStop() --Cargo:Ungroup() - Cargo:__Board( -LoadDelay, CarrierUnit, 25 ) + Cargo:__Board( -LoadDelay, CarrierUnit ) LoadDelay = LoadDelay + Cargo:GetCount() * LoadInterval self:__Board( LoadDelay, Cargo, CarrierUnit, PickupZone ) diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index ce0ab6ce5..3c2825021 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -270,6 +270,8 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName ) self:SetMenu() _SETTINGS:SetSystemMenu( CommandCenterPositionable ) + + self:SetCommandMenu() return self end @@ -440,10 +442,7 @@ function COMMANDCENTER:GetMenu( TaskGroup ) self.CommandCenterMenus[TaskGroup] = CommandCenterMenu if self.AutoAssignTasks == false then - local AutoAssignTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Assign Task On", CommandCenterMenu, self.SetAutoAssignTasks, self, true ):SetTime(MenuTime):SetTag("AutoTask") local AssignTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Assign Task", CommandCenterMenu, self.AssignRandomTask, self, TaskGroup ):SetTime(MenuTime):SetTag("AutoTask") - else - local AutoAssignTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, "Assign Task Off", CommandCenterMenu, self.SetAutoAssignTasks, self, false ):SetTime(MenuTime):SetTag("AutoTask") end CommandCenterMenu:Remove( MenuTime, "AutoTask" ) @@ -475,10 +474,32 @@ function COMMANDCENTER:AssignRandomTask( TaskGroup ) end +--- Sets the menu of the command center. +-- This command is called within the :New() method. +-- @param #COMMANDCENTER self +function COMMANDCENTER:SetCommandMenu() + + local MenuTime = timer.getTime() + + if self.CommandCenterPositionable and self.CommandCenterPositionable:IsInstanceOf(GROUP) then + local CommandCenterText = self:GetText() + local CommandCenterMenu = MENU_GROUP:New( self.CommandCenterPositionable, CommandCenterText ):SetTime(MenuTime) + + if self.AutoAssignTasks == false then + local AutoAssignTaskMenu = MENU_GROUP_COMMAND:New( self.CommandCenterPositionable, "Assign Task On", CommandCenterMenu, self.SetAutoAssignTasks, self, true ):SetTime(MenuTime):SetTag("AutoTask") + else + local AutoAssignTaskMenu = MENU_GROUP_COMMAND:New( self.CommandCenterPositionable, "Assign Task Off", CommandCenterMenu, self.SetAutoAssignTasks, self, false ):SetTime(MenuTime):SetTag("AutoTask") + end + CommandCenterMenu:Remove( MenuTime, "AutoTask" ) + end + +end + + + --- Automatically assigns tasks to all TaskGroups. -- @param #COMMANDCENTER self -- @param #boolean AutoAssign true for ON and false or nil for OFF. --- @return #COMMANDCENTER function COMMANDCENTER:SetAutoAssignTasks( AutoAssign ) self.AutoAssignTasks = AutoAssign or false @@ -495,6 +516,8 @@ function COMMANDCENTER:SetAutoAssignTasks( AutoAssign ) else self:ScheduleStop( self.AssignTasks ) end + + self:SetCommandCenterMenu() end @@ -517,12 +540,13 @@ function COMMANDCENTER:AssignTasks() end end end + end --- Get all the Groups active within the command center. -- @param #COMMANDCENTER self --- @return Core.Set#SET_GROUP +-- @return Core.Set#SET_GROUP The set of groups active within the command center. function COMMANDCENTER:AddGroups() local GroupSet = SET_GROUP:New() @@ -538,7 +562,7 @@ end --- Checks of the TaskGroup has a Task. -- @param #COMMANDCENTER self --- @return #boolean +-- @return #boolean When true, the TaskGroup has a Task, otherwise the returned value will be false. function COMMANDCENTER:IsGroupAssigned( TaskGroup ) local Assigned = false @@ -555,9 +579,9 @@ function COMMANDCENTER:IsGroupAssigned( TaskGroup ) end ---- Checks of the COMMANDCENTER has a GROUP. +--- Checks of the command center has the given MissionGroup. -- @param #COMMANDCENTER self --- @param Wrapper.Group#GROUP +-- @param Wrapper.Group#GROUP MissionGroup The group active within one of the missions governed by the command center. -- @return #boolean function COMMANDCENTER:HasGroup( MissionGroup ) @@ -574,37 +598,39 @@ function COMMANDCENTER:HasGroup( MissionGroup ) return Has end ---- Send a CC message to the coalition of the CC. +--- Let the command center send a Message to all players. -- @param #COMMANDCENTER self +-- @param #string Message The message text. function COMMANDCENTER:MessageToAll( Message ) self:GetPositionable():MessageToAll( Message, 20, self:GetName() ) end ---- Send a CC message to a GROUP. +--- Let the command center send a message to the MessageGroup. -- @param #COMMANDCENTER self --- @param #string Message --- @param Wrapper.Group#GROUP TaskGroup -function COMMANDCENTER:MessageToGroup( Message, TaskGroup ) +-- @param #string Message The message text. +-- @param Wrapper.Group#GROUP MessageGroup The group to receive the message. +function COMMANDCENTER:MessageToGroup( Message, MessageGroup ) - self:GetPositionable():MessageToGroup( Message, 15, TaskGroup, self:GetShortText() ) + self:GetPositionable():MessageToGroup( Message, 15, MessageGroup, self:GetShortText() ) end ---- Send a CC message of a specified type to a GROUP. +--- Let the command center send a message to the MessageGroup. -- @param #COMMANDCENTER self --- @param #string Message --- @param Wrapper.Group#GROUP TaskGroup +-- @param #string Message The message text. +-- @param Wrapper.Group#GROUP MessageGroup The group to receive the message. -- @param Core.Message#MESSAGE.MessageType MessageType The type of the message, resulting in automatic time duration and prefix of the message. -function COMMANDCENTER:MessageTypeToGroup( Message, TaskGroup, MessageType ) +function COMMANDCENTER:MessageTypeToGroup( Message, MessageGroup, MessageType ) - self:GetPositionable():MessageTypeToGroup( Message, MessageType, TaskGroup, self:GetShortText() ) + self:GetPositionable():MessageTypeToGroup( Message, MessageType, MessageGroup, self:GetShortText() ) end ---- Send a CC message to the coalition of the CC. +--- Let the command center send a message to the coalition of the command center. -- @param #COMMANDCENTER self +-- @param #string Message The message text. function COMMANDCENTER:MessageToCoalition( Message ) local CCCoalition = self:GetPositionable():GetCoalition() @@ -615,9 +641,9 @@ function COMMANDCENTER:MessageToCoalition( Message ) end ---- Send a CC message of a specified type to the coalition of the CC. +--- Let the command center send a message of a specified type to the coalition of the command center. -- @param #COMMANDCENTER self --- @param #string Message The message. +-- @param #string Message The message text. -- @param Core.Message#MESSAGE.MessageType MessageType The type of the message, resulting in automatic time duration and prefix of the message. function COMMANDCENTER:MessageTypeToCoalition( Message, MessageType ) @@ -629,9 +655,10 @@ function COMMANDCENTER:MessageTypeToCoalition( Message, MessageType ) end ---- Report the status of all MISSIONs to a GROUP. +--- Let the command center send a report of the status of all missions to a group. -- Each Mission is listed, with an indication how many Tasks are still to be completed. -- @param #COMMANDCENTER self +-- @param Wrapper.Group#GROUP ReportGroup The group to receive the report. function COMMANDCENTER:ReportSummary( ReportGroup ) self:F( ReportGroup ) @@ -649,9 +676,10 @@ function COMMANDCENTER:ReportSummary( ReportGroup ) self:MessageToGroup( Report:Text(), ReportGroup ) end ---- Report the players of all MISSIONs to a GROUP. +--- Let the command center send a report of the players of all missions to a group. -- Each Mission is listed, with an indication how many Tasks are still to be completed. -- @param #COMMANDCENTER self +-- @param Wrapper.Group#GROUP ReportGroup The group to receive the report. function COMMANDCENTER:ReportMissionsPlayers( ReportGroup ) self:F( ReportGroup ) @@ -667,9 +695,11 @@ function COMMANDCENTER:ReportMissionsPlayers( ReportGroup ) self:MessageToGroup( Report:Text(), ReportGroup ) end ---- Report the status of a Task to a Group. +--- Let the command center send a report of the status of a task to a group. -- Report the details of a Mission, listing the Mission, and all the Task details. -- @param #COMMANDCENTER self +-- @param Wrapper.Group#GROUP ReportGroup The group to receive the report. +-- @param Tasking.Task#TASK Task The task to be reported. function COMMANDCENTER:ReportDetails( ReportGroup, Task ) self:F( ReportGroup ) From 4625bc73a9fa2eeae3a0fc3d18d8e94aaf63904f Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Thu, 27 Sep 2018 16:21:29 +0200 Subject: [PATCH 408/420] Warehouse v0.5.3w --- Moose Development/Moose/Functional/Range.lua | 2 +- .../Moose/Functional/Warehouse.lua | 37 ++++++++++++++++++- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Functional/Range.lua b/Moose Development/Moose/Functional/Range.lua index e7c015056..6b45a2fb5 100644 --- a/Moose Development/Moose/Functional/Range.lua +++ b/Moose Development/Moose/Functional/Range.lua @@ -104,7 +104,7 @@ -- ## Strafe Pits -- Each strafe pit can consist of multiple targets. Often one findes two or three strafe targets next to each other. -- --- A strafe pit can be added to the range by the @{#RANGE.AddStrafepit}(*targetnames, boxlength, boxwidth, heading, inverseheading, goodpass, foulline*) function. +-- A strafe pit can be added to the range by the @{#RANGE.AddStrafePit}(*targetnames, boxlength, boxwidth, heading, inverseheading, goodpass, foulline*) function. -- -- * The first parameter *targetnames* defines the target or targets. This has to be given as a lua table which contains the names of @{Wrapper.Unit} or @{Static} objects defined in the mission editor. -- * In order to perform a valid pass on the strafe pit, the pilot has to begin his run from the correct direction. Therefore, an "approach box" is defined in front diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index a7c15e897..edfa60407 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -311,7 +311,7 @@ -- The parameter *group* is a late activated template group. The waypoints of this group are used to define the path between the two warehouses. -- By default, the reverse paths is automatically added to get *from* the remote warehouse to this warehouse unless the parameter *oneway* is set to *true*. -- --- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_Off-RoadPaths.png) +-- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_Off-Road_Paths.png) -- -- **Note** that if an off road connection is defined between two warehouses this becomes the default path, i.e. even if there is a path *on road* possible -- this will not be used. @@ -492,6 +492,39 @@ -- So the mission designer can intervene at this point and for example choose to spawn all or paricular types of assets before the warehouse is gone for good. -- -- === +-- +-- # Hook in and Take Control +-- +-- The Finite State Machine implementation allows mission designers to hook into important events and add their own code. +-- Most of these events have already been mentioned but here is the list at a glance: +-- +-- * "NotReadyYet" --> "Start" --> "Running" (Starting the warehouse) +-- * "*" --> "Status" --> "*" (status updated in regular intervals) +-- * "*" --> "AddAsset" --> "*" (adding a new asset to the warehouse stock) +-- * "*" --> "NewAsset" --> "*" (a new asset has been added to the warehouse stock) +-- * "*" --> "AddRequest" --> "*" (adding a request for the warehouse assets) +-- * "Running" --> "Request" --> "*" (a request is processed when the warehouse is running) +-- * "Attacked" --> "Request" --> "*" (a request is processed when the warehouse is attacked) +-- * "*" --> "Arrived" --> "*" (asset group has arrived at its destination) +-- * "*" --> "Delivered" --> "*" (all assets of a request have been delivered) +-- * "Running" --> "SelfRequest" --> "*" (warehouse is requesting asset from itself when running) +-- * "Attacked" --> "SelfRequest" --> "*" (warehouse is requesting asset from itself while under attack) +-- * "*" --> "Attacked" --> "Attacked" (warehouse is being attacked) +-- * "Attacked" --> "Defeated" --> "Running" (an attack was defeated) +-- * "Attacked" --> "Captured" --> "Running" (warehouse was captured by the enemy) +-- * "*" --> "AirbaseCaptured" --> "*" (airbase belonging to the warehouse was captured by the enemy) +-- * "*" --> "AirbaseRecaptured" --> "*" (airbase was re-captured) +-- * "*" --> "Destroyed" --> "Destroyed" (warehouse was destroyed) +-- * "Running" --> "Pause" --> "Paused" (warehouse is paused) +-- * "Paused" --> "Unpause" --> "Running" (warehouse is unpaused) +-- * "*" --> "Stop" --> "Stopped" (warehouse is stopped) +-- +-- The transitions are of the general form "From State" --> "Event" --> "To State". The "*" star denotes that the transition is possible from *any* state. +-- Some transitions, however, are only allowed from certain "From States". For example, no requests can be processed if the warehouse is in "Paused" or "Destroyed" or "Stopped" state. +-- +-- Mission designers can capture the events with OnAfterEvent functions, e.g. @{#WAREHOUSE.OnAfterDelivered} or @{#WAREHOUSE.OnAfterAirbaseCaptured}. +-- +-- === -- -- # Examples -- @@ -1346,7 +1379,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.5.3" +WAREHOUSE.version="0.5.3w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. From 5becf001baee9b67117cfad397c13e73086d2006 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Thu, 27 Sep 2018 22:08:08 +0200 Subject: [PATCH 409/420] -- Fixed NearRadius. -- Some documentation errors. --- Moose Development/Moose/Cargo/Cargo.lua | 2 +- Moose Development/Moose/Cargo/CargoCrate.lua | 2 ++ Moose Development/Moose/Cargo/CargoGroup.lua | 8 ++++---- .../Moose/Cargo/CargoSlingload.lua | 2 ++ Moose Development/Moose/Cargo/CargoUnit.lua | 20 +++++++------------ Moose Development/Moose/Core/Fsm.lua | 3 +-- .../Moose/Tasking/Task_CARGO.lua | 2 +- .../Moose/Wrapper/Controllable.lua | 2 +- 8 files changed, 19 insertions(+), 22 deletions(-) diff --git a/Moose Development/Moose/Cargo/Cargo.lua b/Moose Development/Moose/Cargo/Cargo.lua index 27a40b200..e393b9e9d 100644 --- a/Moose Development/Moose/Cargo/Cargo.lua +++ b/Moose Development/Moose/Cargo/Cargo.lua @@ -471,7 +471,7 @@ do -- CARGO self.CargoLimit = 0 self.LoadRadius = LoadRadius or 500 - self.NearRadius = NearRadius or 25 + --self.NearRadius = NearRadius or 25 self:SetDeployed( false ) diff --git a/Moose Development/Moose/Cargo/CargoCrate.lua b/Moose Development/Moose/Cargo/CargoCrate.lua index 92fccec02..50cef9a97 100644 --- a/Moose Development/Moose/Cargo/CargoCrate.lua +++ b/Moose Development/Moose/Cargo/CargoCrate.lua @@ -67,6 +67,8 @@ do -- CARGO_CRATE self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead ) self:SetEventPriority( 4 ) + + self.NearRadius = NearRadius or 25 return self end diff --git a/Moose Development/Moose/Cargo/CargoGroup.lua b/Moose Development/Moose/Cargo/CargoGroup.lua index 218cb7c93..0203bd6ec 100644 --- a/Moose Development/Moose/Cargo/CargoGroup.lua +++ b/Moose Development/Moose/Cargo/CargoGroup.lua @@ -64,6 +64,8 @@ do -- CARGO_GROUP self.Grouped = true self.CargoUnitTemplate = {} + self.NearRadius = NearRadius + self:SetDeployed( false ) local WeightGroup = 0 @@ -297,9 +299,9 @@ do -- CARGO_GROUP -- @param Wrapper.Unit#UNIT CargoCarrier -- @param #number NearRadius If distance is smaller than this number, cargo is loaded into the carrier. function CARGO_GROUP:onenterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) - --self:F( { CargoCarrier.UnitName, From, Event, To } ) + self:F( { CargoCarrier.UnitName, From, Event, To, NearRadius = NearRadius } ) - local NearRadius = NearRadius or 25 + NearRadius = NearRadius or self.NearRadius if From == "UnLoaded" then @@ -347,8 +349,6 @@ do -- CARGO_GROUP function CARGO_GROUP:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) --self:F( { CargoCarrier.UnitName, From, Event, To } ) - local NearRadius = NearRadius or 100 - local Boarded = true local Cancelled = false local Dead = true diff --git a/Moose Development/Moose/Cargo/CargoSlingload.lua b/Moose Development/Moose/Cargo/CargoSlingload.lua index a3831dcf6..8af9d5304 100644 --- a/Moose Development/Moose/Cargo/CargoSlingload.lua +++ b/Moose Development/Moose/Cargo/CargoSlingload.lua @@ -60,6 +60,8 @@ do -- CARGO_SLINGLOAD self:HandleEvent( EVENTS.PlayerLeaveUnit, self.OnEventCargoDead ) self:SetEventPriority( 4 ) + + self.NearRadius = NearRadius or 25 return self end diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index bb3df8657..c0a6a3147 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -69,8 +69,6 @@ do -- CARGO_UNIT function CARGO_UNIT:onenterUnBoarding( From, Event, To, ToPointVec2, NearRadius ) self:F( { From, Event, To, ToPointVec2, NearRadius } ) - NearRadius = NearRadius or 25 - local Angle = 180 local Speed = 60 local DeployDistance = 9 @@ -139,8 +137,6 @@ do -- CARGO_UNIT function CARGO_UNIT:onleaveUnBoarding( From, Event, To, ToPointVec2, NearRadius ) self:F( { From, Event, To, ToPointVec2, NearRadius } ) - NearRadius = NearRadius or 100 - local Angle = 180 local Speed = 10 local Distance = 5 @@ -167,8 +163,6 @@ do -- CARGO_UNIT function CARGO_UNIT:onafterUnBoarding( From, Event, To, ToPointVec2, NearRadius ) self:F( { From, Event, To, ToPointVec2, NearRadius } ) - NearRadius = NearRadius or 100 - self.CargoInAir = self.CargoObject:InAir() self:T( self.CargoInAir ) @@ -227,7 +221,7 @@ do -- CARGO_UNIT -- @param #string From -- @param #string To function CARGO_UNIT:onafterBoard( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { From, Event, To, CargoCarrier, NearRadius } ) + self:F( { From, Event, To, CargoCarrier, NearRadius = NearRadius } ) self.CargoInAir = self.CargoObject:InAir() @@ -271,7 +265,7 @@ do -- CARGO_UNIT local TaskRoute = self.CargoObject:TaskRoute( Points ) self.CargoObject:SetTask( TaskRoute, 2 ) - self:__Boarding( -5, CargoCarrier, NearRadius ) + self:__Boarding( -5, CargoCarrier, NearRadius, ... ) self.RunCount = 0 end end @@ -288,7 +282,7 @@ do -- CARGO_UNIT -- @param Wrapper.Client#CLIENT CargoCarrier -- @param #number NearRadius Default 25 m. function CARGO_UNIT:onafterBoarding( From, Event, To, CargoCarrier, NearRadius, ... ) - self:F( { From, Event, To, CargoCarrier:GetName() } ) + self:F( { From, Event, To, CargoCarrier:GetName(), NearRadius = NearRadius } ) if CargoCarrier and CargoCarrier:IsAlive() and self.CargoObject and self.CargoObject:IsAlive() then @@ -298,11 +292,11 @@ do -- CARGO_UNIT self:__Load( 1, CargoCarrier, ... ) else if self:IsNear( CargoCarrier:GetPointVec2(), 20 ) then - self:__Boarding( -2, CargoCarrier, NearRadius, ... ) - self.RunCount = self.RunCount + 2 + self:__Boarding( -1, CargoCarrier, NearRadius, ... ) + self.RunCount = self.RunCount + 1 else - self:__Boarding( -10, CargoCarrier, NearRadius, ... ) - self.RunCount = self.RunCount + 10 + self:__Boarding( -5, CargoCarrier, NearRadius, ... ) + self.RunCount = self.RunCount + 5 end if self.RunCount >= 40 then self.RunCount = 0 diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index 9ff0b5d16..65b0d3ea5 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -805,8 +805,7 @@ do -- FSM_CONTROLLABLE -- -- === -- - -- @field #FSM_CONTROLLABLE FSM_CONTROLLABLE - -- + -- @field #FSM_CONTROLLABLE FSM_CONTROLLABLE = { ClassName = "FSM_CONTROLLABLE", } diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 235f69fe2..00de26c1b 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -1017,7 +1017,7 @@ do -- TASK_CARGO else Cargo:MessageToGroup( "Boarding ...", TaskUnit:GetGroup() ) if not Cargo:IsBoarding() then - Cargo:Board( TaskUnit, 20, self ) + Cargo:Board( TaskUnit, nil, self ) end end else diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 4c725c4cb..ad789a5a6 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -13,9 +13,9 @@ --- @type CONTROLLABLE --- @extends Wrapper.Positionable#POSITIONABLE -- @field DCS#Controllable DCSControllable The DCS controllable class. -- @field #string ControllableName The name of the controllable. +-- @extends Wrapper.Positionable#POSITIONABLE From 9bfbfcfd6ae62ec6ab47ee3643229336e8043326 Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 27 Sep 2018 23:44:04 +0200 Subject: [PATCH 410/420] stuff --- Moose Development/Moose/Cargo/CargoUnit.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index c84250aba..3c9e219a6 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -245,7 +245,7 @@ do -- CARGO_UNIT -- If NearRadius is given, then use the given NearRadius, otherwise calculate the NearRadius -- based upon the Carrier bounding radius, which is calculated from the bounding rectangle on the Y axis. env.info(string.format("FF nearradius = %d (before)", NearRadius)) - local NearRadius = CargoCarrier:GetBoundingRadius( NearRadius ) + 5 + local NearRadius = CargoCarrier:GetBoundingRadius( NearRadius or self.NearRadius) + 5 env.info(string.format("FF nearradius = %d isnear=%s", NearRadius, tostring(self:IsNear( CargoCarrier:GetPointVec2(), NearRadius )) )) if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then self:Load( CargoCarrier, NearRadius, ... ) From 9637d0e5458a4c396eab88a6b92eaebe9048c23e Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 27 Sep 2018 23:56:02 +0200 Subject: [PATCH 411/420] Warehouse 0.5.4 --- Moose Development/Moose/Cargo/CargoUnit.lua | 2 +- Moose Development/Moose/Functional/Warehouse.lua | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index 50e031f7b..aeeece955 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -239,7 +239,7 @@ do -- CARGO_UNIT -- If NearRadius is given, then use the given NearRadius, otherwise calculate the NearRadius -- based upon the Carrier bounding radius, which is calculated from the bounding rectangle on the Y axis. env.info(string.format("FF nearradius = %d (before)", NearRadius)) - local NearRadius = CargoCarrier:GetBoundingRadius( NearRadius or self.NearRadius) + 5 + local NearRadius = CargoCarrier:GetBoundingRadius( NearRadius ) + 5 env.info(string.format("FF nearradius = %d isnear=%s", NearRadius, tostring(self:IsNear( CargoCarrier:GetPointVec2(), NearRadius )) )) if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then self:Load( CargoCarrier, NearRadius, ... ) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index edfa60407..c17e810d1 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -1379,7 +1379,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.5.3w" +WAREHOUSE.version="0.5.4" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -3409,7 +3409,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) local asset=self:FindAssetInDB(_group) -- New cargo group object. - local cargogroup=CARGO_GROUP:New(_group, _cargotype,_group:GetName(),_boardradius, 500) --asset.loadradius) + local cargogroup=CARGO_GROUP:New(_group, _cargotype,_group:GetName(),_boardradius, asset.loadradius) -- Set weight for this group. cargogroup:SetWeight(asset.weight) From ffee1b119b735ce21d7161e929defb404eb52bcf Mon Sep 17 00:00:00 2001 From: funkyfranky Date: Fri, 28 Sep 2018 16:23:17 +0200 Subject: [PATCH 412/420] Warehouse v0.5.4w --- .../Moose/Functional/Warehouse.lua | 193 +++++++++++++----- 1 file changed, 142 insertions(+), 51 deletions(-) diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index c17e810d1..9287439a3 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -1,4 +1,4 @@ ---- **Functional** - (R2.5) - Simulation of logistic operations. +--- **Functional** - (R2.4) - Simulation of logistic operations. -- -- The MOOSE warehouse concept simulates the organization and implementation of complex operations regarding the flow of assets between the point of origin and the point of consumption -- in order to meet requirements of a potential conflict. In particular, this class is concerned with maintaining army supply lines while disrupting those of the enemy, since an armed @@ -85,7 +85,7 @@ -- -- Any kind of ground, airborne or naval asset can be stored and are spawned upon request. -- The fact that the assets live only virtually in stock and are put into the game only when needed has a positive impact on the game performance. --- It also alliviates the problem of limited parking spots at smaller air bases +-- It also alliviates the problem of limited parking spots at smaller airbases. -- -- ## What means of transportation are available? -- @@ -112,7 +112,7 @@ -- -- # Creating a Warehouse -- --- A MOOSE warehouse must be represented in game by a phyical *static* object. For example, the mission editor already has warehouse as static object available. +-- A MOOSE warehouse must be represented in game by a physical *static* object. For example, the mission editor already has warehouse as static object available. -- This would be a good first choice but any static object will do. -- -- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_Static.png) @@ -125,11 +125,11 @@ -- Once the static warehouse object is placed in the mission editor it can be used as a MOOSE warehouse by the @{#WAREHOUSE.New}(*warehousestatic*, *alias*) constructor, -- like for example: -- --- warehouseBatumi=WAREHOUSE:New(STATIC:FindByName("Warehouse Batumi"), "My Warehouse Alias") +-- warehouseBatumi=WAREHOUSE:New(STATIC:FindByName("Warehouse Batumi"), "My optional Warehouse Alias") -- warehouseBatumi:Start() -- -- The first parameter *warehousestatic* is the static MOOSE object. By default, the name of the warehouse will be the same as the name given to the static object. --- The second parameter *alias* can be used to choose a more convenient name if desired. This will be the name the warehouse calls itself when reporting messages. +-- The second parameter *alias* is optional and can be used to choose a more convenient name if desired. This will be the name the warehouse calls itself when reporting messages. -- -- Note that a warehouse also needs to be started in order to be in service. This is done with the @{#WAREHOUSE.Start}() or @{#WAREHOUSE.__Start}(*delay*) functions. -- The warehouse is now fully operational and requests are being processed. @@ -137,14 +137,18 @@ -- # Adding Assets -- -- Assets can be added to the warehouse stock by using the @{#WAREHOUSE.AddAsset}(*group*, *ngroups*, *forceattribute*, *forcecargobay*, *forceweight*, *loadradius*) function. --- The parameter *group* has to be a MOOSE @{Wrapper.Group#GROUP}. The parameter *ngroups* specifies how many clones of this group are added to the stock. +-- The parameter *group* has to be a MOOSE @{Wrapper.Group#GROUP}. This is also the only mandatory parameters. All other parameters are optional and can be used for fine tuning if +-- nessary. The parameter *ngroups* specifies how many clones of this group are added to the stock. -- -- infrantry=GROUP:FindByName("Some Infantry Group") -- warehouseBatumi:AddAsset(infantry, 5) -- --- This will add five infantry groups to the warehouse stock. Note that the group will normally be a late activated template group, +-- This will add five infantry groups to the warehouse stock. Note that the group should normally be a late activated template group, -- which was defined in the mission editor. But you can also add other groups which are already spawned and present in the mission. -- +-- Also note that the coalition of the template group (red, blue or neutral) does not matter. The coalition of the assets is determined by the coalition of the warehouse owner. +-- In other words, it is no problem to add red groups to blue warehouses and vice versa. The assets will automatically have the coalition of the warehouse. +-- -- You can add assets with a delay by using the @{#WAREHOUSE.__AddAsset}(*delay*, *group*, *ngroups*, *foceattribute*, *forcecargobay*, *forceweight*, *loadradius*), where *delay* -- is the delay in seconds before the asset is added. -- @@ -152,7 +156,7 @@ -- -- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_Stock-Marker.png) -- --- ## Options for Fine Tuning +-- ## Optional Parameters for Fine Tuning -- -- By default, the generalized attribute of the asset is determined automatically from the DCS descriptor attributes. However, this might not always result in the desired outcome. -- Therefore, it is possible, to force a generalized attribute for the asset with the third optional parameter *forceattribute*, which is of type @{#WAREHOUSE.Attribute}. @@ -162,19 +166,44 @@ -- manually when the asset is added -- -- warehouseBatumi:AddAsset("Huey", 5, WAREHOUSE.Attribute.AIR_TRANSPORTHELO) +-- +-- This becomes important when assets are requested from other warehouses as described below. In this case, the five Hueys are now marked as transport helicopters and +-- not attack helicopters. -- -- ### Setting the Cargo Bay Weight Limit --- You can also ajust the cargo bay weight limit, in case it is not calculated correctly automatically. For example, the cargo bay of a C-17A is much smaller in DCS than that of a C-130, which is +-- You can ajust the cargo bay weight limit, in case it is not calculated correctly automatically. For example, the cargo bay of a C-17A is much smaller in DCS than that of a C-130, which is -- unrealistic. This can be corrected by the *forcecargobay* parmeter which is here set to 77,000 kg -- -- warehouseBatumi:AddAsset("C-17A", nil, nil, 77000) -- +-- The size of the cargo bay is only important when the group is used as transport carrier for other assets. +-- -- ### Setting the Weight --- In the current version of DCS a mortar unit has a weight of 5 tons. This confuses the transporter logic, because it appears to be too have for, e.g. all APCs. You can manually adjust the weight --- by the *forceweight* parameter and set it to 210 kg for each unit in the group +-- If an asset shall be transported by a carrier it important to note that - as in real life - a carrier can only carry cargo up to a certain weight. The weight of the +-- units is automatically determined from the DCS descriptor table. +-- However, in the current DCS version (2.5.3) a mortar unit has a weight of 5 tons. This confuses the transporter logic, because it appears to be too have for, e.g. all APCs. +-- +-- As a workaround, you can manually adjust the weight by the optional *forceweight* parameter: -- -- warehouseBatumi:AddAsset("Mortar Alpha", nil, nil, nil, 210) -- +-- In this case we set it to 210 kg. Note, the weight value set is meant for *each* unit in the group. Therefore, a group consisting of three mortars will have a total weight +-- of 630 kg. This is important as groups cannot be split between carrier units when transporting, i.e. the total weight of the whole group must be smaller than the +-- cargo bay of the transport carrier. +-- +-- ### Setting the Load Radius +-- Boading and loading of cargo into a carrier is modeled in a realistic fashion in the AI\_CARGO\DISPATCHER classes, which are used inernally by the WAREHOUSE class. +-- Meaning that troops (cargo) will board, i.e. run or drive to the carrier, and only once they are in close proximity to the transporter they will be loaded (disappear). +-- +-- Unfortunately, there are some situations where problems can occur. For example, in DCS tanks have the strong tentendcy not to drive around obstacles but rather to roll over them. +-- I have seen cases where an aircraft of the same coalition as the tank was in its way and the tank drove right through the plane waiting on a parking spot and destroying it. +-- +-- As a workaround it is possible to set a larger load radius so that the cargo units are despawned further away from the carrier via the optional **loadradius** parameter: +-- +-- warehouseBatumi:AddAsset("Leopard 2", nil, nil, nil, nil, 250) +-- +-- Adding the asset like this will cause the units to be loaded into the carrier already at a distance of 250 meters. +-- -- === -- -- # Requesting Assets @@ -196,6 +225,9 @@ -- -- ## Requesting by Generalized Attribute -- +-- Generalized attributes are similar to [DCS attributes](https://wiki.hoggitworld.com/view/DCS_enum_attributes). However, they are a bit more general and +-- an asset can only have one generalized attribute by which it is characterized. +-- -- For example: -- -- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 5, WAREHOUSE.TransportType.APC, 2) @@ -205,9 +237,38 @@ -- If either to few infantry or APC groups are available when the request is made, the request is held in the warehouse queue until enough cargo and -- transport assets are available. -- --- Also note that the above request is for five infantry groups. So any group in stock that has the generalized attribute "INFANTRY" can be selected. --- +-- Also note that the above request is for five infantry groups. So any group in stock that has the generalized attribute "GROUND_INFANTRY" can be selected for the request. -- +-- ### Generalized Attributes +-- +-- Currently implemented are: +-- +-- * @{#WAREHOUSE.Attribute.AIR_TRANSPORTPLANE} Airplane with transport capability. This can be used to transport other assets. +-- * @{#WAREHOUSE.Attribute.AIR_AWACS} Airborne Early Warning and Control System. +-- * @{#WAREHOUSE.Attribute.AIR_FIGHTER} Fighter, interceptor, ... airplane. +-- * @{#WAREHOUSE.Attribute.AIR_BOMBER} Aircraft which can be used for strategic bombing. +-- * @{#WAREHOUSE.Attribute.AIR_TANKER} Airplane which can refuel other aircraft. +-- * @{#WAREHOUSE.Attribute.AIR_TRANSPORTHELO} Helicopter with transport capability. This can be used to transport other assets. +-- * @{#WAREHOUSE.Attribute.AIR_ATTACKHELO} Attack helicopter. +-- * @{#WAREHOUSE.Attribute.AIR_UAV} Unpiloted Aerial Vehicle, e.g. drones. +-- * @{#WAREHOUSE.Attribute.AIR_OTHER} Any airborne unit that does not fall into any other airborne category. +-- * @{#WAREHOUSE.Attribute.GROUND_APC} Infantry carriers, in particular Amoured Personell Carrier. This can be used to transport other assets. +-- * @{#WAREHOUSE.Attribute.GROUND_TRUCK} Unarmed ground vehicles, which has the DCS "Truck" attribute. +-- * @{#WAREHOUSE.Attribute.GROUND_INFANTRY} Ground infantry assets. +-- * @{#WAREHOUSE.Attribute.GROUND_ARTILLERY} Artillery assets. +-- * @{#WAREHOUSE.Attribute.GROUND_TANK} Tanks (modern or old). +-- * @{#WAREHOUSE.Attribute.GROUND_TRAIN} Trains. Not that trains are **not** yet properly implemented in DCS and cannot be used currently. +-- * @{#WAREHOUSE.Attribute.GROUND_EWR} Early Warning Radar. +-- * @{#WAREHOUSE.Attribute.GROUND_AAA} Anti-Aircraft Artillery. +-- * @{#WAREHOUSE.Attribute.GROUND_SAM} Surface-to-Air Missile system or components. +-- * @{#WAREHOUSE.Attribute.GROUND_OTHER} Any ground unit that does not fall into any other ground category. +-- * @{#WAREHOUSE.Attribute.NAVAL_AIRCRAFTCARRIER} Aircraft carrier. +-- * @{#WAREHOUSE.Attribute.NAVAL_WARSHIP} War ship, i.e. cruisers, destroyers, firgates and corvettes. +-- * @{#WAREHOUSE.Attribute.NAVAL_ARMEDSHIP} Any armed ship that is not an aircraft carrier, a cruiser, destroyer, firgatte or corvette. +-- * @{#WAREHOUSE.Attribute.NAVAL_UNARMEDSHIP} Any unarmed naval vessel. +-- * @{#WAREHOUSE.Attribute.NAVAL_OTHER} Any naval unit that does not fall into any other naval category. +-- * @{#WAREHOUSE.Attribute.OTHER_UNKNOWN} Anything that does not fall into any other category. +-- -- ## Requesting a Specific Unit Type -- -- A more specific request could look like: @@ -221,26 +282,56 @@ -- -- An even more specific request would be: -- --- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.TEMPLATENAME, "Group Name as in ME", 3) +-- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.GROUPNAME, "Group Name as in ME", 3) -- -- In this case three groups named "Group Name as in ME" are requested. This explicitly request the groups named like that in the Mission Editor. -- -- ## Requesting a General Category -- --- On the other hand, very general unspecifc requests can be made as +-- On the other hand, very general and unspecifc requests can be made by the categroy descriptor. The descriptor value parameter can be any [group category](https://wiki.hoggitworld.com/view/DCS_Class_Group), i.e. -- --- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.CATEGORY, Group.Category.Ground, 10) +-- * Group.Category.AIRPLANE for fixed wing aircraft, +-- * Group.Category.HELICOPTER for helicopters, +-- * Group.Category.GROUND for all ground troops, +-- * Group.Category.SHIP for naval assets, +-- * Group.Category.TRAIN for trains (not implemented and not working in DCS yet). +-- +-- For example, +-- +-- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.CATEGORY, Group.Category.GROUND, 10) -- --- Here, Kubuleti requests 10 ground groups and does not care which ones. This could be a mix of infantry, APCs, trucks etc. +-- means that Kubuleti requests 10 ground groups and does not care which ones. This could be a mix of infantry, APCs, trucks etc. -- -- **Note** that these general requests should be made with *great care* due to the fact, that depending on what a warehouse has in stock a lot of different unit types can be spawned. -- --- # Employing Assets +-- ## Requesting Relative Quantities -- --- Assets in the warehouses stock can used for user defined tasks realtively easily. They can be spawned into the game by a "*self request*", i.e. the warehouse +-- In addition to requesting absolute numbers of assets it is possible to request relative amounts of assets currently in stock. To this end the @{#WAREHOUSE.Quantity} enumerator +-- was introduced: +-- +-- * @{#WAREHOUSE.Quantity.ALL} +-- * @{#WAREHOUSE.Quantity.HALF} +-- * @{#WAREHOUSE.Quantity.QUARTER} +-- * @{#WAREHOUSE.Quantity.THIRD} +-- * @{#WAREHOUSE.Quantity.THREEQUARTERS} +-- +-- For example, +-- +-- warehouseBatumi:AddRequest(warehouseKobuleti, WAREHOUSE.Descriptor.CATEGORY, Group.Category.HELICOPTER, WAREHOUSE.Quantity.HALF) +-- +-- means that Kobuleti warehouse requests half of all available helicopters which Batumi warehouse currently has in stock. +-- +-- # Employing Assets - The Self Request +-- +-- Transferring assets from one warehouse to another is important but of course once the the assets are at the "right" place it is equally important that they +-- can be employed for specific tasks and assignments. +-- +-- Assets in the warehouses stock can be used for user defined tasks quite easily. They can be spawned into the game by a "***self request***", i.e. the warehouse -- requests the assets from itself: -- -- warehouseBatumi:AddRequest(warehouseBatumi, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.GROUND_INFANTRY, 5) +-- +-- Note that the *sending* and *requesting* warehouses are *identical* in this case. -- -- This would simply spawn five infantry groups in the spawn zone of the Batumi warehouse if/when they are available. -- @@ -268,10 +359,10 @@ -- end -- -- The variable *groupset* is a @{Core.Set#SET_GROUP} object and holds all asset groups from the request. The code above shows, how the mission designer can access the groups --- for further tasking. Here, the groups are only smoked but, of course, you can use them for whatever task you fancy. +-- for further tasking. Here, the groups are only smoked but, of course, you can use them for whatever assignment you fancy. -- --- Note that airborne groups are spawned in **uncontrolled state** and need to be activated first before they can start their assigned mission. --- This can be done with the @{Wrapper.Controllable#CONTROLLABLE.StartUncontrolled} function. +-- Note that airborne groups are spawned in **uncontrolled state** and need to be activated first before they can begin with their assigned tasks and missions. +-- This can be done with the @{Wrapper.Controllable#CONTROLLABLE.StartUncontrolled} function as demonstrated in the example section below. -- -- === -- @@ -296,7 +387,7 @@ -- -- Ground assets will use a road connection to travel from one warehouse to another. Therefore, a proper road connection is necessary. -- --- By default, the closest point on road to the center of the spawn zone is choses as road connection automatically. But only, if distance between the spawn zone +-- By default, the closest point on road to the center of the spawn zone is chosen as road connection automatically. But only, if distance between the spawn zone -- and the road connection is less than 3 km. -- -- The user can set the road connection manually with the @{#WAREHOUSE.SetRoadConnection} function. This is only functional for self propelled assets at the moment @@ -304,12 +395,12 @@ -- -- ## Off Road Connections -- --- For ground troops it is also possible to define off road paths from between warehouses if no proper road connection is available or should not be used. +-- For ground troops it is also possible to define off road paths between warehouses if no proper road connection is available or should not be used. -- -- An off road path can be defined via the @{#WAREHOUSE.AddOffRoadPath}(*remotewarehouse*, *group*, *oneway*) function, where -- *remotewarehouse* is the warehouse to which the path leads. --- The parameter *group* is a late activated template group. The waypoints of this group are used to define the path between the two warehouses. --- By default, the reverse paths is automatically added to get *from* the remote warehouse to this warehouse unless the parameter *oneway* is set to *true*. +-- The parameter *group* is a *late activated* template group. The waypoints of this group are used to define the path between the two warehouses. +-- By default, the reverse paths is automatically added to get *from* the remote warehouse *to* this warehouse unless the parameter *oneway* is set to *true*. -- -- ![Banner Image](..\Presentations\WAREHOUSE\Warehouse_Off-Road_Paths.png) -- @@ -317,8 +408,8 @@ -- this will not be used. -- -- Also note that you can define multiple off road connections between two warehouses. If there are multiple paths defined, the connection is chosen randomly. --- It is also possible to add the same path multiple times. By this you can influence the probability of the chosen path. For example Path_1(A->B) has been --- added two times while Path_2(A->B) was added only once. Hence, the group will choose Path_1 with a probability of 66.6 % while Path_2 is only chosen with +-- It is also possible to add the same path multiple times. By this you can influence the probability of the chosen path. For example Path1(A->B) has been +-- added two times while Path2(A->B) was added only once. Hence, the group will choose Path1 with a probability of 66.6 % while Path2 is only chosen with -- a probability of 33.3 %. -- -- ## Rail Connections @@ -642,11 +733,11 @@ -- warehouse.Senaki:AddAsset("Huey", 6) -- -- -- Kusaisi requests 3 Yak-52 form Senaki while Kobuleti wants all the rest. --- warehouse.Senaki:AddRequest(warehouse.Kutaisi, WAREHOUSE.Descriptor.TEMPLATENAME, "Yak-52", 1, nil, nil, 10) --- warehouse.Senaki:AddRequest(warehouse.Kobuleti, WAREHOUSE.Descriptor.TEMPLATENAME, "Yak-52", WAREHOUSE.Quantity.HALF, nil, nil, 70) +-- warehouse.Senaki:AddRequest(warehouse.Kutaisi, WAREHOUSE.Descriptor.GROUPNAME, "Yak-52", 1, nil, nil, 10) +-- warehouse.Senaki:AddRequest(warehouse.Kobuleti, WAREHOUSE.Descriptor.GROUPNAME, "Yak-52", WAREHOUSE.Quantity.HALF, nil, nil, 70) -- -- -- FARP London wants 1/3 of the six available Hueys. --- warehouse.Senaki:AddRequest(warehouse.London, WAREHOUSE.Descriptor.TEMPLATENAME, "Huey", WAREHOUSE.Quantity.THIRD) +-- warehouse.Senaki:AddRequest(warehouse.London, WAREHOUSE.Descriptor.GROUPNAME, "Huey", WAREHOUSE.Quantity.THIRD) -- -- ## Example 4: Transport of Assets by APCs -- @@ -710,8 +801,8 @@ -- ## Example 7: Capturing Airbase and Warehouse -- -- A red BMP has made it through our defence lines and drives towards our unprotected airbase at Senaki. --- Once the BMP captures the airbase (DCS S\_EVENT_\BASE_\CAPTURED is evaluated) the warehouse at Senaki lost its air infrastructure and it is not --- possible any more to spawn airborne units. All requests for airborne units are rejected and cancelled in this case. +-- Once the BMP captures the airbase (DCS [S\_EVENT\_BASE\_CAPTURED](https://wiki.hoggitworld.com/view/DCS_event_base_captured) is evaluated) +-- the warehouse at Senaki lost its air infrastructure and it is not possible any more to spawn airborne units. All requests for airborne units are rejected and cancelled in this case. -- -- The red BMP then drives further to the warehouse. Once it enters the warehouse zone (500 m radius around the warehouse building), the warehouse is -- considered to be under attack. This triggers the event **Attacked**. The @{#WAREHOUSE.OnAfterAttacked} function can be used to react to this situation. @@ -868,10 +959,10 @@ -- warehouse.Batumi:AddAsset("F/A-18C 2ship", 1) -- -- -- USS Stennis requests F/A-18 from Batumi. --- warehouse.Batumi:AddRequest(warehouse.Stennis, WAREHOUSE.Descriptor.TEMPLATENAME, "F/A-18C 2ship") +-- warehouse.Batumi:AddRequest(warehouse.Stennis, WAREHOUSE.Descriptor.GROUPNAME, "F/A-18C 2ship") -- -- -- Kobuleti requests F/A-18 from USS Stennis. --- warehouse.Stennis:AddRequest(warehouse.Kobuleti, WAREHOUSE.Descriptor.TEMPLATENAME, "F/A-18C 2ship") +-- warehouse.Stennis:AddRequest(warehouse.Kobuleti, WAREHOUSE.Descriptor.GROUPNAME, "F/A-18C 2ship") -- -- ## Example 11: Aircraft Carrier - Rescue Helo and Escort -- @@ -903,7 +994,7 @@ -- warehouse.Stennis:AddAsset("CH-53E", 1) -- -- -- Self request of speed boats. --- warehouse.Stennis:__AddRequest(10, warehouse.Stennis, WAREHOUSE.Descriptor.TEMPLATENAME, "CH-53E", 1, nil, nil, nil, "Rescue Helo") +-- warehouse.Stennis:__AddRequest(10, warehouse.Stennis, WAREHOUSE.Descriptor.GROUPNAME, "CH-53E", 1, nil, nil, nil, "Rescue Helo") -- warehouse.Stennis:__AddRequest(30, warehouse.Stennis, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.NAVAL_ARMEDSHIP, 5, nil, nil, nil, "Speedboats Left") -- warehouse.Stennis:__AddRequest(45, warehouse.Stennis, WAREHOUSE.Descriptor.ATTRIBUTE, WAREHOUSE.Attribute.NAVAL_ARMEDSHIP, 5, nil, nil, nil, "Speedboats Right") -- @@ -970,15 +1061,15 @@ -- -- -- So we start another request. -- if request.assignment=="Rescue Helo" then --- warehouse.Stennis:__AddRequest(10, warehouse.Stennis, WAREHOUSE.Descriptor.TEMPLATENAME, "CH-53E", 1, nil, nil, nil, "Rescue Helo") +-- warehouse.Stennis:__AddRequest(10, warehouse.Stennis, WAREHOUSE.Descriptor.GROUPNAME, "CH-53E", 1, nil, nil, nil, "Rescue Helo") -- end -- end -- -- end -- --- ## Example 12: Pause and Unpause a Warehouse +-- ## Example 12: Pause a Warehouse -- --- This example shows how to pause a warehouse. In paused state, no requests will be processed but assets can be added or be requests made. +-- This example shows how to pause and unpause a warehouse. In paused state, requests will not be processed but assets can be added and requests be added. -- -- * Warehouse Batumi is paused after 10 seconds. -- * Request from Berlin after 15 which will not be processed. @@ -1022,8 +1113,8 @@ -- -- ## Example 13: Battlefield Air Interdiction -- --- This example show how to couple the WAREHOUSE class with the @{AI.AI_BAI} class. --- Four enemy targets have been located at the famous Kobuleti X. Three Viggen 2-ship flights are assigned to kill at least one of the BMPs to complete their mission. +-- This example show how to couple the WAREHOUSE class with the @{AI.AI_Bai} class. +-- Four enemy targets have been located at the famous Kobuleti X. All three available Viggen 2-ship flights are assigned to kill at least one of the BMPs to complete their mission. -- -- -- Start Warehouse at Kobuleti. -- warehouse.Kobuleti:Start() @@ -1032,7 +1123,7 @@ -- warehouse.Kobuleti:AddAsset("Viggen 2ship", 3) -- -- -- Self request for all Viggen assets. --- warehouse.Kobuleti:AddRequest(warehouse.Kobuleti, WAREHOUSE.Descriptor.TEMPLATENAME, "Viggen 2ship", WAREHOUSE.Quantity.ALL, nil, nil, nil, "BAI") +-- warehouse.Kobuleti:AddRequest(warehouse.Kobuleti, WAREHOUSE.Descriptor.GROUPNAME, "Viggen 2ship", WAREHOUSE.Quantity.ALL, nil, nil, nil, "BAI") -- -- -- Red targets at Kobuleti X (late activated). -- local RedTargets=GROUP:FindByName("Red IVF Alpha") @@ -1268,12 +1359,12 @@ WAREHOUSE = { --- Descriptors enumerator describing the type of the asset. -- @type WAREHOUSE.Descriptor --- @field #string TEMPLATENAME Name of the asset template. +-- @field #string GROUPNAME Name of the asset template. -- @field #string UNITTYPE Typename of the DCS unit, e.g. "A-10C". -- @field #string ATTRIBUTE Generalized attribute @{#WAREHOUSE.Attribute}. -- @field #string CATEGORY Asset category of type DCS#Group.Category, i.e. GROUND, AIRPLANE, HELICOPTER, SHIP, TRAIN. WAREHOUSE.Descriptor = { - TEMPLATENAME="templatename", + GROUPNAME="templatename", UNITTYPE="unittype", ATTRIBUTE="attribute", CATEGORY="category", @@ -1379,7 +1470,7 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.5.4" +WAREHOUSE.version="0.5.4w" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. @@ -1786,15 +1877,15 @@ function WAREHOUSE:New(warehouse, alias) --- Triggers the FSM event "Captured" when a warehouse has been captured by another coalition. -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] Captured - -- @param DCS#coalition.side Coalition which captured the warehouse. - -- @param DCS#country.id Country which has captured the warehouse. + -- @param DCS#coalition.side Coalition Coalition side which captured the warehouse. + -- @param DCS#country.id Country Country id which has captured the warehouse. --- Triggers the FSM event "Captured" with a delay when a warehouse has been captured by another coalition. -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] __Captured -- @param #number delay Delay in seconds. - -- @param DCS#coalition.side Coalition which captured the warehouse. - -- @param DCS#country.id Country which has captured the warehouse. + -- @param DCS#coalition.side Coalition Coalition side which captured the warehouse. + -- @param DCS#country.id Country Country id which has captured the warehouse. --- On after "Captured" event user function. Called when the warehouse has been captured by an enemy coalition. -- @param #WAREHOUSE self @@ -3103,7 +3194,7 @@ function WAREHOUSE:onbeforeAddRequest(From, Event, To, warehouse, AssetDescripto okay=false end - elseif AssetDescriptor==WAREHOUSE.Descriptor.TEMPLATENAME then + elseif AssetDescriptor==WAREHOUSE.Descriptor.GROUPNAME then if type(AssetDescriptorValue)~="string" then self:_ErrorMessage("ERROR: Invalid request. Asset template name must be passed as a string!", 5) @@ -3118,7 +3209,7 @@ function WAREHOUSE:onbeforeAddRequest(From, Event, To, warehouse, AssetDescripto end else - self:_ErrorMessage("ERROR: Invalid request. Asset descriptor is not ATTRIBUTE, CATEGORY, TEMPLATENAME or UNITTYPE!", 5) + self:_ErrorMessage("ERROR: Invalid request. Asset descriptor is not ATTRIBUTE, CATEGORY, GROUPNAME or UNITTYPE!", 5) okay=false end From 3a8530227d61600d8682df4949277a4feb1e5882 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Fri, 28 Sep 2018 22:16:09 +0200 Subject: [PATCH 413/420] Documentation --- Moose Development/Moose/Core/Goal.lua | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/Moose Development/Moose/Core/Goal.lua b/Moose Development/Moose/Core/Goal.lua index 926e8eca1..f530deb33 100644 --- a/Moose Development/Moose/Core/Goal.lua +++ b/Moose Development/Moose/Core/Goal.lua @@ -107,8 +107,9 @@ do -- Goal end - --- @param #GOAL self - -- @param #string PlayerName + --- Add a new contribution by a player. + -- @param #GOAL self + -- @param #string PlayerName The name of the player. function GOAL:AddPlayerContribution( PlayerName ) self.Players[PlayerName] = self.Players[PlayerName] or 0 self.Players[PlayerName] = self.Players[PlayerName] + 1 @@ -123,21 +124,28 @@ do -- Goal end - --- @param #GOAL self + --- Get the players who contributed to achieve the goal. + -- The result is a list of players, sorted by the name of the players. + -- @param #GOAL self + -- @return #list The list of players, indexed by the player name. function GOAL:GetPlayerContributions() return self.Players or {} end - --- @param #GOAL self + --- Gets the total contributions that happened to achieve the goal. + -- The result is a number. + -- @param #GOAL self + -- @return #number The total number of contributions. 0 is returned if there were no contributions (yet). function GOAL:GetTotalContributions() return self.TotalContributions or 0 end - --- @param #GOAL self - -- @return #boolean true if the goal is Achieved + --- Validates if the goal is achieved. + -- @param #GOAL self + -- @return #boolean true if the goal is achieved. function GOAL:IsAchieved() return self:Is( "Achieved" ) end From dc41852e451c7635e846d8b06be21cbcf65f1460 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sat, 29 Sep 2018 13:55:00 +0200 Subject: [PATCH 414/420] fixed documentation in SPAWN --- Moose Development/Moose/Core/Spawn.lua | 55 ++++++++++++++++---------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/Moose Development/Moose/Core/Spawn.lua b/Moose Development/Moose/Core/Spawn.lua index 8ba3908ca..ca92bad56 100644 --- a/Moose Development/Moose/Core/Spawn.lua +++ b/Moose Development/Moose/Core/Spawn.lua @@ -496,7 +496,12 @@ end --- Sets the coalition of the spawned group. Note that it might be necessary to also set the country explicitly! -- @param #SPAWN self --- @param DCS#coalition.side Coalition Coalition of the group as number of enumerator, i.e. 0=coaliton.side.NEUTRAL, 1=coaliton.side.RED, 2=coalition.side.BLUE. +-- @param DCS#coalition.side Coalition Coalition of the group as number of enumerator: +-- +-- * @{DCS#coaliton.side.NEUTRAL} +-- * @{DCS#coaliton.side.RED} +-- * @{DCS#coalition.side.BLUE} +-- -- @return #SPAWN self function SPAWN:InitCoalition( Coalition ) self:F({coalition=Coalition}) @@ -507,9 +512,12 @@ function SPAWN:InitCoalition( Coalition ) end --- Sets the country of the spawn group. Note that the country determins the coalition of the group depending on which country is defined to be on which side for each specific mission! --- See https://wiki.hoggitworld.com/view/DCS_enum_country for country enumerators. -- @param #SPAWN self --- @param #DCS.country Country Country id as number or enumerator, e.g. country.id.RUSSIA=0, county.id.USA=2 etc. +-- @param #DCS.country Country Country id as number or enumerator: +-- +-- * @{DCS#country.id.RUSSIA} +-- * @{DCS#county.id.USA} +-- -- @return #SPAWN self function SPAWN:InitCountry( Country ) self:F( ) @@ -757,14 +765,20 @@ end ---TODO: Add example. --- This method provides the functionality to randomize the spawning of the Groups at a given list of zones of different types. -- @param #SPAWN self -- @param #table SpawnZoneTable A table with @{Zone} objects. If this table is given, then each spawn will be executed within the given list of @{Zone}s objects. -- @return #SPAWN -- @usage --- -- NATO Tank Platoons invading Gori. --- -- Choose between 3 different zones for each new SPAWN the Group to be executed, regardless of the zone type. +-- -- Create a zone table of the 2 zones. +-- ZoneTable = { ZONE:New( "Zone1" ), ZONE:New( "Zone2" ) } +-- +-- Spawn_Vehicle_1 = SPAWN:New( "Spawn Vehicle 1" ) +-- :InitLimit( 10, 10 ) +-- :InitRandomizeRoute( 1, 1, 200 ) +-- :InitRandomizeZones( ZoneTable ) +-- :SpawnScheduled( 5, .5 ) +-- function SPAWN:InitRandomizeZones( SpawnZoneTable ) self:F( { self.SpawnTemplatePrefix, SpawnZoneTable } ) @@ -883,10 +897,10 @@ end --- Makes the groups visible before start (like a batallion). -- The method will take the position of the group as the first position in the array. -- @param #SPAWN self --- @param #number SpawnAngle The angle in degrees how the groups and each unit of the group will be positioned. --- @param #number SpawnWidth The amount of Groups that will be positioned on the X axis. --- @param #number SpawnDeltaX The space between each Group on the X-axis. --- @param #number SpawnDeltaY The space between each Group on the Y-axis. +-- @param #number SpawnAngle The angle in degrees how the groups and each unit of the group will be positioned. +-- @param #number SpawnWidth The amount of Groups that will be positioned on the X axis. +-- @param #number SpawnDeltaX The space between each Group on the X-axis. +-- @param #number SpawnDeltaY The space between each Group on the Y-axis. -- @return #SPAWN self -- @usage -- -- Define an array of Groups. @@ -1253,16 +1267,17 @@ end -- @param SpawnFunctionArguments A random amount of arguments to be provided to the function when the group spawns. -- @return #SPAWN -- @usage --- -- Declare SpawnObject and call a function when a new Group is spawned. --- local SpawnObject = SPAWN --- :New( "SpawnObject" ) --- :InitLimit( 2, 10 ) --- :OnSpawnGroup( --- function( SpawnGroup ) --- SpawnGroup:E( "I am spawned" ) --- end --- ) --- :SpawnScheduled( 300, 0.3 ) +-- -- Declare SpawnObject and call a function when a new Group is spawned. +-- local SpawnObject = SPAWN +-- :New( "SpawnObject" ) +-- :InitLimit( 2, 10 ) +-- :OnSpawnGroup( +-- function( SpawnGroup ) +-- SpawnGroup:E( "I am spawned" ) +-- end +-- ) +-- :SpawnScheduled( 300, 0.3 ) +-- function SPAWN:OnSpawnGroup( SpawnCallBackFunction, ... ) self:F( "OnSpawnGroup" ) From a84f0e228ac863a23d247eb633383333a5591c5d Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sat, 29 Sep 2018 15:34:21 +0200 Subject: [PATCH 415/420] - pushing fix for issue with command center reporting. - pushing fix for issue with markings in cargo tasking not displayed. - i have removed the workaround for the DCS bug workaround, get the DCS API (getPlayerName()) for a unit is returning "" or nil. --- .../Moose/Tasking/CommandCenter.lua | 2 +- Moose Development/Moose/Tasking/Task.lua | 30 +++++++++++++------ Moose Development/Moose/Tasking/TaskInfo.lua | 16 +++++++++- .../Moose/Tasking/Task_CARGO.lua | 11 ++++++- Moose Development/Moose/Wrapper/Unit.lua | 12 ++++---- 5 files changed, 53 insertions(+), 18 deletions(-) diff --git a/Moose Development/Moose/Tasking/CommandCenter.lua b/Moose Development/Moose/Tasking/CommandCenter.lua index 3c2825021..11bf5b0b2 100644 --- a/Moose Development/Moose/Tasking/CommandCenter.lua +++ b/Moose Development/Moose/Tasking/CommandCenter.lua @@ -670,7 +670,7 @@ function COMMANDCENTER:ReportSummary( ReportGroup ) for MissionID, Mission in pairs( self.Missions ) do local Mission = Mission -- Tasking.Mission#MISSION - Report:Add( " - " .. Mission:ReportSummary() ) + Report:Add( " - " .. Mission:ReportSummary( ReportGroup ) ) end self:MessageToGroup( Report:Text(), ReportGroup ) diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index 84f8158d0..d5361dc17 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -1200,15 +1200,27 @@ function TASK:MenuMarkToGroup( TaskGroup ) self:UpdateTaskInfo( self.DetectedItem ) - local Report = REPORT:New():SetIndent( 0 ) - - self.TaskInfo:Report( Report, "M", TaskGroup ) - - local TargetCoordinate = self.TaskInfo:GetData( "Coordinate" ) -- Core.Point#COORDINATE - local MarkText = Report:Text( ", " ) - self:F( { Coordinate = TargetCoordinate, MarkText = MarkText } ) - TargetCoordinate:MarkToGroup( MarkText, TaskGroup ) - --Coordinate:MarkToAll( Briefing ) + local TargetCoordinates = self.TaskInfo:GetData( "Coordinates" ) -- Core.Point#COORDINATE + if TargetCoordinates then + for TargetCoordinateID, TargetCoordinate in pairs( TargetCoordinates ) do + local Report = REPORT:New():SetIndent( 0 ) + self.TaskInfo:Report( Report, "M", TaskGroup, TargetCoordinateID ) + local MarkText = Report:Text( ", " ) + self:F( { Coordinate = TargetCoordinate, MarkText = MarkText } ) + TargetCoordinate:MarkToGroup( MarkText, TaskGroup ) + --Coordinate:MarkToAll( Briefing ) + end + else + local TargetCoordinate = self.TaskInfo:GetData( "Coordinate" ) -- Core.Point#COORDINATE + if TargetCoordinate then + local Report = REPORT:New():SetIndent( 0 ) + self.TaskInfo:Report( Report, "M", TaskGroup ) + local MarkText = Report:Text( ", " ) + self:F( { Coordinate = TargetCoordinate, MarkText = MarkText } ) + TargetCoordinate:MarkToGroup( MarkText, TaskGroup ) + end + end + end --- Report the task status. diff --git a/Moose Development/Moose/Tasking/TaskInfo.lua b/Moose Development/Moose/Tasking/TaskInfo.lua index 2822615d5..9a425311a 100644 --- a/Moose Development/Moose/Tasking/TaskInfo.lua +++ b/Moose Development/Moose/Tasking/TaskInfo.lua @@ -85,7 +85,7 @@ end -- @return Data The data of the info. function TASKINFO:GetData( Key ) local Object = self.Info:Get( Key ) - return Object.Data + return Object and Object.Data end @@ -130,6 +130,19 @@ function TASKINFO:AddCoordinate( Coordinate, Order, Detail, Keep ) end +--- Add Coordinates. +-- @param #TASKINFO self +-- @param #list Coordinates +-- @param #number Order The display order, which is a number from 0 to 100. +-- @param #TASKINFO.Detail Detail The detail Level. +-- @param #boolean Keep (optional) If true, this would indicate that the planned taskinfo would be persistent when the task is completed, so that the original planned task info is used at the completed reports. +-- @return #TASKINFO self +function TASKINFO:AddCoordinates( Coordinates, Order, Detail, Keep ) + self:AddInfo( "Coordinates", Coordinates, Order, Detail, Keep ) + return self +end + + --- Add Threat. -- @param #TASKINFO self @@ -259,6 +272,7 @@ function TASKINFO:AddCargoSet( SetCargo, Order, Detail, Keep ) ) self:AddInfo( "Cargo", CargoReport:Text(), Order, Detail, Keep ) + return self end diff --git a/Moose Development/Moose/Tasking/Task_CARGO.lua b/Moose Development/Moose/Tasking/Task_CARGO.lua index 00de26c1b..97a067220 100644 --- a/Moose Development/Moose/Tasking/Task_CARGO.lua +++ b/Moose Development/Moose/Tasking/Task_CARGO.lua @@ -1361,10 +1361,19 @@ do -- TASK_CARGO end --- @param #TASK_CARGO self - function TASK_CARGO:UpdateTaskInfo( DetectedItem ) + function TASK_CARGO:UpdateTaskInfo() if self:IsStatePlanned() or self:IsStateAssigned() then + self.TaskInfo:AddTaskName( 0, "MSOD" ) self.TaskInfo:AddCargoSet( self.SetCargo, 10, "SOD", true ) + local Coordinates = {} + for CargoName, Cargo in pairs( self.SetCargo:GetSet() ) do + local Cargo = Cargo -- Cargo.Cargo#CARGO + if not Cargo:IsLoaded() then + Coordinates[#Coordinates+1] = Cargo:GetCoordinate() + end + end + self.TaskInfo:AddCoordinates( Coordinates, 1, "M" ) end end diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 9124a68b7..71fd902a6 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -332,12 +332,12 @@ function UNIT:GetPlayerName() local PlayerName = DCSUnit:getPlayerName() -- TODO Workaround DCS-BUG-3 - https://github.com/FlightControl-Master/MOOSE/issues/696 - if PlayerName == nil or PlayerName == "" then - local PlayerCategory = DCSUnit:getDesc().category - if PlayerCategory == Unit.Category.GROUND_UNIT or PlayerCategory == Unit.Category.SHIP then - PlayerName = "Player" .. DCSUnit:getID() - end - end +-- if PlayerName == nil or PlayerName == "" then +-- local PlayerCategory = DCSUnit:getDesc().category +-- if PlayerCategory == Unit.Category.GROUND_UNIT or PlayerCategory == Unit.Category.SHIP then +-- PlayerName = "Player" .. DCSUnit:getID() +-- end +-- end -- -- Good code -- if PlayerName == nil then -- PlayerName = nil From 7e5495bcd87cbfc53d8e27659396ae08262fbe07 Mon Sep 17 00:00:00 2001 From: Frank Date: Sun, 30 Sep 2018 00:19:08 +0200 Subject: [PATCH 416/420] Warehouse v0.5.5 Added AssetDead event. Some fixes. --- .../Moose/AI/AI_Cargo_Airplane.lua | 9 +- Moose Development/Moose/Cargo/CargoUnit.lua | 4 +- .../Moose/Functional/Detection.lua | 1 - .../Moose/Functional/Warehouse.lua | 144 ++++++++++-------- .../Moose/Wrapper/Controllable.lua | 11 +- 5 files changed, 95 insertions(+), 74 deletions(-) diff --git a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua index 5fb72f990..947d7ec5b 100644 --- a/Moose Development/Moose/AI/AI_Cargo_Airplane.lua +++ b/Moose Development/Moose/AI/AI_Cargo_Airplane.lua @@ -266,14 +266,11 @@ end function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate, Speed, Height, PickupZone ) if Airplane and Airplane:IsAlive() then - --env.info("FF onafterpick aircraft alive") self.PickupZone = PickupZone -- Get closest airbase of current position. local ClosestAirbase, DistToAirbase=Airplane:GetCoordinate():GetClosestAirbase() - - --env.info("FF onafterpickup closest airbase "..ClosestAirbase:GetName()) -- Two cases. Aircraft spawned in air or at an airbase. if Airplane:InAir() then @@ -291,8 +288,6 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate, if Airplane:InAir() or Dist>500 then - --env.info("FF onafterpickup routing to airbase "..ClosestAirbase:GetName()) - -- Route aircraft to pickup airbase. self:Route( Airplane, Airbase, Speed, Height ) @@ -303,7 +298,6 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate, self.RoutePickup = true else - --env.info("FF onafterpick calling landed") -- We are already at the right airbase ==> Landed ==> triggers loading of troops. Is usually called at engine shutdown event. self.RoutePickup=true @@ -312,8 +306,7 @@ function AI_CARGO_AIRPLANE:onafterPickup( Airplane, From, Event, To, Coordinate, end self:GetParent( self, AI_CARGO_AIRPLANE ).onafterPickup( self, Airplane, From, Event, To, Coordinate, Speed, Height, PickupZone ) - else - --env.info("FF onafterpick aircraft not alive") + end diff --git a/Moose Development/Moose/Cargo/CargoUnit.lua b/Moose Development/Moose/Cargo/CargoUnit.lua index aeeece955..6ca4edbff 100644 --- a/Moose Development/Moose/Cargo/CargoUnit.lua +++ b/Moose Development/Moose/Cargo/CargoUnit.lua @@ -238,9 +238,7 @@ do -- CARGO_UNIT if not self.CargoInAir then -- If NearRadius is given, then use the given NearRadius, otherwise calculate the NearRadius -- based upon the Carrier bounding radius, which is calculated from the bounding rectangle on the Y axis. - env.info(string.format("FF nearradius = %d (before)", NearRadius)) - local NearRadius = CargoCarrier:GetBoundingRadius( NearRadius ) + 5 - env.info(string.format("FF nearradius = %d isnear=%s", NearRadius, tostring(self:IsNear( CargoCarrier:GetPointVec2(), NearRadius )) )) + local NearRadius = NearRadius or CargoCarrier:GetBoundingRadius() + 5 if self:IsNear( CargoCarrier:GetPointVec2(), NearRadius ) then self:Load( CargoCarrier, NearRadius, ... ) else diff --git a/Moose Development/Moose/Functional/Detection.lua b/Moose Development/Moose/Functional/Detection.lua index 47df06bcf..0f556bf7c 100644 --- a/Moose Development/Moose/Functional/Detection.lua +++ b/Moose Development/Moose/Functional/Detection.lua @@ -1940,7 +1940,6 @@ do -- DETECTION_UNITS -- @param #DETECTION_UNITS self -- @return #DETECTION_UNITS self function DETECTION_UNITS:CreateDetectionItems() - env.info("FF createdetectionitmes") -- Loop the current detected items, and check if each object still exists and is detected. for DetectedItemKey, DetectedItem in pairs( self.DetectedItems ) do diff --git a/Moose Development/Moose/Functional/Warehouse.lua b/Moose Development/Moose/Functional/Warehouse.lua index 9287439a3..8b08881e8 100644 --- a/Moose Development/Moose/Functional/Warehouse.lua +++ b/Moose Development/Moose/Functional/Warehouse.lua @@ -20,7 +20,8 @@ -- -- === -- --- ### Authors: **funkyfranky**, FlightControl (cargo dispatcher classes) +-- ### Author: **funkyfranky** +-- ### Co-author: FlightControl (cargo dispatcher classes) -- -- @module Functional.Warehouse -- @image MOOSE.JPG @@ -1470,18 +1471,20 @@ WAREHOUSE.db = { --- Warehouse class version. -- @field #string version -WAREHOUSE.version="0.5.4w" +WAREHOUSE.version="0.5.5" ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -- TODO: Warehouse todo list. ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- TODO: Add check if assets "on the move" are stationary. Can happen if ground units get stuck in buildings. If stationary auto complete transport by adding assets to request warehouse? Time? +-- TODO: Optimize findpathonroad. Do it only once (first time) and safe paths between warehouses similar to off-road paths. -- TODO: Spawn assets only virtually, i.e. remove requested assets from stock but do NOT spawn them ==> Interface to A2A dispatcher! Maybe do a negative sign on asset number? -- TODO: Test capturing a neutral warehouse. -- TODO: Make more examples: ARTY, CAP, ... -- TODO: Check also general requests like all ground. Is this a problem for self propelled if immobile units are among the assets? Check if transport. -- TODO: Handle the case when units of a group die during the transfer. --- TODO: Added habours as interface for transport to from warehouses? +-- TODO: Added habours as interface for transport to from warehouses? Could make a rudimentary shipping dispatcher. -- TODO: Add save/load capability of warehouse <==> percistance after mission restart. Difficult in lua! -- DONE: Get cargo bay and weight from CARGO_GROUP and GROUP. No necessary any more! -- DONE: Add possibility to set weight and cargo bay manually in AddAsset function as optional parameters. @@ -1606,7 +1609,8 @@ function WAREHOUSE:New(warehouse, alias) self:AddTransition("Attacked", "Defeated", "Running") -- Attack by other coalition was defeated! self:AddTransition("Attacked", "Captured", "Running") -- Warehouse was captured by another coalition. It must have been attacked first. self:AddTransition("*", "AirbaseCaptured", "*") -- Airbase was captured by other coalition. - self:AddTransition("*", "AirbaseRecaptured", "*") -- Airbase was re-captured from other coalition. + self:AddTransition("*", "AirbaseRecaptured", "*") -- Airbase was re-captured from other coalition. + self:AddTransition("*", "AssetDead", "*") -- An asset group died. self:AddTransition("*", "Destroyed", "Destroyed") -- Warehouse was destroyed. All assets in stock are gone and warehouse is stopped. ------------------------ @@ -1835,21 +1839,21 @@ function WAREHOUSE:New(warehouse, alias) --- Triggers the FSM event "Attacked" when a warehouse is under attack by an another coalition. - -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] Attacked + -- @param #WAREHOUSE self -- @param DCS#coalition.side Coalition Coalition side which is attacking the warehouse, i.e. a number of @{DCS#coalition.side} enumerator. -- @param DCS#country.id Country Country ID, which is attacking the warehouse, i.e. a number @{DCS#country.id} enumerator. --- Triggers the FSM event "Attacked" with a delay when a warehouse is under attack by an another coalition. - -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] __Attacked + -- @param #WAREHOUSE self -- @param #number delay Delay in seconds. -- @param DCS#coalition.side Coalition Coalition side which is attacking the warehouse, i.e. a number of @{DCS#coalition.side} enumerator. -- @param DCS#country.id Country Country ID, which is attacking the warehouse, i.e. a number @{DCS#country.id} enumerator. --- On after "Attacked" event user function. Called when a warehouse (zone) is under attack by an enemy. - -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] OnAfterAttacked + -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. @@ -1858,38 +1862,38 @@ function WAREHOUSE:New(warehouse, alias) --- Triggers the FSM event "Defeated" when an attack from an enemy was defeated. - -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] Defeated + -- @param #WAREHOUSE self --- Triggers the FSM event "Defeated" with a delay when an attack from an enemy was defeated. - -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] __Defeated + -- @param #WAREHOUSE self -- @param #number delay Delay in seconds. --- On after "Defeated" event user function. Called when an enemy attack was defeated. + -- @function [parent=#WAREHOUSE] OnAfterDefeate -- @param #WAREHOUSE self - -- @function [parent=#WAREHOUSE] OnAfterDefeated -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. --- Triggers the FSM event "Captured" when a warehouse has been captured by another coalition. - -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] Captured + -- @param #WAREHOUSE self -- @param DCS#coalition.side Coalition Coalition side which captured the warehouse. -- @param DCS#country.id Country Country id which has captured the warehouse. --- Triggers the FSM event "Captured" with a delay when a warehouse has been captured by another coalition. - -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] __Captured + -- @param #WAREHOUSE self -- @param #number delay Delay in seconds. -- @param DCS#coalition.side Coalition Coalition side which captured the warehouse. -- @param DCS#country.id Country Country id which has captured the warehouse. --- On after "Captured" event user function. Called when the warehouse has been captured by an enemy coalition. - -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] OnAfterCaptured + -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. @@ -1898,19 +1902,19 @@ function WAREHOUSE:New(warehouse, alias) -- --- Triggers the FSM event "AirbaseCaptured" when the airbase of the warehouse has been captured by another coalition. - -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] AirbaseCaptured + -- @param #WAREHOUSE self -- @param DCS#coalition.side Coalition Coalition side which captured the airbase, i.e. a number of @{DCS#coalition.side} enumerator. --- Triggers the FSM event "AirbaseCaptured" with a delay when the airbase of the warehouse has been captured by another coalition. - -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] __AirbaseCaptured + -- @param #WAREHOUSE self -- @param #number delay Delay in seconds. -- @param DCS#coalition.side Coalition Coalition side which captured the airbase, i.e. a number of @{DCS#coalition.side} enumerator. --- On after "AirbaseCaptured" even user function. Called when the airbase of the warehouse has been captured by another coalition. - -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] OnAfterAirbaseCaptured + -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. @@ -1923,32 +1927,55 @@ function WAREHOUSE:New(warehouse, alias) -- @param DCS#coalition.side Coalition Coalition which re-captured the airbase, i.e. the same as the current warehouse owner coalition. --- Triggers the FSM event "AirbaseRecaptured" with a delay when the airbase of the warehouse has been re-captured from the other coalition. - -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] __AirbaseRecaptured + -- @param #WAREHOUSE self -- @param #number delay Delay in seconds. -- @param DCS#coalition.side Coalition Coalition which re-captured the airbase, i.e. the same as the current warehouse owner coalition. --- On after "AirbaseRecaptured" event user function. Called when the airbase of the warehouse has been re-captured from the other coalition. - -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] OnAfterAirbaseRecaptured + -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. -- @param DCS#coalition.side Coalition Coalition which re-captured the airbase, i.e. the same as the current warehouse owner coalition. - --- Triggers the FSM event "Destroyed" when the warehouse was destroyed. Services are stopped. + --- Triggers the FSM event "AssetDead" when an asset group has died. + -- @function [parent=#WAREHOUSE] AssetDead -- @param #WAREHOUSE self + -- @param #WAREHOUSE.Assetitem asset The asset that is dead. + -- @param #WAREHOUSE.Pendingitem request The request of the dead asset. + + --- Triggers the delayed FSM event "AssetDead" when an asset group has died. + -- @function [parent=#WAREHOUSE] __AssetDead + -- @param #WAREHOUSE self + -- @param #number delay Delay in seconds. + -- @param #WAREHOUSE.Assetitem asset The asset that is dead. + -- @param #WAREHOUSE.Pendingitem request The request of the dead asset. + + --- On after "AssetDead" event user function. Called when an asset group died. + -- @function [parent=#WAREHOUSE] OnAfterAssetDead + -- @param #WAREHOUSE self + -- @param #string From From state. + -- @param #string Event Event. + -- @param #string To To state. + -- @param #WAREHOUSE.Assetitem asset The asset that is dead. + -- @param #WAREHOUSE.Pendingitem request The request of the dead asset. + + + --- Triggers the FSM event "Destroyed" when the warehouse was destroyed. Services are stopped. -- @function [parent=#WAREHOUSE] Destroyed + -- @param #WAREHOUSE self --- Triggers the FSM event "Destroyed" with a delay when the warehouse was destroyed. Services are stopped. - -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] Destroyed + -- @param #WAREHOUSE self -- @param #number delay Delay in seconds. --- On after "Destroyed" event user function. Called when the warehouse was destroyed. Services are stopped. - -- @param #WAREHOUSE self -- @function [parent=#WAREHOUSE] OnAfterDestroyed + -- @param #WAREHOUSE self -- @param #string From From state. -- @param #string Event Event. -- @param #string To To state. @@ -3054,7 +3081,6 @@ function WAREHOUSE:_RegisterAsset(group, ngroups, forceattribute, forcecargobay, -- Cargo bay size. local bay=forcecargobay or unit:GetCargoBayFreeWeight() - env.info(string.format("FF unit %s bay=%d", unit:GetName(), bay)) -- Add bay size to table. table.insert(cargobay, bay) @@ -3436,7 +3462,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Route plane to the requesting warehouses airbase. -- Actually, the route is already set. We only need to activate the uncontrolled group. - self:_RouteAir(group, Request.airbase) + self:_RouteAir(group) elseif _cargocategory==Group.Category.SHIP then self:T2(self.wid..string.format("Route naval group %s.", group:GetName())) @@ -3557,6 +3583,9 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) -- Spawn helos at airport in controlled state. They need to fly to the spawn zone. spawngroup=self:_SpawnAssetAircraft(_alias,_assetitem, Pending, Parking[_assetitem.uid], false) + + -- Activate helo randomly within the next 10 seconds. + --spawngroup:StartUncontrolled(math.random(10)) elseif Request.transporttype==WAREHOUSE.TransportType.APC then @@ -3729,7 +3758,7 @@ function WAREHOUSE:onafterRequest(From, Event, To, Request) end --- Function called if carrier group is going home. - function CargoTransport:OnAfterHome(From, Event, To, Carrier, Coordinate, Speed, HomeZone) + function CargoTransport:OnAfterHome(From, Event, To, Carrier, Coordinate, Speed, Height, HomeZone) -- Get warehouse state. local warehouse=Carrier:GetState(Carrier, "WAREHOUSE") --#WAREHOUSE @@ -3911,23 +3940,6 @@ function WAREHOUSE:onafterArrived(From, Event, To, group) end ---- Get asset from group and request. --- @param #WAREHOUSE self --- @param Wrapper.Group#GROUP group The group for which the asset should be obtained. --- @param #WAREHOUSE.Pendingitem request Pending request. --- @return #WAREHOUSE.Assetitem The asset. -function WAREHOUSE:_GetAssetFromGroupRequest(group,request) - - -- Get the IDs for this group. In particular, we use the asset ID to figure out which group was delivered. - local wid,aid,rid=self:_GetIDsFromGroup(group) - - -- Retrieve asset from request. - local asset=request.assets[aid] -end - - - - --- On after "Delivered" event. Triggered when all asset groups have reached their destination. Corresponding request is deleted from the pending queue. -- @param #WAREHOUSE self -- @param #string From From state. @@ -4165,6 +4177,20 @@ function WAREHOUSE:onafterAirbaseRecaptured(From, Event, To, Coalition) end +--- On after "AssetDead" event triggerd when an asset group died. +-- @param #WAREHOUSE self +-- @param #string From From state. +-- @param #string Event Event. +-- @param #string To To state. +-- @param #WAREHOUSE.Assetitem asset The asset that is dead. +-- @param #WAREHOUSE.Pendingitem request The request of the dead asset. +function WAREHOUSE:onafterAssetDead(From, Event, To, asset, request) + local text=string.format("Asset %s from request id=%d is dead!", asset.templatename, request.uid) + self:T(self.wid..text) + self:_DebugMessage(text) +end + + --- On after "Destroyed" event. Warehouse was destroyed. All services are stopped. Warehouse is going to "Stopped" state in one minute. -- @param #WAREHOUSE self -- @param #string From From state. @@ -4216,13 +4242,8 @@ function WAREHOUSE:_SpawnAssetRequest(Request) Parking=self:_FindParkingForAssets(self.airbase,_assetstock) or {} end - -- Spawn aircraft in uncontrolled state if request comes from the same warehouse. - local UnControlled=false - local AIOnOff=true - if Request.toself then - UnControlled=true - AIOnOff=false - end + -- Spawn aircraft in uncontrolled state. + local UnControlled=true -- Create an empty group set. local _groupset=SET_GROUP:New() @@ -4303,7 +4324,7 @@ end -- @param #WAREHOUSE.Assetitem asset Ground asset that will be spawned. -- @param #WAREHOUSE.Queueitem request Request belonging to this asset. Needed for the name/alias. -- @param Core.Zone#ZONE spawnzone Zone where the assets should be spawned. --- @param boolean aioff If true, AI of ground units are set to off. +-- @param #boolean aioff If true, AI of ground units are set to off. -- @return Wrapper.Group#GROUP The spawned group or nil if the group could not be spawned. function WAREHOUSE:_SpawnAssetGroundNaval(alias, asset, request, spawnzone, aioff) @@ -4317,8 +4338,6 @@ function WAREHOUSE:_SpawnAssetGroundNaval(alias, asset, request, spawnzone, aiof -- Get a random coordinate in the spawn zone. local coord=spawnzone:GetRandomCoordinate() - - --spawnzone:SmokeZone(1, 30) -- Translate the position of the units. for i=1,#template.units do @@ -4344,7 +4363,7 @@ function WAREHOUSE:_SpawnAssetGroundNaval(alias, asset, request, spawnzone, aiof template.x = coord.x template.y = coord.z - template.alt = coord.y + template.alt = coord.y -- Spawn group. local group=_DATABASE:Spawn(template) --Wrapper.Group#GROUP @@ -4671,18 +4690,18 @@ end --- Route the airplane from one airbase another. Activates uncontrolled aircraft and sets ROE/ROT for ferry flights. -- ROE is set to return fire and ROT to passive defence. -- @param #WAREHOUSE self --- @param Wrapper.Group#GROUP Aircraft Airplane group to be routed. +-- @param Wrapper.Group#GROUP aircraft Airplane group to be routed. function WAREHOUSE:_RouteAir(aircraft) if aircraft and aircraft:IsAlive()~=nil then -- Debug info. self:T2(self.wid..string.format("RouteAir aircraft group %s alive=%s", aircraft:GetName(), tostring(aircraft:IsAlive()))) + + -- Give start command to activate uncontrolled aircraft within the next 60 seconds. + local starttime=math.random(60) + aircraft:StartUncontrolled(starttime) - -- Give start command to activate uncontrolled aircraft. - --aircraft:SetCommand({id='Start', params={}}) - aircraft:StartUncontrolled() - -- Debug info. self:T2(self.wid..string.format("RouteAir aircraft group %s alive=%s (after start command)", aircraft:GetName(), tostring(aircraft:IsAlive()))) @@ -4977,7 +4996,6 @@ function WAREHOUSE:_UnitDead(deadunit, request) local unitname=self:_GetNameWithOut(deadunit) local groupname=self:_GetNameWithOut(group) - -- Debug message. local text=string.format("Unit %s died! #units=%d/%d ==> Group dead=%s (IsAlive=%s).", unitname, nunits, nunits0, tostring(groupdead), tostring(group:IsAlive())) self:T2(self.wid..text) @@ -4987,9 +5005,15 @@ function WAREHOUSE:_UnitDead(deadunit, request) self:E(self.wid.."ERROR: Number of units negative! This should not happen.") end + -- Group is dead! if groupdead then self:T(self.wid..string.format("Group %s (transport=%s) is dead!", groupname, tostring(self:_GroupIsTransport(group,request)))) - group:SmokeWhite() + if self.Debug then + group:SmokeWhite() + end + -- Trigger AssetDead event. + local asset=self:FindAssetInDB(group) + self:AssetDead(asset, request) end @@ -5803,7 +5827,7 @@ function WAREHOUSE:_GetTransportsForAssets(request) -- How many times does the cargo fit into the carrier? local delta=cargobay-asset.weight - env.info(string.format("k=%d, j=%d delta=%d cargobay=%d weight=%d", k, j, delta, cargobay, asset.weight)) + --env.info(string.format("k=%d, j=%d delta=%d cargobay=%d weight=%d", k, j, delta, cargobay, asset.weight)) --self:E(self.wid..string.format("%s unit %d loads cargo uid=%d: bayempty=%02d, bayloaded = %02d - weight=%02d", transport.templatename, k, asset.uid, transport.cargobay[k], cargobay, asset.weight)) diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index ad789a5a6..68f035bed 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -619,9 +619,16 @@ end --- Give an uncontrolled air controllable the start command. -- @param #CONTROLLABLE self +-- @param #number delay (Optional) Delay before start command in seconds. -- @return #CONTROLLABLE self -function CONTROLLABLE:StartUncontrolled() - self:SetCommand({id='Start', params={}}) +function CONTROLLABLE:StartUncontrolled(delay) + if delay and delay>0 then + env.info(string.format("FF %s delayed start after %d seconds", self:GetName(), delay)) + SCHEDULER:New(nil, CONTROLLABLE.StartUncontrolled, {self}, delay) + else + env.info(string.format("FF %s instant start", self:GetName())) + self:SetCommand({id='Start', params={}}) + end return self end From 064111407b82219a189030bf381319d1095cf657 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Sun, 30 Sep 2018 08:03:03 +0200 Subject: [PATCH 417/420] Fixing SET_GROUP RemoveGroupsByName method. --- Moose Development/Moose/Core/Set.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 58d82b11b..6fa79d6f5 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -860,7 +860,7 @@ do -- SET_GROUP local RemoveGroupNamesArray = ( type( RemoveGroupNames ) == "table" ) and RemoveGroupNames or { RemoveGroupNames } for RemoveGroupID, RemoveGroupName in pairs( RemoveGroupNamesArray ) do - self:Remove( RemoveGroupName.GroupName ) + self:Remove( RemoveGroupName ) end return self From d03761486145ce72d8b7e7c1a7b394c7d85adf43 Mon Sep 17 00:00:00 2001 From: FlightControl Date: Mon, 1 Oct 2018 19:47:11 +0200 Subject: [PATCH 418/420] Changes. --- Moose Development/Moose/Core/Point.lua | 5 +- Moose Development/Moose/Core/Set.lua | 20 ++++++ Moose Development/Moose/Tasking/Task.lua | 10 +-- Moose Development/Moose/Tasking/TaskInfo.lua | 11 +-- .../Moose/Tasking/Task_Cargo_CSAR.lua | 51 +------------- .../Moose/Tasking/Task_Cargo_Dispatcher.lua | 68 ++++++++++++++++++- Moose Development/Moose/Wrapper/Group.lua | 38 +++++++++++ 7 files changed, 141 insertions(+), 62 deletions(-) diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index 049f9b444..984f6eb5d 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -1929,17 +1929,20 @@ do -- COORDINATE -- @param Core.Settings#SETTINGS Settings -- @param Tasking.Task#TASK Task The task for which coordinates need to be calculated. -- @return #string The coordinate Text in the configured coordinate system. - function COORDINATE:ToString( Controllable, Settings, Task ) -- R2.2 + function COORDINATE:ToString( Controllable, Settings, Task ) self:F2( { Controllable = Controllable and Controllable:GetName() } ) local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS local ModeA2A = false + self:E('A2A false') if Task then + self:E('Task ' .. Task.ClassName ) if Task:IsInstanceOf( TASK_A2A ) then ModeA2A = true + self:E('A2A true') else if Task:IsInstanceOf( TASK_A2G ) then ModeA2A = false diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index 6fa79d6f5..d1a1cc8db 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -3119,6 +3119,26 @@ do -- SET_STATIC end + --- Calculate the maxium A2G threat level of the SET_STATIC. + -- @param #SET_STATIC self + -- @return #number The maximum threatlevel + function SET_STATIC:CalculateThreatLevelA2G() + + local MaxThreatLevelA2G = 0 + local MaxThreatText = "" + for StaticName, StaticData in pairs( self:GetSet() ) do + local ThreatStatic = StaticData -- Wrapper.Static#STATIC + local ThreatLevelA2G, ThreatText = ThreatStatic:GetThreatLevel() + if ThreatLevelA2G > MaxThreatLevelA2G then + MaxThreatLevelA2G = ThreatLevelA2G + MaxThreatText = ThreatText + end + end + + self:F( { MaxThreatLevelA2G = MaxThreatLevelA2G, MaxThreatText = MaxThreatText } ) + return MaxThreatLevelA2G, MaxThreatText + + end --- -- @param #SET_STATIC self diff --git a/Moose Development/Moose/Tasking/Task.lua b/Moose Development/Moose/Tasking/Task.lua index d5361dc17..0f4ec262d 100644 --- a/Moose Development/Moose/Tasking/Task.lua +++ b/Moose Development/Moose/Tasking/Task.lua @@ -1204,7 +1204,7 @@ function TASK:MenuMarkToGroup( TaskGroup ) if TargetCoordinates then for TargetCoordinateID, TargetCoordinate in pairs( TargetCoordinates ) do local Report = REPORT:New():SetIndent( 0 ) - self.TaskInfo:Report( Report, "M", TaskGroup, TargetCoordinateID ) + self.TaskInfo:Report( Report, "M", TaskGroup, self ) local MarkText = Report:Text( ", " ) self:F( { Coordinate = TargetCoordinate, MarkText = MarkText } ) TargetCoordinate:MarkToGroup( MarkText, TaskGroup ) @@ -1214,7 +1214,7 @@ function TASK:MenuMarkToGroup( TaskGroup ) local TargetCoordinate = self.TaskInfo:GetData( "Coordinate" ) -- Core.Point#COORDINATE if TargetCoordinate then local Report = REPORT:New():SetIndent( 0 ) - self.TaskInfo:Report( Report, "M", TaskGroup ) + self.TaskInfo:Report( Report, "M", TaskGroup, self ) local MarkText = Report:Text( ", " ) self:F( { Coordinate = TargetCoordinate, MarkText = MarkText } ) TargetCoordinate:MarkToGroup( MarkText, TaskGroup ) @@ -1751,7 +1751,7 @@ function TASK:ReportSummary( ReportGroup ) -- Determine the status of the Task. Report:Add( "State: <" .. self:GetState() .. ">" ) - self.TaskInfo:Report( Report, "S", ReportGroup ) + self.TaskInfo:Report( Report, "S", ReportGroup, self ) return Report:Text( ', ' ) end @@ -1768,7 +1768,7 @@ function TASK:ReportOverview( ReportGroup ) local TaskName = self:GetName() local Report = REPORT:New() - self.TaskInfo:Report( Report, "O", ReportGroup ) + self.TaskInfo:Report( Report, "O", ReportGroup, self ) return Report:Text() end @@ -1852,7 +1852,7 @@ function TASK:ReportDetails( ReportGroup ) Report:AddIndent( Players ) end - self.TaskInfo:Report( Report, "D", ReportGroup ) + self.TaskInfo:Report( Report, "D", ReportGroup, self ) return Report:Text() end diff --git a/Moose Development/Moose/Tasking/TaskInfo.lua b/Moose Development/Moose/Tasking/TaskInfo.lua index 9a425311a..d3fc88350 100644 --- a/Moose Development/Moose/Tasking/TaskInfo.lua +++ b/Moose Development/Moose/Tasking/TaskInfo.lua @@ -284,8 +284,9 @@ end -- @param Core.Report#REPORT Report -- @param #TASKINFO.Detail Detail The detail Level. -- @param Wrapper.Group#GROUP ReportGroup +-- @param Tasking.Task#TASK Task -- @return #TASKINFO self -function TASKINFO:Report( Report, Detail, ReportGroup ) +function TASKINFO:Report( Report, Detail, ReportGroup, Task ) local Line = 0 local LineReport = REPORT:New() @@ -306,7 +307,7 @@ function TASKINFO:Report( Report, Detail, ReportGroup ) end if Key == "Coordinate" then local Coordinate = Data.Data -- Core.Point#COORDINATE - Text = Coordinate:ToString( ReportGroup:GetUnit(1), nil, self ) + Text = Coordinate:ToString( ReportGroup:GetUnit(1), nil, Task ) end if Key == "Threat" then local DataText = Data.Data -- #string @@ -322,15 +323,15 @@ function TASKINFO:Report( Report, Detail, ReportGroup ) end if Key == "QFE" then local Coordinate = Data.Data -- Core.Point#COORDINATE - Text = Coordinate:ToStringPressure( ReportGroup:GetUnit(1), nil, self ) + Text = Coordinate:ToStringPressure( ReportGroup:GetUnit(1), nil, Task ) end if Key == "Temperature" then local Coordinate = Data.Data -- Core.Point#COORDINATE - Text = Coordinate:ToStringTemperature( ReportGroup:GetUnit(1), nil, self ) + Text = Coordinate:ToStringTemperature( ReportGroup:GetUnit(1), nil, Task ) end if Key == "Wind" then local Coordinate = Data.Data -- Core.Point#COORDINATE - Text = Coordinate:ToStringWind( ReportGroup:GetUnit(1), nil, self ) + Text = Coordinate:ToStringWind( ReportGroup:GetUnit(1), nil, Task ) end if Key == "Cargo" then local DataText = Data.Data -- #string diff --git a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua index 88cc801cc..7b099980f 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_CSAR.lua @@ -289,16 +289,6 @@ do -- TASK_CARGO_CSAR self:F( { CargoDeployed = self.CargoDeployed ~= nil and "true" or "false" } ) - --- OnBefore Transition Handler for Event CargoPickedUp. - -- @function [parent=#TASK_CARGO_CSAR] OnBeforeCargoPickedUp - -- @param #TASK_CARGO_CSAR self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. - -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - -- @return #boolean Return false to cancel Transition. - --- OnAfter Transition Handler for Event CargoPickedUp. -- @function [parent=#TASK_CARGO_CSAR] OnAfterCargoPickedUp -- @param #TASK_CARGO_CSAR self @@ -308,30 +298,6 @@ do -- TASK_CARGO_CSAR -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - --- Synchronous Event Trigger for Event CargoPickedUp. - -- @function [parent=#TASK_CARGO_CSAR] CargoPickedUp - -- @param #TASK_CARGO_CSAR self - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. - -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - - --- Asynchronous Event Trigger for Event CargoPickedUp. - -- @function [parent=#TASK_CARGO_CSAR] __CargoPickedUp - -- @param #TASK_CARGO_CSAR self - -- @param #number Delay The delay in seconds. - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that PickedUp the cargo. You can use this to retrieve the PlayerName etc. - -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - - --- OnBefore Transition Handler for Event CargoDeployed. - -- @function [parent=#TASK_CARGO_CSAR] OnBeforeCargoDeployed - -- @param #TASK_CARGO_CSAR self - -- @param #string From The From State string. - -- @param #string Event The Event string. - -- @param #string To The To State string. - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. - -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. - -- @return #boolean Return false to cancel Transition. - --- OnAfter Transition Handler for Event CargoDeployed. -- @function [parent=#TASK_CARGO_CSAR] OnAfterCargoDeployed -- @param #TASK_CARGO_CSAR self @@ -342,21 +308,6 @@ do -- TASK_CARGO_CSAR -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. - --- Synchronous Event Trigger for Event CargoDeployed. - -- @function [parent=#TASK_CARGO_CSAR] CargoDeployed - -- @param #TASK_CARGO_CSAR self - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. - -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. - - --- Asynchronous Event Trigger for Event CargoDeployed. - -- @function [parent=#TASK_CARGO_CSAR] __CargoDeployed - -- @param #TASK_CARGO_CSAR self - -- @param #number Delay The delay in seconds. - -- @param Wrapper.Unit#UNIT TaskUnit The Unit (Client) that Deployed the cargo. You can use this to retrieve the PlayerName etc. - -- @param Core.Cargo#CARGO Cargo The Cargo that got PickedUp by the TaskUnit. You can use this to check Cargo Status. - -- @param Core.Zone#ZONE DeployZone The zone where the Cargo got Deployed or UnBoarded. - local Fsm = self:GetUnitProcess() local CargoReport = REPORT:New( "Rescue a downed pilot from the following position:") @@ -379,6 +330,8 @@ do -- TASK_CARGO_CSAR return self end + + function TASK_CARGO_CSAR:ReportOrder( ReportGroup ) diff --git a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua index 9f81a5d79..6833f197c 100644 --- a/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua +++ b/Moose Development/Moose/Tasking/Task_Cargo_Dispatcher.lua @@ -70,6 +70,7 @@ do -- TASK_CARGO_DISPATCHER -- @type TASK_CARGO_DISPATCHER -- @extends Tasking.Task_Manager#TASK_MANAGER -- @field TASK_CARGO_DISPATCHER.CSAR CSAR + -- @field Core.Set#SET_ZONE SetZonesCSAR --- @type TASK_CARGO_DISPATCHER.CSAR -- @field Wrapper.Unit#UNIT PilotUnit @@ -260,6 +261,18 @@ do -- TASK_CARGO_DISPATCHER -- -- Use the @{#TASK_CARGO_DISPATCHER.SetCSARDeployZone}() to setup one deployment zone, and @{#TASK_CARGO_DISPATCHER.SetCSARDeployZones}() to setup multiple default deployment zones in one call. -- + -- ## 4.4. **CSAR ejection zones**. + -- + -- Setup a set of zones where the pilots will only eject and a task is created for CSAR. When such a set of zones is given, any ejection outside those zones will not result in a pilot created for CSAR! + -- + -- Use the @{#TASK_CARGO_DISPATCHER.SetCSARZones}() to setup the set of zones. + -- + -- ## 4.5. **CSAR ejection maximum**. + -- + -- Setup how many pilots will eject the maximum. This to avoid an overload of CSAR tasks being created :-) The default is endless CSAR tasks. + -- + -- Use the @{#TASK_CARGO_DISPATCHER.SetMaxCSAR}() to setup the maximum of pilots that will eject for CSAR. + -- -- -- # 5) Handle cargo task events. -- @@ -395,6 +408,9 @@ do -- TASK_CARGO_DISPATCHER self:SetCSARRadius() self:__StartTasks( 5 ) + self.MaxCSAR = nil + self.CountCSAR = 0 + -- For CSAR missions, we process the event when a pilot ejects. self:HandleEvent( EVENTS.Ejection ) @@ -403,6 +419,47 @@ do -- TASK_CARGO_DISPATCHER end + --- Sets the set of zones were pilots will only be spawned (eject) when the planes crash. + -- Note that because this is a set of zones, the MD can create the zones dynamically within his mission! + -- Just provide a set of zones, see usage, but find the tactical situation here: + -- + -- ![CSAR Zones](../Tasking/CSAR_Zones.JPG) + -- + -- @param #TASK_CARGO_DISPATCHER self + -- @param Core.Set#SET_ZONE SetZonesCSAR The set of zones where pilots will only be spawned for CSAR when they eject. + -- @usage + -- + -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, AttackGroups ) + -- + -- -- Use this call to pass the set of zones. + -- -- Note that you can create the set of zones inline, because the FilterOnce method (and other SET_ZONE methods return self). + -- -- So here the zones can be created as normal trigger zones (MOOSE creates a collection of ZONE objects when teh mission starts of all trigger zones). + -- -- Just name them as CSAR zones here. + -- TaskDispatcher:SetCSARZones( SET_ZONE:New():FilterPrefixes("CSAR"):FilterOnce() ) + -- + function TASK_CARGO_DISPATCHER:SetCSARZones( SetZonesCSAR ) + + self.SetZonesCSAR = SetZonesCSAR + + end + + + --- Sets the maximum of pilots that will be spawned (eject) when the planes crash. + -- @param #TASK_CARGO_DISPATCHER self + -- @param #number MaxCSAR The maximum of pilots that will eject for CSAR. + -- @usage + -- + -- TaskDispatcher = TASK_CARGO_DISPATCHER:New( Mission, AttackGroups ) + -- + -- -- Use this call to the maximum of CSAR to 10. + -- TaskDispatcher:SetMaxCSAR( 10 ) + -- + function TASK_CARGO_DISPATCHER:SetMaxCSAR( MaxCSAR ) + + self.MaxCSAR = MaxCSAR + + end + --- Handle the event when a pilot ejects. @@ -420,8 +477,15 @@ do -- TASK_CARGO_DISPATCHER -- Only add a CSAR task if the coalition of the mission is equal to the coalition of the ejected unit. if CSARCoalition == self.Mission:GetCommandCenter():GetCoalition() then - local CSARTaskName = self:AddCSARTask( self.CSARTaskName, CSARCoordinate, CSARHeading, CSARCountry, self.CSARBriefing ) - self:SetCSARDeployZones( CSARTaskName, self.CSARDeployZones ) + -- And only add if the eject is in one of the zones, if defined. + if not self.SetZonesCSAR or ( self.SetZonesCSAR and self.SetZonesCSAR:IsCoordinateInZone( CSARCoordinate ) ) then + -- And only if the maximum of pilots is not reached that ejected! + if not self.MaxCSAR or ( self.MaxCSAR and self.CountCSAR < self.MaxCSAR ) then + local CSARTaskName = self:AddCSARTask( self.CSARTaskName, CSARCoordinate, CSARHeading, CSARCountry, self.CSARBriefing ) + self:SetCSARDeployZones( CSARTaskName, self.CSARDeployZones ) + self.CountCSAR = self.CountCSAR + 1 + end + end end end diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index d12f613d0..536d7f8e8 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1217,6 +1217,25 @@ end function GROUP:GetMinHeight() self:F2() + local DCSGroup = self:GetDCSObject() + + if DCSGroup then + local GroupHeightMin = 999999999 + + for Index, UnitData in pairs( DCSGroup:getUnits() ) do + local UnitData = UnitData -- DCS#Unit + + local UnitHeight = UnitData:getPoint() + + if UnitHeight < GroupHeightMin then + GroupHeightMin = UnitHeight + end + end + + return GroupHeightMin + end + + return nil end --- Returns the current maximum height of the group. @@ -1226,6 +1245,25 @@ end function GROUP:GetMaxHeight() self:F2() + local DCSGroup = self:GetDCSObject() + + if DCSGroup then + local GroupHeightMax = -999999999 + + for Index, UnitData in pairs( DCSGroup:getUnits() ) do + local UnitData = UnitData -- DCS#Unit + + local UnitHeight = UnitData:getPoint() + + if UnitHeight > GroupHeightMax then + GroupHeightMax = UnitHeight + end + end + + return GroupHeightMax + end + + return nil end -- RESPAWNING From 8f6a3d5c49d6e88d0b32f646970709db51a06c3d Mon Sep 17 00:00:00 2001 From: FlightControl Date: Mon, 1 Oct 2018 20:29:33 +0200 Subject: [PATCH 419/420] I have the refuel again working for the AI_A2A_DISPATCHER. And the status checks are again functional for CAP and GCI! --- Moose Development/Moose/AI/AI_A2A.lua | 5 +---- Moose Development/Moose/AI/AI_A2A_Cap.lua | 3 ++- Moose Development/Moose/AI/AI_A2A_Dispatcher.lua | 4 ++-- Moose Development/Moose/AI/AI_A2A_Gci.lua | 1 + Moose Development/Moose/Wrapper/Controllable.lua | 1 - 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/Moose Development/Moose/AI/AI_A2A.lua b/Moose Development/Moose/AI/AI_A2A.lua index dc02a56a1..d9b61970f 100644 --- a/Moose Development/Moose/AI/AI_A2A.lua +++ b/Moose Development/Moose/AI/AI_A2A.lua @@ -400,7 +400,6 @@ end -- @param #string Event The Event string. -- @param #string To The To State string. function AI_A2A:onafterStart( Controllable, From, Event, To ) - self:F2() self:__Status( 10 ) -- Check status status every 30 seconds. @@ -423,8 +422,6 @@ end --- @param #AI_A2A self function AI_A2A:onafterStatus() - self:F( " Checking Status" ) - if self.Controllable and self.Controllable:IsAlive() then local RTB = false @@ -452,7 +449,7 @@ function AI_A2A:onafterStatus() if not self:Is( "Fuel" ) and not self:Is( "Home" ) then local Fuel = self.Controllable:GetFuelMin() - self:F({Fuel=Fuel}) + self:F({Fuel=Fuel, PatrolFuelThresholdPercentage=self.PatrolFuelThresholdPercentage}) if Fuel < self.PatrolFuelThresholdPercentage then if self.TankerName then self:E( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... Refuelling at Tanker!" ) diff --git a/Moose Development/Moose/AI/AI_A2A_Cap.lua b/Moose Development/Moose/AI/AI_A2A_Cap.lua index 480bd39ec..41c8c8a5e 100644 --- a/Moose Development/Moose/AI/AI_A2A_Cap.lua +++ b/Moose Development/Moose/AI/AI_A2A_Cap.lua @@ -282,13 +282,14 @@ function AI_A2A_CAP:New( AICap, PatrolZone, PatrolFloorAltitude, PatrolCeilingAl end --- onafter State Transition for Event Patrol. --- @param #AI_A2A_GCI self +-- @param #AI_A2A_CAP self -- @param Wrapper.Group#GROUP AICap The AI Group managed by the FSM. -- @param #string From The From State string. -- @param #string Event The Event string. -- @param #string To The To State string. function AI_A2A_CAP:onafterStart( AICap, From, Event, To ) + self:GetParent( self ).onafterStart( self, AICap, From, Event, To ) AICap:HandleEvent( EVENTS.Takeoff, nil, self ) end diff --git a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua index 135a9a0fc..20af4f2fe 100644 --- a/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua +++ b/Moose Development/Moose/AI/AI_A2A_Dispatcher.lua @@ -2365,7 +2365,7 @@ do -- AI_A2A_DISPATCHER -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) -- -- -- Now Setup the default fuel treshold. - -- A2ADispatcher:SetDefaultRefuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. + -- A2ADispatcher:SetDefaultFuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. -- function AI_A2A_DISPATCHER:SetDefaultFuelThreshold( FuelThreshold ) @@ -2407,7 +2407,7 @@ do -- AI_A2A_DISPATCHER -- A2ADispatcher = AI_A2A_DISPATCHER:New( Detection ) -- -- -- Now Setup the default fuel treshold. - -- A2ADispatcher:SetDefaultRefuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. + -- A2ADispatcher:SetDefaultFuelThreshold( 0.30 ) -- Go RTB when only 30% of fuel remaining in the tank. -- -- -- Now Setup the default tanker. -- A2ADispatcher:SetDefaultTanker( "Tanker" ) -- The group name of the tanker is "Tanker" in the Mission Editor. diff --git a/Moose Development/Moose/AI/AI_A2A_Gci.lua b/Moose Development/Moose/AI/AI_A2A_Gci.lua index cc24341c3..53b7141ab 100644 --- a/Moose Development/Moose/AI/AI_A2A_Gci.lua +++ b/Moose Development/Moose/AI/AI_A2A_Gci.lua @@ -290,6 +290,7 @@ end -- @param #string To The To State string. function AI_A2A_GCI:onafterStart( AIIntercept, From, Event, To ) + self:GetParent( self ).onafterStart( self, AIIntercept, From, Event, To ) AIIntercept:HandleEvent( EVENTS.Takeoff, nil, self ) end diff --git a/Moose Development/Moose/Wrapper/Controllable.lua b/Moose Development/Moose/Wrapper/Controllable.lua index 68f035bed..f86c5aff2 100644 --- a/Moose Development/Moose/Wrapper/Controllable.lua +++ b/Moose Development/Moose/Wrapper/Controllable.lua @@ -1654,7 +1654,6 @@ end -- RouteToZone( GroundGroup, ZoneList[1] ) -- function CONTROLLABLE:TaskFunction( FunctionString, ... ) - self:E({TaskFunction=FunctionString, arguments=arg}) local DCSTask From ec9a5efdde705711d71f443224bd2c699042c04a Mon Sep 17 00:00:00 2001 From: FlightControl Date: Mon, 1 Oct 2018 22:06:42 +0200 Subject: [PATCH 420/420] -- Added AnyInZone stuff. -- Now for tasking, routing messages show the task name... --- Moose Development/Moose/Actions/Act_Route.lua | 8 +++---- Moose Development/Moose/Core/Set.lua | 23 +++++++++++++++++++ Moose Development/Moose/Wrapper/Group.lua | 17 ++++++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/Moose Development/Moose/Actions/Act_Route.lua b/Moose Development/Moose/Actions/Act_Route.lua index 9f858879f..d719f1f7b 100644 --- a/Moose Development/Moose/Actions/Act_Route.lua +++ b/Moose Development/Moose/Actions/Act_Route.lua @@ -362,7 +362,7 @@ do -- ACT_ROUTE_POINT local Distance = self.Coordinate:Get2DDistance( ProcessUnit:GetCoordinate() ) if Distance <= self.Range then - local RouteText = "You have arrived." + local RouteText = "Task \"" .. self:GetTask():GetName() .. "\", you have arrived." self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Information ) return true end @@ -381,7 +381,7 @@ do -- ACT_ROUTE_POINT -- @param #string To function ACT_ROUTE_POINT:onafterReport( ProcessUnit, From, Event, To ) - local RouteText = self:GetRouteText( ProcessUnit ) + local RouteText = "Task \"" .. self:GetTask():GetName() .. "\", " .. self:GetRouteText( ProcessUnit ) self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Update ) end @@ -453,7 +453,7 @@ do -- ACT_ROUTE_ZONE function ACT_ROUTE_ZONE:onfuncHasArrived( ProcessUnit ) if ProcessUnit:IsInZone( self.Zone ) then - local RouteText = "You have arrived within the zone." + local RouteText = "Task \"" .. self:GetTask():GetName() .. "\", you have arrived within the zone." self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Information ) end @@ -471,7 +471,7 @@ do -- ACT_ROUTE_ZONE function ACT_ROUTE_ZONE:onafterReport( ProcessUnit, From, Event, To ) self:F( { ProcessUnit = ProcessUnit } ) - local RouteText = self:GetRouteText( ProcessUnit ) + local RouteText = "Task \"" .. self:GetTask():GetName() .. "\", " .. self:GetRouteText( ProcessUnit ) self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Update ) end diff --git a/Moose Development/Moose/Core/Set.lua b/Moose Development/Moose/Core/Set.lua index d1a1cc8db..15621ec6f 100644 --- a/Moose Development/Moose/Core/Set.lua +++ b/Moose Development/Moose/Core/Set.lua @@ -1229,6 +1229,29 @@ do -- SET_GROUP end return true end + + --- Iterate the SET_GROUP and call an iterator function for each alive GROUP that has any unit in the @{Core.Zone}, providing the GROUP and optional parameters to the called function. + -- @param #SET_GROUP 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 GROUP in the SET_GROUP. The function needs to accept a GROUP parameter. + -- @return #SET_GROUP self + function SET_GROUP:ForEachGroupAnyInZone( ZoneObject, IteratorFunction, ... ) + self:F2( arg ) + + self:ForEach( IteratorFunction, arg, self:GetSet(), + --- @param Core.Zone#ZONE_BASE ZoneObject + -- @param Wrapper.Group#GROUP GroupObject + function( ZoneObject, GroupObject ) + if GroupObject:IsAnyInZone( ZoneObject ) then + return true + else + return false + end + end, { ZoneObject } ) + + return self + end + --- Iterate the SET_GROUP and return true if at least one of the @{Wrapper.Group#GROUP} is completely inside the @{Core.Zone#ZONE} -- @param #SET_GROUP self diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 536d7f8e8..e00e79c4d 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -1008,6 +1008,23 @@ function GROUP:IsNotInZone( Zone ) return true end +--- Returns true if any units of the group are within a @{Core.Zone}. +-- @param #GROUP self +-- @param Core.Zone#ZONE_BASE Zone The zone to test. +-- @return #boolean Returns true if any unit of the Group is within the @{Core.Zone#ZONE_BASE} +function GROUP:IsAnyInZone( Zone ) + + if not self:IsAlive() then return false end + + for UnitID, UnitData in pairs( self:GetUnits() ) do + local Unit = UnitData -- Wrapper.Unit#UNIT + if Zone:IsVec3InZone( Unit:GetVec3() ) then + return true + end + end + return false +end + --- Returns the number of UNITs that are in the @{Zone} -- @param #GROUP self -- @param Core.Zone#ZONE_BASE Zone The zone to test.